feat(lcd): support buffer switch interrupt

This commit is contained in:
Chen Jichang
2026-04-20 18:16:49 +08:00
parent 33589325c7
commit e1d48975fb
26 changed files with 337 additions and 104 deletions

View File

@@ -1,4 +1,4 @@
dependencies:
lvgl/lvgl: "9.4.0"
lvgl/lvgl: "9.5.0"
esp_lcd_ili9881c: "^1.0.0"
esp_lcd_ek79007: "^1.0.0"

View File

@@ -331,7 +331,7 @@ void app_main(void)
esp_lcd_dpi_panel_event_callbacks_t cbs = {
.on_color_trans_done = example_notify_lvgl_flush_ready,
#if CONFIG_EXAMPLE_MONITOR_REFRESH_BY_GPIO
.on_refresh_done = example_monitor_refresh_rate,
.on_vsync = example_monitor_refresh_rate,
#endif
};
ESP_ERROR_CHECK(esp_lcd_dpi_panel_register_event_callbacks(mipi_dpi_panel, &cbs, display));

View File

@@ -1,3 +1,3 @@
idf_component_register(SRCS "rgb_lcd_example_main.c" "lvgl_demo_ui.c"
PRIV_REQUIRES esp_lcd
PRIV_REQUIRES esp_lcd esp_timer
INCLUDE_DIRS ".")

View File

@@ -1,2 +1,2 @@
dependencies:
lvgl/lvgl: "9.2.0"
lvgl/lvgl: "9.5.0"

View File

@@ -101,15 +101,43 @@ static const char *TAG = "example";
// LVGL library is not thread-safe, this example will call LVGL APIs from different tasks, so use a mutex to protect it
static _lock_t lvgl_api_lock;
static TaskHandle_t lvgl_task_handle;
extern void example_lvgl_demo_ui(lv_display_t *disp);
#if CONFIG_EXAMPLE_USE_DOUBLE_FB
static bool example_on_frame_buf_complete(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *event_data, void *user_ctx)
{
(void)panel;
(void)event_data;
(void)user_ctx;
BaseType_t need_yield = pdFALSE;
if (lvgl_task_handle) {
vTaskNotifyGiveFromISR(lvgl_task_handle, &need_yield);
}
return need_yield == pdTRUE;
}
static void example_lvgl_flush_wait_cb(lv_display_t *disp)
{
// The flush callback only submits the rendered buffer to the LCD driver.
// With direct-mode double buffering, LVGL must wait until the RGB panel has
// switched away from the previous frame buffer before rendering into it again.
if (lv_display_flush_is_last(disp)) {
// Wait until the previous frame buffer is no longer referenced by DMA.
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
}
lv_display_flush_ready(disp);
}
#else
static bool example_notify_lvgl_flush_ready(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *event_data, void *user_ctx)
{
lv_display_t *disp = (lv_display_t *)user_ctx;
lv_display_flush_ready(disp);
return false;
}
#endif
static void example_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
{
@@ -118,6 +146,20 @@ static void example_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uin
int offsetx2 = area->x2;
int offsety1 = area->y1;
int offsety2 = area->y2;
#if CONFIG_EXAMPLE_USE_DOUBLE_FB
if (!lv_display_flush_is_last(disp)) {
lv_display_flush_ready(disp);
return;
}
// In direct mode, LVGL may flush multiple dirty areas. Switch the RGB panel to the new
// frame buffer only after the last dirty area has been rendered.
offsetx1 = 0;
offsety1 = 0;
offsetx2 = EXAMPLE_LCD_H_RES - 1;
offsety2 = EXAMPLE_LCD_V_RES - 1;
// Clear any stale completion from the previous frame before waiting for this frame.
ulTaskNotifyTake(pdTRUE, 0);
#endif
// pass the draw buffer to the driver
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, px_map);
}
@@ -264,10 +306,19 @@ void app_main(void)
// set the callback which can copy the rendered image to an area of the display
lv_display_set_flush_cb(display, example_lvgl_flush_cb);
#if CONFIG_EXAMPLE_USE_DOUBLE_FB
// The wait callback keeps LVGL from reusing a frame buffer until the panel driver
// reports that the buffer has finished refreshing.
lv_display_set_flush_wait_cb(display, example_lvgl_flush_wait_cb);
#endif
ESP_LOGI(TAG, "Register event callbacks");
esp_lcd_rgb_panel_event_callbacks_t cbs = {
#if CONFIG_EXAMPLE_USE_DOUBLE_FB
.on_frame_buf_complete = example_on_frame_buf_complete,
#else
.on_color_trans_done = example_notify_lvgl_flush_ready,
#endif
};
ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, display));
@@ -282,7 +333,7 @@ void app_main(void)
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
ESP_LOGI(TAG, "Create LVGL task");
xTaskCreate(example_lvgl_port_task, "LVGL", EXAMPLE_LVGL_TASK_STACK_SIZE, NULL, EXAMPLE_LVGL_TASK_PRIORITY, NULL);
xTaskCreate(example_lvgl_port_task, "LVGL", EXAMPLE_LVGL_TASK_STACK_SIZE, NULL, EXAMPLE_LVGL_TASK_PRIORITY, &lvgl_task_handle);
ESP_LOGI(TAG, "Display LVGL UI");
// Lock the mutex due to the LVGL APIs are not thread-safe