|
|
|
|
@@ -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
|
|
|
|
|
|