Skip to content

Commit

Permalink
Merge branch 'feat/rgb_lcd_example_lvgl9' into 'master'
Browse files Browse the repository at this point in the history
feat(rgb_lcd): adapt to LVGL v9

See merge request espressif/esp-idf!33479
  • Loading branch information
suda-morris committed Sep 12, 2024
2 parents 2cef806 + 6e8a958 commit c6433de
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 124 deletions.
19 changes: 9 additions & 10 deletions examples/peripherals/lcd/rgb_panel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@

# 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:

| Number of Frame Buffers | LVGL buffering mode | Way to avoid tear effect |
|-------------------------|---------------------|-------------------------------------------------------------------------------------------------------------|
| 1 | Two buffers | Extra synchronization mechanism is needed, e.g. using semaphore. |
| 2 | Full refresh | There's no intersection between writing to an offline frame buffer and reading from an online frame buffer. |
| Driver Buffers | LVGL draw buffers | Pros and Cons |
|-----------------|---------------------|-----------------------------------------------------------------------------------|
| 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 Frame Buffer + </br> 2 Bounce Buffers | Two partial buffers | Fast DMA read, high CPU usage, more internal memory cost |

## How to use the example

Expand Down Expand Up @@ -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.
* 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_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?
* 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.

Expand Down
3 changes: 1 addition & 2 deletions examples/peripherals/lcd/rgb_panel/main/idf_component.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
dependencies:
idf: ">=4.4"
lvgl/lvgl: "~8.3.0"
lvgl/lvgl: "^9.0.0"
37 changes: 19 additions & 18 deletions examples/peripherals/lcd/rgb_panel/main/lvgl_demo_ui.c
Original file line number Diff line number Diff line change
@@ -1,58 +1,59 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* 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"

static void draw_event_cb(lv_event_t *e)
{
lv_obj_draw_part_dsc_t *dsc = lv_event_get_draw_part_dsc(e);
if (dsc->part == LV_PART_ITEMS) {
lv_draw_task_t *draw_task = lv_event_get_draw_task(e);
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_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);

/*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*/
lv_coord_t *x_array = lv_chart_get_x_array(obj, ser);
lv_coord_t *y_array = lv_chart_get_y_array(obj, ser);
int32_t *x_array = lv_chart_get_x_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.*/
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 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),
x_opa + y_opa);
}
}

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));
}

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_set_size(chart, 200, 150);
lv_obj_set_size(chart, 400, 300);
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_set_style_line_width(chart, 0, LV_PART_ITEMS); /*Remove the lines*/
lv_obj_add_event_cb(chart, draw_event_cb, LV_EVENT_DRAW_TASK_ADDED, NULL);
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_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_Y, 0, 1000);

Expand Down
Loading

0 comments on commit c6433de

Please sign in to comment.