Description
I am trying to port LVGL v8.0 to ESP32 recently. (v7.11 works perfectly in my project). But after fixing all complie errors and run with some simple widgets, I found that LVGL only renders part of the graphics in my display(sreenshot showed below).
My display resolution is 320x172, controller is ST7789. and I still use Kconfig to configure display resolution in v8.0.
What have you tried so far?
I have added some prints in LVGL drivers to debug.
In lvgl_esp32_drivers/lvgl_tft/st7789.c
I print the area coordinate:
/* The ST7789 display controller can drive 320*240 displays, when using a 240*240
* display there's a gap of 80px, we need to edit the coordinates to take into
* account that gap, this is not necessary in all orientations. */
void st7789_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_map)
{
uint8_t data[4] = {0};
uint16_t offsetx1 = area->x1;
uint16_t offsetx2 = area->x2;
uint16_t offsety1 = area->y1;
uint16_t offsety2 = area->y2;
ets_printf("flush offsetx1: %d, offsetx2: %d, offsety1: %d, offsety2: %d\n", area->x1, area->x2, area->y1, area->y2);
#if (CONFIG_LV_TFT_DISPLAY_OFFSETS)
offsetx1 += CONFIG_LV_TFT_DISPLAY_X_OFFSET;
offsetx2 += CONFIG_LV_TFT_DISPLAY_X_OFFSET;
offsety1 += CONFIG_LV_TFT_DISPLAY_Y_OFFSET;
offsety2 += CONFIG_LV_TFT_DISPLAY_Y_OFFSET;
and in lvgl/src/core/lv_refr.c
print the area coordinate also:
/**
* Invalidate an area on display to redraw it
* @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
* @param disp pointer to display where the area should be invalidated (NULL can be used if there is
* only one display)
*/
void _lv_inv_area(lv_disp_t * disp, const lv_area_t * area_p)
{
if(!disp) disp = lv_disp_get_default();
if(!disp) return;
/*Clear the invalidate buffer if the parameter is NULL*/
if(area_p == NULL) {
disp->inv_p = 0;
return;
}
lv_area_t scr_area;
scr_area.x1 = 0;
scr_area.y1 = 0;
scr_area.x2 = lv_disp_get_hor_res(disp) - 1;
scr_area.y2 = lv_disp_get_ver_res(disp) - 1;
ets_printf("scr_area x1: %d, x2: %d, y1: %d, y2: %d\n", scr_area.x1, scr_area.x2, scr_area.y1, scr_area.y2);
lv_area_t com_area;
bool suc;
suc = _lv_area_intersect(&com_area, area_p, &scr_area);
if(suc == false) return; /*Out of the screen*/
after calling lv_obj_set_style_bg_color(scr, lv_palette_main(LV_PALETTE_YELLOW), LV_PART_MAIN)
to configure background color, I found that somthing wrong from UART log:
scr_area x1: 0, x2: 319, y1: 0, y2: 171
flush offsetx1: 0, offsetx2: 319, offsety1: 0, offsety2: 39
The height of area in st7789_flush
is only 40, and in _lv_inv_area
the height is the 172.
I think maybe it is the reason that the LVGL only renders the part of the display, But unfortunately I can not figure out what cause it. Hope that someone could give me some advices about this issue. Thanks so much!
What MCU/Processor/Board and compiler are you using?
ESP32-PICO-D4
Display resolution:320x172
Display controller: ST7789
What LVGL version are you using?
lv_port_esp32 (branch: master branch)
lvgl (branch: release/v8.0)
esp-idf (branch: release/v4.1)
What do you want to achieve?
Code to reproduce
The following is the guiTask:
/*********************
* DEFINES
*********************/
#define TAG "demo"
#define LV_TICK_PERIOD_MS 1
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_tick_task(void *arg);
static void guiTask(void *pvParameter);
static void demo_application(void);
/**********************
* APPLICATION MAIN
**********************/
void app_main() {
/* If you want to use a task to create the graphic, you NEED to create a Pinned task
* Otherwise there can be problem such as memory corruption and so on.
* NOTE: When not using Wi-Fi nor Bluetooth you can pin the guiTask to core 0 */
xTaskCreatePinnedToCore(guiTask, "gui", 4096*2, NULL, 0, NULL, 1);
}
/* Creates a semaphore to handle concurrent call to lvgl stuff
* If you wish to call *any* lvgl function from other threads/tasks
* you should lock on the very same semaphore! */
SemaphoreHandle_t xGuiSemaphore;
static void guiTask(void *pvParameter) {
(void) pvParameter;
xGuiSemaphore = xSemaphoreCreateMutex();
lv_init();
/* Initialize SPI or I2C bus used by the drivers */
lvgl_driver_init();
lv_color_t* buf1 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
assert(buf1 != NULL);
/* Use double buffered when not working with monochrome displays */
#ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME
lv_color_t* buf2 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
assert(buf2 != NULL);
#else
static lv_color_t *buf2 = NULL;
#endif
static lv_disp_draw_buf_t draw_buf;
uint32_t size_in_px = DISP_BUF_SIZE;
#if defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820 \
|| defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A \
|| defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D \
|| defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306
/* Actual size in pixels, not bytes. */
size_in_px *= 8;
#endif
/* Initialize the working buffer depending on the selected display.
* NOTE: buf2 == NULL when using monochrome displays. */
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, size_in_px);
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.flush_cb = disp_driver_flush;
disp_drv.hor_res = LV_HOR_RES_MAX; /*Set the horizontal resolution of the display*/
disp_drv.ver_res = LV_VER_RES_MAX;
#if defined CONFIG_DISPLAY_ORIENTATION_PORTRAIT || defined CONFIG_DISPLAY_ORIENTATION_PORTRAIT_INVERTED
disp_drv.rotated = 1;
#endif
/* When using a monochrome display we need to register the callbacks:
* - rounder_cb
* - set_px_cb */
#ifdef CONFIG_LV_TFT_DISPLAY_MONOCHROME
disp_drv.rounder_cb = disp_driver_rounder;
disp_drv.set_px_cb = disp_driver_set_px;
#endif
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
/* Register an input device when enabled on the menuconfig */
#if CONFIG_LV_TOUCH_CONTROLLER != TOUCH_CONTROLLER_NONE
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.read_cb = touch_driver_read;
indev_drv.type = LV_INDEV_TYPE_POINTER;
lv_indev_drv_register(&indev_drv);
#endif
/* Create and start a periodic timer interrupt to call lv_tick_inc */
const esp_timer_create_args_t periodic_timer_args = {
.callback = &lv_tick_task,
.name = "periodic_gui"
};
esp_timer_handle_t periodic_timer;
ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, LV_TICK_PERIOD_MS * 1000));
/* Create the demo application */
demo_application();
while (1) {
/* Delay 1 tick (assumes FreeRTOS tick is 10ms */
vTaskDelay(pdMS_TO_TICKS(10));
/* Try to take the semaphore, call lvgl related function on success */
if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {
lv_task_handler();
xSemaphoreGive(xGuiSemaphore);
}
}
/* A task should NEVER return */
free(buf1);
#ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME
free(buf2);
#endif
vTaskDelete(NULL);
}
static void lv_tick_task(void *arg) {
(void) arg;
lv_tick_inc(LV_TICK_PERIOD_MS);
}
and my simple test demo:
static void demo_application(void)
{
lv_obj_t *scr = lv_obj_create(lv_scr_act());
lv_obj_set_size(scr, 320, 172);
lv_obj_set_pos(scr, 0, 0);
lv_obj_set_style_bg_color(scr, lv_palette_main(LV_PALETTE_YELLOW), LV_PART_MAIN);
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_obj_set_pos(label, 0, 0);
lv_label_set_text(label, "Hello, world\n");
}
I define the LV_HOR_RES_MAX
and LV_VER_RES_MAX
in lv_conf_internal.h
:
/* Maximal horizontal and vertical resolution to support by the library.*/
#ifndef LV_HOR_RES_MAX
# ifdef CONFIG_LV_HOR_RES_MAX
# define LV_HOR_RES_MAX CONFIG_LV_HOR_RES_MAX
# else
# define LV_HOR_RES_MAX (320)
# endif
#endif
#ifndef LV_VER_RES_MAX
# ifdef CONFIG_LV_VER_RES_MAX
# define LV_VER_RES_MAX CONFIG_LV_VER_RES_MAX
# else
# define LV_VER_RES_MAX (172)
# endif
#endif