Screen flickers sometime whenever we flush the buffer

Description

Display shows flickering whenever when lvgl flush the buffer.

  • Double buffer is enabled
  • bounce buffer size is 40*LCD_WIDTH
  • LCD size is 480*480 and it is circular display
  • CONFIG_SPIRAM_FETCH_INSTRUCTIONS and CONFIG_SPIRAM_RODATA enabled
  • ‘XIP on PSRAM’ is enable.
  • CPU frequency is 240MHz
  • We are using RGB parallel lines and 16-bit color.
  • Cache configuration

What MCU/Processor/Board and compiler are you using?

We are using ESP32S3R8 module on custom PCB. And compiler is ESP-IDF v5.3.2.

What LVGL version are you using?

LVGL version is 8.3.

What do you want to achieve?

We want to stop flickering.

What have you tried so far?

We checked that eof_count for DMA is correct i.e. 12 for each vsync. We have also tried reducing the bounce buffer, removing the bounce buffer and XiP config. Also tried semaphore.

static void lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{
    esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data;
    int offsetx1 = area->x1;
    int offsetx2 = area->x2;
    int offsety1 = area->y1;
    int offsety2 = area->y2;
#if CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM
    xSemaphoreGive(sem_gui_ready);
    xSemaphoreTake(sem_vsync_end, portMAX_DELAY);
#endif

    // pass the draw buffer to the driver
    esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
    lv_disp_flush_ready(drv);
}

static bool rgb_on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *event_data, void *user_data)
{
    BaseType_t high_task_awoken = pdFALSE;
#if CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM
    if (xSemaphoreTakeFromISR(sem_gui_ready, &high_task_awoken) == pdTRUE)
    {
        xSemaphoreGiveFromISR(sem_vsync_end, &high_task_awoken);
    }
#endif
}

Code to reproduce

Below is the code for initialization as we are facing this issue in all screens

void lv_port_disp_init(void)
{
    static lv_disp_drv_t disp_drv; // contains callback functions

    st7701_reg_init(); // st7701 register config

#if CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM
    /* Create semaphores */
    sem_vsync_end = xSemaphoreCreateBinary();
    assert(sem_vsync_end);
    sem_gui_ready = xSemaphoreCreateBinary();
    assert(sem_gui_ready);
#endif

    gpio_config_t bk_gpio_config = {
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = 1ULL << GPIO_LCD_BL};
    ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));

    ESP_LOGI(TAG, "Install RGB panel driver");
    esp_lcd_rgb_panel_config_t panel_config = {
        .data_width = 16, // RGB565 in parallel mode, thus 16bit in width
        .psram_trans_align = 64,
#if CONFIG_EXAMPLE_USE_BOUNCE_BUFFER
        .bounce_buffer_size_px = 40 * LCD_WIDTH,
#endif
        .clk_src = LCD_CLK_SRC_DEFAULT,
        .disp_gpio_num = GPIO_NUM_NC,
        .pclk_gpio_num = GPIO_LCD_PCLK,
        .vsync_gpio_num = GPIO_LCD_VSYNC,
        .hsync_gpio_num = GPIO_LCD_HSYNC,
        .de_gpio_num = GPIO_LCD_DE,
        .data_gpio_nums = {
            GPIO_LCD_B0,
            GPIO_LCD_B1,
            GPIO_LCD_B2,
            GPIO_LCD_B3,
            GPIO_LCD_B4,
            GPIO_LCD_G0,
            GPIO_LCD_G1,
            GPIO_LCD_G2,
            GPIO_LCD_G3,
            GPIO_LCD_G4,
            GPIO_LCD_G5,
            GPIO_LCD_R0,
            GPIO_LCD_R1,
            GPIO_LCD_R2,
            GPIO_LCD_R3,
            GPIO_LCD_R4,
        },
        .timings = {
            .pclk_hz = 10 * 1000 * 1000, .h_res = LCD_WIDTH, .v_res = LCD_HEIGHT,
            // The following parameters should refer to LCD spec
            .hsync_back_porch = 50,
            .hsync_front_porch = 10,
            .hsync_pulse_width = 8,
            .vsync_back_porch = 20,
            .vsync_front_porch = 10,
            .vsync_pulse_width = 8,
            .flags.pclk_active_neg = false, // RGB data is clocked out on falling edge
        },
        .flags.fb_in_psram = true, // allocate frame buffer in PSRAM
        .flags.double_fb = true,
#if CONFIG_EXAMPLE_DOUBLE_FB
        .flags.double_fb = true, // allocate double frame buffer
#endif                           // CONFIG_EXAMPLE_DOUBLE_FB
    };

    ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &Lcd_panel_handle));

    /* Register event callbacks */
    esp_lcd_rgb_panel_event_callbacks_t cbs = {
        .on_vsync = rgb_on_vsync_event,
        .on_bounce_empty = rgb_on_bounce_buf_fill,
        .on_bounce_frame_finish = rgb_on_bounce_buf_finish,
    };

    ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(Lcd_panel_handle, &cbs, &disp_drv));

    /* Initialize RGB LCD panel */
    ESP_ERROR_CHECK(esp_lcd_panel_reset(Lcd_panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_init(Lcd_panel_handle));

    ESP_LOGI(TAG, "Turn on LCD backlight");
    /*
        打开屏幕背光
    */
    gpio_set_level(GPIO_LCD_BL, 1);
    ESP_LOGI(TAG, "LCD resolution: %dx%d", LCD_WIDTH, LCD_HEIGHT);

    /* initialize LVGL draw buffers */
    /*
        对LVGL渲染屏幕缓冲区进行初始化
    */
    static lv_disp_draw_buf_t disp_buf;
    void *buf1 = NULL;
    void *buf2 = NULL;

#if CONFIG_EXAMPLE_DOUBLE_FB
    ESP_LOGI(TAG, "Use frame buffers as LVGL draw buffers");
    ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(Lcd_panel_handle, 2, &buf1, &buf2));
    // initialize LVGL draw buffers
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, LCD_WIDTH * LCD_HEIGHT);
#else

    ESP_LOGI(TAG, "Allocate separate LVGL draw buffers from PSRAM");
    uint16_t fact = 480;
    buf1 = heap_caps_malloc(LCD_WIDTH * fact * sizeof(lv_color_t), MALLOC_CAP_SPIRAM); // MALLOC_CAP_SPIRAM
    assert(buf1);

#if 1

    buf2 = heap_caps_malloc(LCD_WIDTH * fact * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
    assert(buf2);
    // initialize LVGL draw buffers
    lv_disp_draw_buf_init(&disp_buf, buf1, buf2, LCD_WIDTH * fact);

#else
    lv_disp_draw_buf_init(&disp_buf, buf1, NULL, LCD_WIDTH * fact);
#endif
#endif // CONFIG_EXAMPLE_DOUBLE_FB

    /* Register the display in LVGL */
    lv_disp_drv_init(&disp_drv);

    /*Set the resolution of the display*/
    disp_drv.hor_res = LCD_WIDTH;
    disp_drv.ver_res = LCD_HEIGHT;
    /*Used to copy the buffer's content to the display*/
    disp_drv.flush_cb = lvgl_flush_cb;
    /*Set a display buffer*/
    disp_drv.draw_buf = &disp_buf;

    disp_drv.user_data = Lcd_panel_handle;

    disp_drv.full_refresh = true;

#if CONFIG_EXAMPLE_DOUBLE_FB
    disp_drv.full_refresh = true; // the full_refresh mode can maintain the synchronization between the two frame buffers
#endif

    /*Finally register the driver*/
    lv_disp_drv_register(&disp_drv);
}

Screenshot and/or video