Hi
i’m using sharp mip (LS027B7DH01) an uc1610 (ea dogxl160) based displays for my project and have managed them to work with littlevGL (V5.3) but have some questions.
- sharp mip (LS027B7DH01 400x240 monochrome) :
/**
* @file SHARP_MIP.c
*
*/
/*-------------------------------------------------------------------------------------------------
* SHARP memory in pixel monochrome display series
* LS012B7DD01 (184x38 pixels.)
* LS013B7DH03 (128x128 pixels.)
* LS013B7DH05 (144x168 pixels.)
* LS027B7DH01 (400x240 pixels.) (tested)
* LS032B7DD02 (336x536 pixels.)
* LS044Q7DH01 (320x240 pixels.)
*
* These displays need periodic com inversion, there are two ways :
* - software com inversion when writing a frame on screen
* memory : define SHARP_MIP_SOFT_COM_INVERSION 1 and
* set EXTMODE display pin LOW
* - hardware com inversion with EXTCOMIN display pin :
* define SHARP_MIP_SOFT_COM_INVERSION 0,
* set EXTMODE display pin HIGH and handle
* EXTCOMIN waveform (for example with mcu pwm output),
* see datasheet pages 8-12 for details
*
* VDB size : (LV_VER_RES / X) * (2 + LV_HOR_RES / 8) + 2 bytes, structure :
* [FRAME_HEADER (1 byte)] [GATE_ADDR (1 byte )] [LINE_DATA (LV_HOR_RES / 8 bytes)] 1st line
* [DUMMY (1 byte)] [GATE_ADDR (1 byte )] [LINE_DATA (LV_HOR_RES / 8 bytes)] 2nd line
* ...........................................................................................
* [DUMMY (1 byte)] [GATE_ADDR (1 byte )] [LINE_DATA (LV_HOR_RES / 8 bytes)] last line
* [DUMMY (2 bytes)]
*
* Since extra bytes (dummy, addresses, header) are stored in VDB, we need to use
* an "external" VDB. Configuration in lv_conf becomes :
* #define LV_VDB_SIZE ((LV_VER_RES / X) * LV_HOR_RES)
* #define LV_VDB_PX_BPP 1
* #define LV_VDB_ADR LV_VDB_ADR_INV
* and before lv_init() we must call lv_vdb_set_adr(our_vdb_address, NULL)
*-----------------------------------------------------------------------------------------------*/
/*********************
* INCLUDES
*********************/
#include "SHARP_MIP.h"
#if USE_SHARP_MIP
#include <stdbool.h>
#include "../lvgl/lv_core/lv_vdb.h"
#include LV_DRV_DISP_INCLUDE
#include LV_DRV_DELAY_INCLUDE
/*********************
* DEFINES
*********************/
#define SHARP_MIP_HEADER 0
#define SHARP_MIP_UPDATE_RAM_FLAG (1U << 7U) /* (M0) Mode flag : H -> update memory, L -> maintain memory */
#define SHARP_MIP_COM_INVERSION_FLAG (1U << 6U) /* (M1) Frame inversion flag : relevant when EXTMODE = L, */
/* H -> outputs VCOM = H, L -> outputs VCOM = L */
#define SHARP_MIP_CLEAR_SCREEN_FLAG (1U << 5U) /* (M2) All clear flag : H -> clear all pixels */
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
#if SHARP_MIP_SOFT_COM_INVERSION
static bool_t com_output_state = false;
#endif
/**********************
* MACROS
**********************/
/*
* Return the VDB byte index corresponding to the pixel
* relatives coordinates (x, y) in the area.
* The area is rounded to a whole screen line.
*/
#define BUFIDX(x, y) (((x) >> 3U) + ((y) * (2U + (SHARP_MIP_HOR_RES >> 3U))) + 2U)
/*
* Return the byte bitmask of a pixel bit corresponding
* to VDB arrangement (8 pixels per byte on lines).
*/
#define PIXIDX(x) SHARP_MIP_REV_BYTE(1U << ((x) & 7U))
/**********************
* GLOBAL FUNCTIONS
**********************/
void sharp_mip_init(void) {
sharp_mip_init_board(); /*Function defined in our LV_DRV_DISP_INCLUDE*/
sharp_mip_display_enable(); /*Function defined in our LV_DRV_DISP_INCLUDE*/
/* These displays have nothing to initialize */
}
void sharp_mip_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p) {
(void) x1;
(void) x2;
/*Return if the area is out the screen*/
if(y2 < 0) return;
if(y1 > SHARP_MIP_VER_RES - 1) return;
/*Truncate the area to the screen*/
int32_t act_y1 = y1 < 0 ? 0 : y1;
int32_t act_y2 = y2 > SHARP_MIP_VER_RES - 1 ? SHARP_MIP_VER_RES - 1 : y2;
/*Buffer address & size*/
uint8_t * buf = (uint8_t *) color_p; /*Get the buffer address*/
uint16_t buf_h = (act_y2 - act_y1 + 1); /*Number of buffer lines*/
uint16_t buf_size = buf_h * (2 + SHARP_MIP_HOR_RES / 8) + 2; /*Buffer size in bytes */
/* Set lines to flush dummy byte & gate address in VDB*/
for(uint16_t act_y = 0 ; act_y < buf_h ; act_y++) {
buf[BUFIDX(0, act_y) - 1] = SHARP_MIP_REV_BYTE((act_y1 + act_y + 1));
buf[BUFIDX(0, act_y) - 2] = 0;
}
/* Set last dummy two bytes in VDB */
buf[BUFIDX(0, buf_h) - 1] = 0;
buf[BUFIDX(0, buf_h) - 2] = 0;
/* Set frame header in VDB */
buf[0] = SHARP_MIP_HEADER |
SHARP_MIP_UPDATE_RAM_FLAG;
#if SHARP_MIP_SOFT_COM_INVERSION
if (com_output_state) {
com_output_state = false;
} else {
buf[0] |= SHARP_MIP_COM_INVERSION_FLAG;
com_output_state = true;
}
#endif
/* Write the frame on display memory */
LV_DRV_DISP_SPI_CS(1);
LV_DRV_DISP_SPI_WR_ARRAY(buf, buf_size);
LV_DRV_DISP_SPI_CS(0);
lv_flush_ready();
}
void sharp_mip_vdb_wr(uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa) {
(void) buf_w;
(void) opa;
if (lv_color_to1(color) != 0) {
buf[BUFIDX(x, y)] |= PIXIDX(x); /*Set VDB pixel bit to 1 for other colors than BLACK*/
} else {
buf[BUFIDX(x, y)] &= ~PIXIDX(x); /*Set VDB pixel bit to 0 for BLACK color*/
}
}
void sharp_mip_round_cb(lv_area_t *area) {
/* Round area to a whole line */
area->x1 = 0;
area->x2 = SHARP_MIP_HOR_RES - 1;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif
the buffer stores extra bytes to transmit to display controller, is there a way to do that without using an “external” buffer ?
- UC1610 (ea dogxl160 160x104 4 gray) :
/**
* @file UC1610.c
*
*/
/*********************
* INCLUDES
*********************/
#include "UC1610.h"
#if USE_UC1610
#include <stdbool.h>
#include "../lvgl/lv_core/lv_vdb.h"
#include LV_DRV_DISP_INCLUDE
#include LV_DRV_DELAY_INCLUDE
/*********************
* DEFINES
*********************/
#define UC1610_CMD_MODE 0U
#define UC1610_DATA_MODE 1U
#define UC1610_RESET_MODE 0U
#define UC1610_SET_MODE 1U
/* hardware control commands */
#define UC1610_SYSTEM_RESET 0xE2 /* software reset */
#define UC1610_NOP 0xE3
#define UC1610_SET_TEMP_COMP 0x24 /* set temperature compensation, default -0.05%/°C */
#define UC1610_SET_PANEL_LOADING 0x29 /* set panel loading, default 16~21 nF */
#define UC1610_SET_PUMP_CONTROL 0x2F /* default internal Vlcd (8x pump) */
#define UC1610_SET_LCD_BIAS_RATIO 0xEB /* default 11 */
#define UC1610_SET_VBIAS_POT 0x81 /* 1 byte (0~255) to follow setting the contrast, default 0x81 */
#define UC1610_SET_LINE_RATE 0xA0 /* default 12,1 Klps */
#define UC1610_SET_DISPLAY_ENABLE 0xAE /* + 1 / 0 : exit sleep mode / entering sleep mode */
#define UC1610_SET_LCD_GRAY_SHADE 0xD0 /* default 24% between the two gray shade levels */
#define UC1610_SET_COM_END 0xF1 /* set the number of used com electrodes (lines number -1) */
/* ram address control */
#define UC1610_SET_AC 0x88 /* set ram address control */
#define UC1610_AC_WA_FLAG (1U << 0U) /* automatic column/page increment wrap around (1 : cycle increment) */
#define UC1610_AC_AIO_FLAG (1U << 1U) /* auto increment order (0/1 : column/page increment first) */
#define UC1610_AC_PID_FLAG (1U << 2U) /* page address auto increment order (0/1 : +1/-1) */
/* set cursor ram address */
#define UC1610_SET_CA_LSB 0x00 /* + 4 LSB bits */
#define UC1610_SET_CA_MSB 0x10 /* + 4 MSB bits // MSB + LSB values range : 0~159 */
#define UC1610_SET_PA 0x60 /* + 5 bits // values range : 0~26 */
/* display control commands */
#define UC1610_SET_FIXED_LINES 0x90 /* + 4 bits = 2xFL */
#define UC1610_SET_SCROLL_LINES_LSB 0x40 /* + 4 LSB bits scroll up display by N (7 bits) lines */
#define UC1610_SET_SCROLL_LINES_MSB 0x50 /* + 3 MSB bits */
#define UC1610_SET_ALL_PIXEL_ON 0xA4 /* + 1 / 0 : set all pixel on, reverse */
#define UC1610_SET_INVERSE_DISPLAY 0xA6 /* + 1 / 0 : inverse all data stored in ram, reverse */
#define UC1610_SET_MAPPING_CONTROL 0xC0 /* control mirorring */
#define UC1610_SET_MAPPING_CONTROL_LC_FLAG (1U << 0U)
#define UC1610_SET_MAPPING_CONTROL_MX_FLAG (1U << 1U)
#define UC1610_SET_MAPPING_CONTROL_MY_FLAG (1U << 2U)
/* window program mode */
#define UC1610_SET_WINDOW_PROGRAM_ENABLE 0xF8 /* + 1 / 0 : enable / disable window programming mode, */
/* reset before changing boundaries */
#define UC1610_SET_WP_STARTING_CA 0xF4 /* 1 byte to follow for column address */
#define UC1610_SET_WP_ENDING_CA 0xF6 /* 1 byte to follow for column address */
#define UC1610_SET_WP_STARTING_PA 0xF5 /* 1 byte to follow for page address */
#define UC1610_SET_WP_ENDING_PA 0xF7 /* 1 byte to follow for page address */
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/**********************
* STATIC VARIABLES
**********************/
static uint8_t cmd_buf[12];
/**********************
* MACROS
**********************/
/* Return the byte bitmask of a pixel color corresponding to VDB arrangement */
#define PIXIDX(y, c) ((c) << (((y) & 3U) << 1U))
/**********************
* GLOBAL FUNCTIONS
**********************/
void uc1610_init(void) {
uc1610_init_board(); /* provided by "#include LV_DRV_DISP_INCLUDE" */
LV_DRV_DELAY_MS(12);
/* initialization sequence */
#if UC1610_INIT_HARD_RST
LV_DRV_DISP_RST(UC1610_RESET_MODE); /* hardware reset */
LV_DRV_DELAY_MS(1);
LV_DRV_DISP_RST(UC1610_SET_MODE);
#else
cmd_buf[0] = UC1610_SYSTEM_RESET; /* software reset */
LV_DRV_DISP_CMD_DATA(UC1610_CMD_MODE);
LV_DRV_DISP_SPI_CS(0);
LV_DRV_DISP_SPI_WR_ARRAY(cmd_buf, 1);
LV_DRV_DISP_SPI_CS(1);
#endif
LV_DRV_DELAY_MS(2);
cmd_buf[0] = UC1610_SET_COM_END; /* set com end value */
cmd_buf[1] = UC1610_VER_RES - 1;
cmd_buf[2] = UC1610_SET_PANEL_LOADING;
cmd_buf[3] = UC1610_SET_LCD_BIAS_RATIO;
cmd_buf[4] = UC1610_SET_VBIAS_POT; /* set contrast */
cmd_buf[5] = (UC1610_INIT_CONTRAST * 255) / 100;
#if UC1610_TOP_VIEW
cmd_buf[6] = UC1610_SET_MAPPING_CONTROL | /* top view */
UC1610_SET_MAPPING_CONTROL_MY_FLAG |
UC1610_SET_MAPPING_CONTROL_MX_FLAG;
#else
cmd_buf[6] = UC1610_SET_MAPPING_CONTROL; /* bottom view */
#endif
cmd_buf[7] = UC1610_SET_SCROLL_LINES_LSB | 0; /* set scroll line on line 0 */
cmd_buf[8] = UC1610_SET_SCROLL_LINES_MSB | 0;
cmd_buf[9] = UC1610_SET_AC | UC1610_AC_WA_FLAG; /* set auto increment wrap around */
cmd_buf[10] = UC1610_SET_INVERSE_DISPLAY | 1; /* invert colors to complies lv color system */
cmd_buf[11] = UC1610_SET_DISPLAY_ENABLE | 1; /* turn display on */
LV_DRV_DISP_CMD_DATA(UC1610_CMD_MODE);
LV_DRV_DISP_SPI_CS(0);
LV_DRV_DISP_SPI_WR_ARRAY(cmd_buf, 12);
LV_DRV_DISP_SPI_CS(1);
}
void uc1610_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p) {
/*Return if the area is out the screen*/
if(x2 < 0) return;
if(y2 < 0) return;
if(x1 > UC1610_HOR_RES - 1) return;
if(y1 > UC1610_VER_RES - 1) return;
/*Truncate the area to the screen*/
int32_t act_x1 = x1 < 0 ? 0 : x1;
int32_t act_y1 = y1 < 0 ? 0 : y1;
int32_t act_x2 = x2 > UC1610_HOR_RES - 1 ? UC1610_HOR_RES - 1 : x2;
int32_t act_y2 = y2 > UC1610_VER_RES - 1 ? UC1610_VER_RES - 1 : y2;
/*Buffer address & size*/
uint8_t * buf = (uint8_t *) color_p;
uint16_t buf_size = (act_x2 - act_x1 + 1) * (((act_y2 - act_y1) >> 2) + 1);
/*Set display window to fill*/
cmd_buf[0] = UC1610_SET_WINDOW_PROGRAM_ENABLE | 0; /* before changing boundaries */
cmd_buf[1] = UC1610_SET_WP_STARTING_CA;
cmd_buf[2] = act_x1;
cmd_buf[3] = UC1610_SET_WP_ENDING_CA;
cmd_buf[4] = act_x2;
cmd_buf[5] = UC1610_SET_WP_STARTING_PA;
cmd_buf[6] = act_y1 >> 2U;
cmd_buf[7] = UC1610_SET_WP_ENDING_PA;
cmd_buf[8] = act_y2 >> 2U;
cmd_buf[9] = UC1610_SET_WINDOW_PROGRAM_ENABLE | 1; /* entering window programming */
LV_DRV_DISP_CMD_DATA(UC1610_CMD_MODE);
LV_DRV_DISP_SPI_CS(0);
LV_DRV_DISP_SPI_WR_ARRAY(cmd_buf, 10);
LV_DRV_DISP_SPI_CS(1);
/*Flush VDB on display memory*/
LV_DRV_DISP_CMD_DATA(UC1610_DATA_MODE);
LV_DRV_DISP_SPI_CS(0);
LV_DRV_DISP_SPI_WR_ARRAY(buf, buf_size);
LV_DRV_DISP_SPI_CS(1);
lv_flush_ready();
}
void uc1610_vdb_wr(uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa) {
(void) opa;
uint16_t idx = x + buf_w * (y >> 2U);
buf[idx] &= ~PIXIDX(y, 0x03); /* reset pixel color */
buf[idx] |= PIXIDX(y, lv_color_to2(color)); /* write new color */
}
void uc1610_round_cb(lv_area_t *area) {
/*Round y window to display memory page size*/
area->y1 = (area->y1 & (~0x03));
area->y2 = (area->y2 & (~0x03)) + 0x03;
}
/**********************
* STATIC FUNCTIONS
**********************/
#endif
it’s a 4 gray color display and i had to add 4 gray related in lv_color.h
#elif LV_COLOR_DEPTH == 2
#define LV_COLOR_SIZE 8
typedef union
{
uint8_t blue :2;
uint8_t green :2;
uint8_t red :2;
uint8_t full :2;
} lv_color2_t;
#elif LV_COLOR_DEPTH == 2
typedef uint8_t lv_color_int_t;
typedef lv_color2_t lv_color_t;
lv_color_to1
#elif LV_COLOR_DEPTH == 2
if(color.full & 0x2) return 1;
else return 0;
lv_color_to2
static inline uint8_t lv_color_to2(lv_color_t color)
{
#if LV_COLOR_DEPTH == 1
return color.full * 3;
#elif LV_COLOR_DEPTH == 2
return color.full;
#elif LV_COLOR_DEPTH == 8
return color.full >> 6;
#elif LV_COLOR_DEPTH == 16
return color.full >> 14;
#elif LV_COLOR_DEPTH == 32
return color.full >> 30;
#endif
}
lv_color_to8
#elif LV_COLOR_DEPTH == 2
return color.full * 0x55;
lv_color_to16
#elif LV_COLOR_DEPTH == 2
return color.full * 0x5555;
lv_color_to32
#elif LV_COLOR_DEPTH == 2
return color.full * 0x55555555;
and macro to create colors
#elif LV_COLOR_DEPTH == 2
#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){(b8 >> 6 | g8 >> 6 | r8 >> 6)})
#elif LV_COLOR_DEPTH == 2
#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){(r8 >> 6 | g8 >> 6 | b8 >> 6)})
I’m not familiar with colors formats so not sure lv_color_toX are right, do you think it’s ok ?
Do you see something to improve ?
I can contribute these drivers if you think they are right.
Thanks