Merge branch 'feat/rgb_lcd_example_lvgl9' into 'master'

feat(rgb_lcd): adapt to LVGL v9

See merge request espressif/esp-idf!33479
This commit is contained in:
morris 2024-09-12 18:32:26 +08:00
commit c6433de7cc
7 changed files with 120 additions and 126 deletions

View File

@ -3,18 +3,17 @@
# RGB LCD Panel Example # RGB LCD Panel Example
[esp_lcd](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html) supports RGB interfaced LCD panel, with one or two frame buffer(s) managed by the driver itself. [esp_lcd](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/lcd/rgb_lcd.html) supports RGB interfaced LCD panel, with multiple buffer modes. This example shows the general process of installing an RGB panel driver, and displays a scatter chart on the screen based on the LVGL library.
This example shows the general process of installing an RGB panel driver, and displays a scatter chart on the screen based on the LVGL library. This example uses the [esp_timer](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/esp_timer.html) to generate the ticks needed by LVGL and uses a dedicated task to run the `lv_timer_handler()`. Since the LVGL APIs are not thread-safe, this example uses a mutex which be invoked before the call of `lv_timer_handler()` and released after it. The same mutex needs to be used in other tasks and threads around every LVGL (lv_...) related function call and code. For more porting guides, please refer to [LVGL Display porting reference](https://docs.lvgl.io/master/porting/display.html).
This example uses the [esp_timer](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/esp_timer.html) to generate the ticks needed by LVGL and uses a dedicated task to run the `lv_timer_handler()`. Since the LVGL APIs are not thread-safe, this example uses a mutex which be invoked before the call of `lv_timer_handler()` and released after it. The same mutex needs to be used in other tasks and threads around every LVGL (lv_...) related function call and code. For more porting guides, please refer to [LVGL porting doc](https://docs.lvgl.io/master/porting/index.html). This example uses 3 kinds of **buffering mode**:
This example uses two kinds of **buffering mode** based on the number of frame buffers: | Driver Buffers | LVGL draw buffers | Pros and Cons |
|-----------------|---------------------|-----------------------------------------------------------------------------------|
| Number of Frame Buffers | LVGL buffering mode | Way to avoid tear effect | | 1 Frame Buffer | Two partial buffers | fewest memory footprint, performance affected by memory copy |
|-------------------------|---------------------|-------------------------------------------------------------------------------------------------------------| | 2 Frame Buffers | Direct refresh mode | no extra memory copy between draw buffer and frame buffer, large memory cost |
| 1 | Two buffers | Extra synchronization mechanism is needed, e.g. using semaphore. | | 1 Frame Buffer + </br> 2 Bounce Buffers | Two partial buffers | Fast DMA read, high CPU usage, more internal memory cost |
| 2 | Full refresh | There's no intersection between writing to an offline frame buffer and reading from an online frame buffer. |
## How to use the example ## How to use the example
@ -114,7 +113,7 @@ I (926) example: Display LVGL Scatter Chart
* Or adding an extra synchronization mechanism between writing (by Cache) and reading (by EDMA) the frame buffer. * Or adding an extra synchronization mechanism between writing (by Cache) and reading (by EDMA) the frame buffer.
* Low PCLK frequency * Low PCLK frequency
* Enable `CONFIG_EXAMPLE_USE_BOUNCE_BUFFER`, which will make the LCD controller fetch data from internal SRAM (instead of the PSRAM), but at the cost of increasing CPU usage. * Enable `CONFIG_EXAMPLE_USE_BOUNCE_BUFFER`, which will make the LCD controller fetch data from internal SRAM (instead of the PSRAM), but at the cost of increasing CPU usage.
* Enable `CONFIG_SPIRAM_FETCH_INSTRUCTIONS` and `CONFIG_SPIRAM_RODATA` can also help if the you're not using the bounce buffer mode. These two configurations can save some **SPI0** bandwidth from being consumed by ICache. * Enable `CONFIG_SPIRAM_XIP_FROM_PSRAM` can also help if the you're not using the bounce buffer mode. These two configurations can save some **SPI0** bandwidth from being consumed by ICache.
* Why the RGB timing is correct but the LCD doesn't show anything? * Why the RGB timing is correct but the LCD doesn't show anything?
* Please read the datasheet of the IC used by your LCD module, and check if it needs a special initialization sequence. The initialization is usually done by sending some specific SPI commands and parameters to the IC. After the initialization, the LCD will be ready to receive RGB data. For simplicity, this example only works out of the box for those LCD modules which don't need extra initialization. * Please read the datasheet of the IC used by your LCD module, and check if it needs a special initialization sequence. The initialization is usually done by sending some specific SPI commands and parameters to the IC. After the initialization, the LCD will be ready to receive RGB data. For simplicity, this example only works out of the box for those LCD modules which don't need extra initialization.

View File

@ -1,3 +1,2 @@
dependencies: dependencies:
idf: ">=4.4" lvgl/lvgl: "^9.0.0"
lvgl/lvgl: "~8.3.0"

View File

@ -1,33 +1,36 @@
/* /*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: CC0-1.0 * SPDX-License-Identifier: CC0-1.0
*/ */
// This demo UI is adapted from LVGL official example: https://docs.lvgl.io/master/examples.html#scatter-chart // This demo UI is adapted from LVGL official example: https://docs.lvgl.io/master/widgets/chart.html#scatter-chart
#include "lvgl.h" #include "lvgl.h"
static void draw_event_cb(lv_event_t *e) static void draw_event_cb(lv_event_t *e)
{ {
lv_obj_draw_part_dsc_t *dsc = lv_event_get_draw_part_dsc(e); lv_draw_task_t *draw_task = lv_event_get_draw_task(e);
if (dsc->part == LV_PART_ITEMS) { lv_draw_dsc_base_t *base_dsc = lv_draw_task_get_draw_dsc(draw_task);
if (base_dsc->part == LV_PART_INDICATOR) {
lv_obj_t *obj = lv_event_get_target(e); lv_obj_t *obj = lv_event_get_target(e);
lv_chart_series_t *ser = lv_chart_get_series_next(obj, NULL); lv_chart_series_t *ser = lv_chart_get_series_next(obj, NULL);
lv_draw_rect_dsc_t *rect_draw_dsc = lv_draw_task_get_draw_dsc(draw_task);
uint32_t cnt = lv_chart_get_point_count(obj); uint32_t cnt = lv_chart_get_point_count(obj);
/*Make older value more transparent*/ /*Make older value more transparent*/
dsc->rect_dsc->bg_opa = (LV_OPA_COVER * dsc->id) / (cnt - 1); rect_draw_dsc->bg_opa = (LV_OPA_COVER * base_dsc->id2) / (cnt - 1);
/*Make smaller values blue, higher values red*/ /*Make smaller values blue, higher values red*/
lv_coord_t *x_array = lv_chart_get_x_array(obj, ser); int32_t *x_array = lv_chart_get_x_array(obj, ser);
lv_coord_t *y_array = lv_chart_get_y_array(obj, ser); int32_t *y_array = lv_chart_get_y_array(obj, ser);
/*dsc->id is the tells drawing order, but we need the ID of the point being drawn.*/ /*dsc->id is the tells drawing order, but we need the ID of the point being drawn.*/
uint32_t start_point = lv_chart_get_x_start_point(obj, ser); uint32_t start_point = lv_chart_get_x_start_point(obj, ser);
uint32_t p_act = (start_point + dsc->id) % cnt; /*Consider start point to get the index of the array*/ uint32_t p_act = (start_point + base_dsc->id2) % cnt; /*Consider start point to get the index of the array*/
lv_opa_t x_opa = (x_array[p_act] * LV_OPA_50) / 200; lv_opa_t x_opa = (x_array[p_act] * LV_OPA_50) / 200;
lv_opa_t y_opa = (y_array[p_act] * LV_OPA_50) / 1000; lv_opa_t y_opa = (y_array[p_act] * LV_OPA_50) / 1000;
dsc->rect_dsc->bg_color = lv_color_mix(lv_palette_main(LV_PALETTE_RED), rect_draw_dsc->bg_color = lv_color_mix(lv_palette_main(LV_PALETTE_RED),
lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_BLUE),
x_opa + y_opa); x_opa + y_opa);
} }
@ -35,24 +38,22 @@ static void draw_event_cb(lv_event_t *e)
static void add_data(lv_timer_t *timer) static void add_data(lv_timer_t *timer)
{ {
lv_obj_t *chart = timer->user_data; lv_obj_t *chart = lv_timer_get_user_data(timer);
lv_chart_set_next_value2(chart, lv_chart_get_series_next(chart, NULL), lv_rand(0, 200), lv_rand(0, 1000)); lv_chart_set_next_value2(chart, lv_chart_get_series_next(chart, NULL), lv_rand(0, 200), lv_rand(0, 1000));
} }
void example_lvgl_demo_ui(lv_disp_t *disp) void example_lvgl_demo_ui(lv_display_t *disp)
{ {
lv_obj_t *scr = lv_disp_get_scr_act(disp); lv_obj_t *scr = lv_display_get_screen_active(disp);
lv_obj_t *chart = lv_chart_create(scr); lv_obj_t *chart = lv_chart_create(scr);
lv_obj_set_size(chart, 200, 150); lv_obj_set_size(chart, 400, 300);
lv_obj_align(chart, LV_ALIGN_CENTER, 0, 0); lv_obj_align(chart, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_event_cb(chart, draw_event_cb, LV_EVENT_DRAW_PART_BEGIN, NULL); lv_obj_add_event_cb(chart, draw_event_cb, LV_EVENT_DRAW_TASK_ADDED, NULL);
lv_obj_set_style_line_width(chart, 0, LV_PART_ITEMS); /*Remove the lines*/ lv_obj_add_flag(chart, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS);
lv_obj_set_style_line_width(chart, 0, LV_PART_ITEMS);
lv_chart_set_type(chart, LV_CHART_TYPE_SCATTER); lv_chart_set_type(chart, LV_CHART_TYPE_SCATTER);
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_X, 5, 5, 5, 1, true, 30);
lv_chart_set_axis_tick(chart, LV_CHART_AXIS_PRIMARY_Y, 10, 5, 6, 5, true, 50);
lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_X, 0, 200); lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_X, 0, 200);
lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, 0, 1000); lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, 0, 1000);

View File

@ -1,10 +1,12 @@
/* /*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: CC0-1.0 * SPDX-License-Identifier: CC0-1.0
*/ */
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include <sys/lock.h>
#include "sdkconfig.h" #include "sdkconfig.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
@ -22,7 +24,17 @@ static const char *TAG = "example";
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec ////////////////////////////// //////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FPS = 18000000/(1+40+20+800)/(1+8+4+480) = 42Hz
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (18 * 1000 * 1000) #define EXAMPLE_LCD_PIXEL_CLOCK_HZ (18 * 1000 * 1000)
#define EXAMPLE_LCD_H_RES 800
#define EXAMPLE_LCD_V_RES 480
#define EXAMPLE_LCD_HSYNC 1
#define EXAMPLE_LCD_HBP 40
#define EXAMPLE_LCD_HFP 20
#define EXAMPLE_LCD_VSYNC 1
#define EXAMPLE_LCD_VBP 8
#define EXAMPLE_LCD_VFP 4
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 1 #define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL #define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_BK_LIGHT 4 #define EXAMPLE_PIN_NUM_BK_LIGHT 4
@ -58,13 +70,17 @@ static const char *TAG = "example";
#define EXAMPLE_LCD_NUM_FB 1 #define EXAMPLE_LCD_NUM_FB 1
#endif // CONFIG_EXAMPLE_DOUBLE_FB #endif // CONFIG_EXAMPLE_DOUBLE_FB
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your Application ///////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define EXAMPLE_LVGL_DRAW_BUF_LINES 50 // number of display lines in each draw buffer
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2 #define EXAMPLE_LVGL_TICK_PERIOD_MS 2
#define EXAMPLE_LVGL_TASK_MAX_DELAY_MS 500
#define EXAMPLE_LVGL_TASK_MIN_DELAY_MS 1
#define EXAMPLE_LVGL_TASK_STACK_SIZE (4 * 1024) #define EXAMPLE_LVGL_TASK_STACK_SIZE (4 * 1024)
#define EXAMPLE_LVGL_TASK_PRIORITY 2 #define EXAMPLE_LVGL_TASK_PRIORITY 2
static SemaphoreHandle_t lvgl_mux = NULL; // 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;
// we use two semaphores to sync the VSYNC event and the LVGL task, to avoid potential tearing effect // we use two semaphores to sync the VSYNC event and the LVGL task, to avoid potential tearing effect
#if CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM #if CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM
@ -72,7 +88,7 @@ SemaphoreHandle_t sem_vsync_end;
SemaphoreHandle_t sem_gui_ready; SemaphoreHandle_t sem_gui_ready;
#endif #endif
extern void example_lvgl_demo_ui(lv_disp_t *disp); extern void example_lvgl_demo_ui(lv_display_t *disp);
static bool example_on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *event_data, void *user_data) static bool example_on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *event_data, void *user_data)
{ {
@ -85,9 +101,9 @@ static bool example_on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_r
return high_task_awoken == pdTRUE; return high_task_awoken == pdTRUE;
} }
static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) static void example_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
{ {
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; esp_lcd_panel_handle_t panel_handle = lv_display_get_user_data(disp);
int offsetx1 = area->x1; int offsetx1 = area->x1;
int offsetx2 = area->x2; int offsetx2 = area->x2;
int offsety1 = area->y1; int offsety1 = area->y1;
@ -97,8 +113,8 @@ static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_
xSemaphoreTake(sem_vsync_end, portMAX_DELAY); xSemaphoreTake(sem_vsync_end, portMAX_DELAY);
#endif #endif
// pass the draw buffer to the driver // pass the draw buffer to the driver
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, px_map);
lv_disp_flush_ready(drv); lv_display_flush_ready(disp);
} }
static void example_increase_lvgl_tick(void *arg) static void example_increase_lvgl_tick(void *arg)
@ -107,44 +123,38 @@ static void example_increase_lvgl_tick(void *arg)
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS); lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
} }
bool example_lvgl_lock(int timeout_ms)
{
// Convert timeout in milliseconds to FreeRTOS ticks
// If `timeout_ms` is set to -1, the program will block until the condition is met
const TickType_t timeout_ticks = (timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE;
}
void example_lvgl_unlock(void)
{
xSemaphoreGiveRecursive(lvgl_mux);
}
static void example_lvgl_port_task(void *arg) static void example_lvgl_port_task(void *arg)
{ {
ESP_LOGI(TAG, "Starting LVGL task"); ESP_LOGI(TAG, "Starting LVGL task");
uint32_t task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS; uint32_t time_till_next_ms = 0;
while (1) { while (1) {
// Lock the mutex due to the LVGL APIs are not thread-safe _lock_acquire(&lvgl_api_lock);
if (example_lvgl_lock(-1)) { time_till_next_ms = lv_timer_handler();
task_delay_ms = lv_timer_handler(); _lock_release(&lvgl_api_lock);
// Release the mutex usleep(1000 * time_till_next_ms);
example_lvgl_unlock();
}
if (task_delay_ms > EXAMPLE_LVGL_TASK_MAX_DELAY_MS) {
task_delay_ms = EXAMPLE_LVGL_TASK_MAX_DELAY_MS;
} else if (task_delay_ms < EXAMPLE_LVGL_TASK_MIN_DELAY_MS) {
task_delay_ms = EXAMPLE_LVGL_TASK_MIN_DELAY_MS;
}
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
} }
} }
static void example_bsp_init_lcd_backlight(void)
{
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT
};
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
#endif
}
static void example_bsp_set_lcd_backlight(uint32_t level)
{
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, level);
#endif
}
void app_main(void) void app_main(void)
{ {
static lv_disp_draw_buf_t disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
static lv_disp_drv_t disp_drv; // contains callback functions
#if CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM #if CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM
ESP_LOGI(TAG, "Create semaphores"); ESP_LOGI(TAG, "Create semaphores");
sem_vsync_end = xSemaphoreCreateBinary(); sem_vsync_end = xSemaphoreCreateBinary();
@ -153,14 +163,9 @@ void app_main(void)
assert(sem_gui_ready); assert(sem_gui_ready);
#endif #endif
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
ESP_LOGI(TAG, "Turn off LCD backlight"); ESP_LOGI(TAG, "Turn off LCD backlight");
gpio_config_t bk_gpio_config = { example_bsp_init_lcd_backlight();
.mode = GPIO_MODE_OUTPUT, example_bsp_set_lcd_backlight(EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT
};
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
#endif
ESP_LOGI(TAG, "Install RGB LCD panel driver"); ESP_LOGI(TAG, "Install RGB LCD panel driver");
esp_lcd_panel_handle_t panel_handle = NULL; esp_lcd_panel_handle_t panel_handle = NULL;
@ -169,7 +174,7 @@ void app_main(void)
.dma_burst_size = 64, .dma_burst_size = 64,
.num_fbs = EXAMPLE_LCD_NUM_FB, .num_fbs = EXAMPLE_LCD_NUM_FB,
#if CONFIG_EXAMPLE_USE_BOUNCE_BUFFER #if CONFIG_EXAMPLE_USE_BOUNCE_BUFFER
.bounce_buffer_size_px = 10 * EXAMPLE_LCD_H_RES, .bounce_buffer_size_px = 20 * EXAMPLE_LCD_H_RES,
#endif #endif
.clk_src = LCD_CLK_SRC_DEFAULT, .clk_src = LCD_CLK_SRC_DEFAULT,
.disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN, .disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN,
@ -199,62 +204,61 @@ void app_main(void)
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ, .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
.h_res = EXAMPLE_LCD_H_RES, .h_res = EXAMPLE_LCD_H_RES,
.v_res = EXAMPLE_LCD_V_RES, .v_res = EXAMPLE_LCD_V_RES,
// The following parameters should refer to LCD spec .hsync_back_porch = EXAMPLE_LCD_HBP,
.hsync_back_porch = 40, .hsync_front_porch = EXAMPLE_LCD_HFP,
.hsync_front_porch = 20, .hsync_pulse_width = EXAMPLE_LCD_HSYNC,
.hsync_pulse_width = 1, .vsync_back_porch = EXAMPLE_LCD_VBP,
.vsync_back_porch = 8, .vsync_front_porch = EXAMPLE_LCD_VFP,
.vsync_front_porch = 4, .vsync_pulse_width = EXAMPLE_LCD_VSYNC,
.vsync_pulse_width = 1, .flags = {
.flags.pclk_active_neg = true, .pclk_active_neg = true,
},
}, },
.flags.fb_in_psram = true, // allocate frame buffer in PSRAM .flags.fb_in_psram = true, // allocate frame buffer in PSRAM
}; };
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle)); ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
ESP_LOGI(TAG, "Register event callbacks");
esp_lcd_rgb_panel_event_callbacks_t cbs = {
.on_vsync = example_on_vsync_event,
};
ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, &disp_drv));
ESP_LOGI(TAG, "Initialize RGB LCD panel"); ESP_LOGI(TAG, "Initialize RGB LCD panel");
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
ESP_LOGI(TAG, "Turn on LCD backlight"); ESP_LOGI(TAG, "Turn on LCD backlight");
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL); example_bsp_set_lcd_backlight(EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
#endif
ESP_LOGI(TAG, "Initialize LVGL library"); ESP_LOGI(TAG, "Initialize LVGL library");
lv_init(); lv_init();
// create a lvgl display
lv_display_t *display = lv_display_create(EXAMPLE_LCD_H_RES, EXAMPLE_LCD_V_RES);
// associate the rgb panel handle to the display
lv_display_set_user_data(display, panel_handle);
// set color depth
lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB565);
// create draw buffers
void *buf1 = NULL; void *buf1 = NULL;
void *buf2 = NULL; void *buf2 = NULL;
#if CONFIG_EXAMPLE_DOUBLE_FB #if CONFIG_EXAMPLE_DOUBLE_FB
ESP_LOGI(TAG, "Use frame buffers as LVGL draw buffers"); ESP_LOGI(TAG, "Use frame buffers as LVGL draw buffers");
ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &buf1, &buf2)); ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &buf1, &buf2));
// initialize LVGL draw buffers // set LVGL draw buffers and direct mode
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES); lv_display_set_buffers(display, buf1, buf2, EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES * sizeof(lv_color16_t), LV_DISPLAY_RENDER_MODE_DIRECT);
#else #else
ESP_LOGI(TAG, "Allocate separate LVGL draw buffers from PSRAM"); ESP_LOGI(TAG, "Allocate LVGL draw buffers");
buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM); // it's recommended to allocate the draw buffer from internal memory, for better performance
size_t draw_buffer_sz = EXAMPLE_LCD_H_RES * EXAMPLE_LVGL_DRAW_BUF_LINES * sizeof(lv_color16_t);
buf1 = heap_caps_malloc(draw_buffer_sz, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
assert(buf1); assert(buf1);
// initialize LVGL draw buffers // set LVGL draw buffers and partial mode
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 100); lv_display_set_buffers(display, buf1, buf2, draw_buffer_sz, LV_DISPLAY_RENDER_MODE_PARTIAL);
#endif // CONFIG_EXAMPLE_DOUBLE_FB #endif // CONFIG_EXAMPLE_DOUBLE_FB
ESP_LOGI(TAG, "Register display driver to LVGL"); // set the callback which can copy the rendered image to an area of the display
lv_disp_drv_init(&disp_drv); lv_display_set_flush_cb(display, example_lvgl_flush_cb);
disp_drv.hor_res = EXAMPLE_LCD_H_RES;
disp_drv.ver_res = EXAMPLE_LCD_V_RES; ESP_LOGI(TAG, "Register event callbacks");
disp_drv.flush_cb = example_lvgl_flush_cb; esp_lcd_rgb_panel_event_callbacks_t cbs = {
disp_drv.draw_buf = &disp_buf; .on_vsync = example_on_vsync_event,
disp_drv.user_data = panel_handle; };
#if CONFIG_EXAMPLE_DOUBLE_FB ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, display));
disp_drv.full_refresh = true; // the full_refresh mode can maintain the synchronization between the two frame buffers
#endif
lv_disp_t *disp = lv_disp_drv_register(&disp_drv);
ESP_LOGI(TAG, "Install LVGL tick timer"); ESP_LOGI(TAG, "Install LVGL tick timer");
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event) // Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
@ -266,16 +270,12 @@ void app_main(void)
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer)); ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000)); ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
lvgl_mux = xSemaphoreCreateRecursiveMutex();
assert(lvgl_mux);
ESP_LOGI(TAG, "Create LVGL task"); 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, NULL);
ESP_LOGI(TAG, "Display LVGL Scatter Chart"); ESP_LOGI(TAG, "Display LVGL Scatter Chart");
// Lock the mutex due to the LVGL APIs are not thread-safe // Lock the mutex due to the LVGL APIs are not thread-safe
if (example_lvgl_lock(-1)) { _lock_acquire(&lvgl_api_lock);
example_lvgl_demo_ui(disp); example_lvgl_demo_ui(display);
// Release the mutex _lock_release(&lvgl_api_lock);
example_lvgl_unlock();
}
} }

View File

@ -1,6 +1,5 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD # SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
import pytest import pytest
from pytest_embedded import Dut from pytest_embedded import Dut
@ -19,10 +18,8 @@ from pytest_embedded import Dut
def test_rgb_lcd_lvgl(dut: Dut) -> None: def test_rgb_lcd_lvgl(dut: Dut) -> None:
dut.expect_exact('example: Turn off LCD backlight') dut.expect_exact('example: Turn off LCD backlight')
dut.expect_exact('example: Install RGB LCD panel driver') dut.expect_exact('example: Install RGB LCD panel driver')
dut.expect_exact('example: Register event callbacks')
dut.expect_exact('example: Initialize RGB LCD panel') dut.expect_exact('example: Initialize RGB LCD panel')
dut.expect_exact('example: Turn on LCD backlight') dut.expect_exact('example: Turn on LCD backlight')
dut.expect_exact('example: Initialize LVGL library') dut.expect_exact('example: Initialize LVGL library')
dut.expect_exact('example: Register display driver to LVGL')
dut.expect_exact('example: Install LVGL tick timer') dut.expect_exact('example: Install LVGL tick timer')
dut.expect_exact('example: Display LVGL Scatter Chart') dut.expect_exact('example: Display LVGL Scatter Chart')

View File

@ -1,5 +1,4 @@
CONFIG_LV_MEM_CUSTOM=y CONFIG_LV_CONF_SKIP=y
CONFIG_LV_MEMCPY_MEMSET_STD=y CONFIG_LV_USE_OBSERVER=y
CONFIG_LV_USE_USER_DATA=y CONFIG_LV_USE_SYSMON=y
CONFIG_LV_USE_CHART=y
CONFIG_LV_USE_PERF_MONITOR=y CONFIG_LV_USE_PERF_MONITOR=y

View File

@ -4,5 +4,4 @@ CONFIG_SPIRAM_SPEED_80M=y
# Enabling the following configurations can help increase the PCLK frequency in the case when # Enabling the following configurations can help increase the PCLK frequency in the case when
# the Frame Buffer is allocated from the PSRAM and fetched by EDMA # the Frame Buffer is allocated from the PSRAM and fetched by EDMA
CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y CONFIG_SPIRAM_XIP_FROM_PSRAM=y
CONFIG_SPIRAM_RODATA=y