Description
I want to use LVGL for a project involving the New Haven Display NHD-2.8-240320AF-CSXP that is driven by an ST7789 with a resolution of 320x240 in landscape orientation. I can see the UI I created but only if I keep the size below 240 in any direction. As soon as I choose anything higher the image gets scrambled.
What MCU/Processor/Board and compiler are you using?
As MCU I’m using an STM32U59A on a custom design board, the display is connected to FMC to interface it in parallel mode where A24 is connected to RS. The software makes use of FreeRTOS and is built within STM32CubeIDE. The UI is created with EEZ Studio
What do you want to achieve?
The natural resolution of the display is 240x320 but I want to use it as an 320x240 UI. How do I have to adjust the EEZ Studio settings and the LVGL init routine to get this working?
What have you tried so far?
I provided functions to make use of the LVGL built-in driver for the ST7789 and already adjusted the mipi init command list according the init values provided by New Haven’s documentation. For testing purposes I created a 300x300 UI with four colored panels in the upper left corner and I can set the rotation via an UART console.
Code to reproduce
// within my lcd.c
#define FMC_BANK1_REG ((__IO uint16_t *) 0x60000000) // Register Address for A0
#define FMC_BANK1_DATA ((__IO uint16_t *) 0x62000000) // Data Address for A24 -> A24<<1
#define LCD_H_RES 240
#define LCD_V_RES 320
volatile int32_t lcd_bus_busy = 0;
static int32_t lcd_io_init(void)
{
// reset LCD
HAL_GPIO_WritePin(nRES_DISPL_GPIO_Port, nRES_DISPL_Pin, GPIO_PIN_RESET);
osDelay(pdMS_TO_TICKS(5));
HAL_GPIO_WritePin(nRES_DISPL_GPIO_Port, nRES_DISPL_Pin, GPIO_PIN_SET);
osDelay(pdMS_TO_TICKS(10));
// Enable background lighting
HAL_TIM_PWM_Start(&htim15, TIM_CHANNEL_1);
__HAL_TIM_SET_COMPARE(&htim15, TIM_CHANNEL_1, htim15.Instance->ARR * 50 / 100);
return HAL_OK;
}
static void lcd_send_cmd(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, const uint8_t *param, size_t param_size)
{
while (lcd_bus_busy); /* wait until previous transfer is finished */
lcd_bus_busy = 1;
*FMC_BANK1_REG = *cmd;
for (uint32_t i = 0; i < param_size; i += 1) {
*FMC_BANK1_DATA = *(uint8_t *)(¶m[i]);
}
lcd_bus_busy = 0;
lv_display_flush_ready(disp);
}
static void lcd_send_color(lv_display_t *disp, const uint8_t *cmd, size_t cmd_size, uint8_t *param, size_t param_size)
{
while (lcd_bus_busy); /* wait until previous transfer is finished */
lcd_bus_busy = 1;
*FMC_BANK1_REG = *cmd;
for (uint32_t i = 0; i < param_size; i += 2) {
*FMC_BANK1_DATA = *(uint16_t *)(¶m[i]);
}
lcd_bus_busy = 0;
lv_display_flush_ready(disp);
}
void lcd_task(void *params)
{
UNUSED(params);
lv_display_t *disp;
lv_init();
lcd_io_init();
disp = lv_st7789_create(LCD_H_RES, LCD_V_RES, LV_LCD_FLAG_NONE, lcd_send_cmd, lcd_send_color);
//lv_display_set_rotation(disp, LV_DISPLAY_ROTATION_270);
lv_color_t * buf1 = NULL;
lv_color_t * buf2 = NULL;
uint32_t buf_size = LCD_H_RES * LCD_V_RES * lv_color_format_get_size(lv_display_get_color_format(disp)) + 40000;
buf1 = lv_malloc(buf_size);
if(buf1 == NULL) {
console_printf("[LCD] display draw buffer malloc failed");
osThreadExit();
}
buf2 = lv_malloc(buf_size);
if(buf2 == NULL) {
console_printf("[LCD] display double buffer malloc failed");
lv_free(buf1);
osThreadExit();
}
lv_display_set_buffers(disp, buf1, buf2, buf_size, LV_DISPLAY_RENDER_MODE_FULL);
ui_init(disp);
for(;;) {
// The task running lv_timer_handler should have lower priority than that running `lv_tick_inc`
lv_timer_handler();
// raise the task priority of LVGL and/or reduce the handler period can improve the performance
osDelay(10);
}
}
// adjustments to lv_st7789.c from LVGL
static const uint8_t init_cmd_list[] = {
LV_LCD_CMD_ENTER_INVERT_MODE, 0,
CMD_PORCTRL, 5, 0x0c, 0x0c, 0x00, 0x33, 0x33,
CMD_GCTRL, 1, 0x35, /* GCTRL -- panel dependent */
CMD_VCOMS, 1, 0x2b, /* VCOMS -- panel dependent */
CMD_LCMCTRL, 1, 0x2c,
CMD_VDVVRHEN, 2, 0x01, 0xff,
CMD_VRHS, 1, 0x11, /* VRHS - panel dependent */
CMD_VDVSET, 1, 0x20,
CMD_FRCTR2, 1, 0x0f,
CMD_PWCTRL1, 2, 0xa4, 0xa1,
/*CMD_RAMCTRL, 2, 0x00, 0xC0,*/ /* controls mapping of RGB565 to RGB666 */
CMD_PVGAMCTRL, 14, 0xd0, 0x00, 0x05, 0x0e, 0x15, 0x0d, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19,
CMD_NVGAMCTRL, 14, 0xd0, 0x00, 0x05, 0x0d, 0x0c, 0x06, 0x2d, 0x44, 0x40, 0x0e, 0x1c, 0x18, 0x16, 0x19,
/*LV_LCD_CMD_SET_GAMMA_CURVE, 1, 0x01,*/
//LV_LCD_CMD_SET_COLUMN_ADDRESS, 4, 0x00, 0x00, 0x00, 0xef,
//LV_LCD_CMD_SET_PAGE_ADDRESS, 4, 0x00, 0x00, 0x01, 0x3f,
LV_LCD_CMD_DELAY_MS, LV_LCD_CMD_EOF
};
Screenshot and/or video
To see the screen drawing better I increased the transmit time, usually a full frame is transmitted within 20ms and the screen redraw time is set to 33ms. It’s slow on purpose right now. The video shows power on of the device and then the rotation changes to 90, 180, 270 and back to 0
Setting screen rotations
Update: with further debugging I could eliminate a bug in my implementation of lcd_send_cmd most of the commands accept only 1 Byte for each parameter but for the parallel interface I always have to send 2 Bytes for each parameter. Now I seem to get the colors as expected but for 90 and 270 rotation there still is some offset that LVGL adds to the buffer data.