Merge branch 'refactor/rgb_lcd_gpio_assign' into 'master'

feat(lcd): add Kconfig to assign default GPIO for RGB LCD example

See merge request espressif/esp-idf!33734
This commit is contained in:
morris 2024-09-23 18:23:58 +08:00
commit 0690e53294
12 changed files with 356 additions and 124 deletions

View File

@ -13,7 +13,7 @@ extern "C" {
#endif #endif
#if CONFIG_EXAMPLE_LCD_PATTERN_ILI9881C #if CONFIG_EXAMPLE_LCD_PATTERN_ILI9881C
// FPS = 80000000/(40+140+40+800)/(4+16+16+1280) = 60Hz // Refresh Rate = 80000000/(40+140+40+800)/(4+16+16+1280) = 60Hz
#define EXAMPLE_MIPI_DSI_DPI_CLK_MHZ 80 #define EXAMPLE_MIPI_DSI_DPI_CLK_MHZ 80
#define EXAMPLE_MIPI_DSI_IMAGE_HSYNC 40 #define EXAMPLE_MIPI_DSI_IMAGE_HSYNC 40
#define EXAMPLE_MIPI_DSI_IMAGE_HBP 140 #define EXAMPLE_MIPI_DSI_IMAGE_HBP 140
@ -22,7 +22,7 @@ extern "C" {
#define EXAMPLE_MIPI_DSI_IMAGE_VBP 16 #define EXAMPLE_MIPI_DSI_IMAGE_VBP 16
#define EXAMPLE_MIPI_DSI_IMAGE_VFP 16 #define EXAMPLE_MIPI_DSI_IMAGE_VFP 16
#elif CONFIG_EXAMPLE_LCD_PATTERN_EK79007 #elif CONFIG_EXAMPLE_LCD_PATTERN_EK79007
// FPS = 48000000/(10+120+120+1024)/(1+20+10+600) = 60Hz // Refresh Rate = 48000000/(10+120+120+1024)/(1+20+10+600) = 60Hz
#define EXAMPLE_MIPI_DSI_DPI_CLK_MHZ 48 #define EXAMPLE_MIPI_DSI_DPI_CLK_MHZ 48
#define EXAMPLE_MIPI_DSI_IMAGE_HSYNC 10 #define EXAMPLE_MIPI_DSI_IMAGE_HSYNC 10
#define EXAMPLE_MIPI_DSI_IMAGE_HBP 120 #define EXAMPLE_MIPI_DSI_IMAGE_HBP 120

View File

@ -51,8 +51,8 @@ Run `idf.py menuconfig` and go to `Example Configuration`:
* Choose the LCD model in `Select MIPI LCD model` according to your board. * Choose the LCD model in `Select MIPI LCD model` according to your board.
* Choose whether to `Use DMA2D to copy draw buffer to frame buffer` asynchronously. If you choose `No`, the draw buffer will be copied to the frame buffer synchronously by CPU. * Choose whether to `Use DMA2D to copy draw buffer to frame buffer` asynchronously. If you choose `No`, the draw buffer will be copied to the frame buffer synchronously by CPU.
* Choose if you want to `Monitor FPS by GPIO`. If you choose `Yes`, then you can attach an oscilloscope or logic analyzer to the GPIO pin to monitor the FPS of the display. * Choose if you want to `Monitor Refresh Rate by GPIO`. If you choose `Yes`, then you can attach an oscilloscope or logic analyzer to the GPIO pin to monitor the Refresh Rate of the display.
Please note, the actual FPS should be **double** the square wave frequency. Please note, the actual Refresh Rate should be **double** the square wave frequency.
### Build and Flash ### Build and Flash

View File

@ -6,12 +6,12 @@ menu "Example Configuration"
Enable this option, DMA2D will be used to copy the LVGL draw buffer to the target frame buffer. Enable this option, DMA2D will be used to copy the LVGL draw buffer to the target frame buffer.
This can save some CPU time and improve the performance. This can save some CPU time and improve the performance.
config EXAMPLE_MONITOR_FPS_BY_GPIO config EXAMPLE_MONITOR_REFRESH_BY_GPIO
bool "Monitor FPS by GPIO" bool "Monitor Refresh Rate by GPIO"
default y default y
help help
Enable this option, you can visualize the FPS by attaching a logic analyzer to a specific GPIO. Enable this option, you can visualize the refresh rate by attaching a logic analyzer to a specific GPIO.
The GPIO will output a square wave with the frequency of FPS/2. The GPIO will output a square wave with the frequency equals to half of the refresh rate.
choice choice
prompt "Select MIPI LCD model" prompt "Select MIPI LCD model"

View File

@ -1,4 +1,4 @@
dependencies: dependencies:
lvgl/lvgl: "^9.0.0" lvgl/lvgl: "~9.2.0"
esp_lcd_ili9881c: "^1.0.0" esp_lcd_ili9881c: "^1.0.0"
esp_lcd_ek79007: "^1.0.0" esp_lcd_ek79007: "^1.0.0"

View File

@ -29,7 +29,7 @@ static const char *TAG = "example";
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if CONFIG_EXAMPLE_LCD_USE_ILI9881C #if CONFIG_EXAMPLE_LCD_USE_ILI9881C
// FPS = 80000000/(40+140+40+800)/(4+16+16+1280) = 60Hz // Refresh Rate = 80000000/(40+140+40+800)/(4+16+16+1280) = 60Hz
#define EXAMPLE_MIPI_DSI_DPI_CLK_MHZ 80 #define EXAMPLE_MIPI_DSI_DPI_CLK_MHZ 80
#define EXAMPLE_MIPI_DSI_LCD_H_RES 800 #define EXAMPLE_MIPI_DSI_LCD_H_RES 800
#define EXAMPLE_MIPI_DSI_LCD_V_RES 1280 #define EXAMPLE_MIPI_DSI_LCD_V_RES 1280
@ -40,7 +40,7 @@ static const char *TAG = "example";
#define EXAMPLE_MIPI_DSI_LCD_VBP 16 #define EXAMPLE_MIPI_DSI_LCD_VBP 16
#define EXAMPLE_MIPI_DSI_LCD_VFP 16 #define EXAMPLE_MIPI_DSI_LCD_VFP 16
#elif CONFIG_EXAMPLE_LCD_USE_EK79007 #elif CONFIG_EXAMPLE_LCD_USE_EK79007
// FPS = 48000000/(10+120+120+1024)/(1+20+10+600) = 60Hz // Refresh Rate = 48000000/(10+120+120+1024)/(1+20+10+600) = 60Hz
#define EXAMPLE_MIPI_DSI_DPI_CLK_MHZ 48 #define EXAMPLE_MIPI_DSI_DPI_CLK_MHZ 48
#define EXAMPLE_MIPI_DSI_LCD_H_RES 1024 #define EXAMPLE_MIPI_DSI_LCD_H_RES 1024
#define EXAMPLE_MIPI_DSI_LCD_V_RES 600 #define EXAMPLE_MIPI_DSI_LCD_V_RES 600
@ -67,8 +67,8 @@ static const char *TAG = "example";
#define EXAMPLE_PIN_NUM_BK_LIGHT -1 #define EXAMPLE_PIN_NUM_BK_LIGHT -1
#define EXAMPLE_PIN_NUM_LCD_RST -1 #define EXAMPLE_PIN_NUM_LCD_RST -1
#if CONFIG_EXAMPLE_MONITOR_FPS_BY_GPIO #if CONFIG_EXAMPLE_MONITOR_REFRESH_BY_GPIO
#define EXAMPLE_PIN_NUM_FPS_MONITOR 20 // Monitor the FPS by toggling the GPIO #define EXAMPLE_PIN_NUM_REFRESH_MONITOR 20 // Monitor the Refresh Rate by toggling the GPIO
#endif #endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -110,6 +110,11 @@ static void example_lvgl_port_task(void *arg)
_lock_acquire(&lvgl_api_lock); _lock_acquire(&lvgl_api_lock);
time_till_next_ms = lv_timer_handler(); time_till_next_ms = lv_timer_handler();
_lock_release(&lvgl_api_lock); _lock_release(&lvgl_api_lock);
// in case of task watch dog timeout, set the minimal delay to 10ms
if (time_till_next_ms < 10) {
time_till_next_ms = 10;
}
usleep(1000 * time_till_next_ms); usleep(1000 * time_till_next_ms);
} }
} }
@ -121,12 +126,12 @@ static bool example_notify_lvgl_flush_ready(esp_lcd_panel_handle_t panel, esp_lc
return false; return false;
} }
#if CONFIG_EXAMPLE_MONITOR_FPS_BY_GPIO #if CONFIG_EXAMPLE_MONITOR_REFRESH_BY_GPIO
static bool example_monitor_fps(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) static bool example_monitor_refresh_rate(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
{ {
static int io_level = 0; static int io_level = 0;
// please note, the real FPS should be 2*frequency of this GPIO toggling // please note, the real refresh rate should be 2*frequency of this GPIO toggling
gpio_set_level(EXAMPLE_PIN_NUM_FPS_MONITOR, io_level); gpio_set_level(EXAMPLE_PIN_NUM_REFRESH_MONITOR, io_level);
io_level = !io_level; io_level = !io_level;
return false; return false;
} }
@ -164,12 +169,12 @@ static void example_bsp_set_lcd_backlight(uint32_t level)
#endif #endif
} }
#if CONFIG_EXAMPLE_MONITOR_FPS_BY_GPIO #if CONFIG_EXAMPLE_MONITOR_REFRESH_BY_GPIO
static void example_bsp_init_fps_monitor_io(void) static void example_bsp_init_refresh_monitor_io(void)
{ {
gpio_config_t monitor_io_conf = { gpio_config_t monitor_io_conf = {
.mode = GPIO_MODE_OUTPUT, .mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_FPS_MONITOR, .pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_REFRESH_MONITOR,
}; };
ESP_ERROR_CHECK(gpio_config(&monitor_io_conf)); ESP_ERROR_CHECK(gpio_config(&monitor_io_conf));
} }
@ -177,8 +182,8 @@ static void example_bsp_init_fps_monitor_io(void)
void app_main(void) void app_main(void)
{ {
#if CONFIG_EXAMPLE_MONITOR_FPS_BY_GPIO #if CONFIG_EXAMPLE_MONITOR_REFRESH_BY_GPIO
example_bsp_init_fps_monitor_io(); example_bsp_init_refresh_monitor_io();
#endif #endif
example_bsp_enable_dsi_phy_power(); example_bsp_enable_dsi_phy_power();
@ -291,8 +296,8 @@ void app_main(void)
ESP_LOGI(TAG, "Register DPI panel event callback for LVGL flush ready notification"); ESP_LOGI(TAG, "Register DPI panel event callback for LVGL flush ready notification");
esp_lcd_dpi_panel_event_callbacks_t cbs = { esp_lcd_dpi_panel_event_callbacks_t cbs = {
.on_color_trans_done = example_notify_lvgl_flush_ready, .on_color_trans_done = example_notify_lvgl_flush_ready,
#if CONFIG_EXAMPLE_MONITOR_FPS_BY_GPIO #if CONFIG_EXAMPLE_MONITOR_REFRESH_BY_GPIO
.on_refresh_done = example_monitor_fps, .on_refresh_done = example_monitor_refresh_rate,
#endif #endif
}; };
ESP_ERROR_CHECK(esp_lcd_dpi_panel_register_event_callbacks(mipi_dpi_panel, &cbs, display)); ESP_ERROR_CHECK(esp_lcd_dpi_panel_register_event_callbacks(mipi_dpi_panel, &cbs, display));

View File

@ -27,7 +27,7 @@ This example uses 3 kinds of **buffering mode**:
The connection between ESP Board and the LCD is as follows: The connection between ESP Board and the LCD is as follows:
``` ```text
ESP Board RGB Panel ESP Board RGB Panel
+-----------------------+ +-------------------+ +-----------------------+ +-------------------+
| GND +--------------+GND | | GND +--------------+GND |
@ -51,12 +51,6 @@ The connection between ESP Board and the LCD is as follows:
+-------------------+ +-------------------+
``` ```
The GPIO number used by this example can be changed in [lvgl_example_main.c](main/rgb_lcd_example_main.c).
Especially, please pay attention to the level used to turn on the LCD backlight, some LCD module needs a low level to turn it on, while others take a high level. You can change the backlight level macro `EXAMPLE_LCD_BK_LIGHT_ON_LEVEL` in [lvgl_example_main.c](main/rgb_lcd_example_main.c).
If the RGB LCD panel only supports DE mode, you can even bypass the `HSYNC` and `VSYNC` signals, by assigning `EXAMPLE_PIN_NUM_HSYNC` and `EXAMPLE_PIN_NUM_VSYNC` with `-1`.
### Configure ### Configure
Run `idf.py menuconfig` and go to `Example Configuration`: Run `idf.py menuconfig` and go to `Example Configuration`:
@ -64,6 +58,8 @@ Run `idf.py menuconfig` and go to `Example Configuration`:
1. Choose whether to `Use double Frame Buffer` 1. Choose whether to `Use double Frame Buffer`
2. Choose whether to `Avoid tearing effect` (available only when step `1` was chosen to false) 2. Choose whether to `Avoid tearing effect` (available only when step `1` was chosen to false)
3. Choose whether to `Use bounce buffer` (available only when step `1` was chosen to false) 3. Choose whether to `Use bounce buffer` (available only when step `1` was chosen to false)
4. Choose the number of LCD data lines in `RGB LCD Data Lines`
5. Set the GPIOs used by RGB LCD peripheral in `GPIO assignment`, e.g. the synchronization signals (HSYNC, VSYNC, DE) and the data lines
### Build and Flash ### Build and Flash
@ -79,29 +75,28 @@ See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/l
```bash ```bash
... ...
I (0) cpu_start: Starting scheduler on APP CPU. I (872) main_task: Started on CPU0
I (856) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations I (882) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations
I (856) example: Create semaphores I (882) main_task: Calling app_main()
I (866) example: Turn off LCD backlight I (892) example: Turn off LCD backlight
I (866) gpio: GPIO[4]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 I (892) example: Install RGB LCD panel driver
I (876) example: Install RGB LCD panel driver I (922) example: Initialize RGB LCD panel
I (906) example: Register event callbacks I (922) example: Turn on LCD backlight
I (906) example: Initialize RGB LCD panel I (922) example: Initialize LVGL library
I (906) example: Turn on LCD backlight I (932) example: Allocate LVGL draw buffers
I (906) example: Initialize LVGL library I (932) example: Register event callbacks
I (916) example: Allocate separate LVGL draw buffers from PSRAM I (932) example: Install LVGL tick timer
I (916) example: Register display driver to LVGL I (942) example: Create LVGL task
I (926) example: Install LVGL tick timer I (942) example: Starting LVGL task
I (926) example: Create LVGL task I (992) example: Display LVGL UI
I (926) example: Starting LVGL task I (1102) main_task: Returned from app_main()
I (926) example: Display LVGL Scatter Chart
... ...
``` ```
## Troubleshooting ## Troubleshooting
* Why the LCD doesn't light up? * Why the LCD doesn't light up?
* Check the backlight's turn-on level, and update it in `EXAMPLE_LCD_BK_LIGHT_ON_LEVEL` * Please pay attention to the level used to turn on the LCD backlight, some LCD module needs a low level to turn it on, while others take a high level. You can change the backlight level macro `EXAMPLE_LCD_BK_LIGHT_ON_LEVEL` in [lvgl_example_main.c](main/rgb_lcd_example_main.c).
* No memory for frame buffer * No memory for frame buffer
* The frame buffer of RGB panel is located in ESP side (unlike other controller based LCDs, where the frame buffer is located in external chip). As the frame buffer usually consumes much RAM (depends on the LCD resolution and color depth), we recommend to put the frame buffer into PSRAM (like what we do in this example). However, putting frame buffer in PSRAM will limit the maximum PCLK due to the bandwidth of **SPI0**. * The frame buffer of RGB panel is located in ESP side (unlike other controller based LCDs, where the frame buffer is located in external chip). As the frame buffer usually consumes much RAM (depends on the LCD resolution and color depth), we recommend to put the frame buffer into PSRAM (like what we do in this example). However, putting frame buffer in PSRAM will limit the maximum PCLK due to the bandwidth of **SPI0**.
* LCD screen drift * LCD screen drift

View File

@ -18,4 +18,101 @@ menu "Example Configuration"
help help
Enable this option, the example will use a pair of semaphores to avoid the tearing effect. Enable this option, the example will use a pair of semaphores to avoid the tearing effect.
Note, if the Double Frame Buffer is used, then we can also avoid the tearing effect without the lock. Note, if the Double Frame Buffer is used, then we can also avoid the tearing effect without the lock.
choice EXAMPLE_LCD_DATA_LINES
prompt "RGB LCD Data Lines"
default EXAMPLE_LCD_DATA_LINES_16
help
Select the number of data lines of the RGB LCD.
config EXAMPLE_LCD_DATA_LINES_16
bool "16 data lines"
endchoice
config EXAMPLE_LCD_DATA_LINES
int
default 16 if EXAMPLE_LCD_DATA_LINES_16
menu "GPIO assignment"
config EXAMPLE_LCD_VSYNC_GPIO
int "VSYNC GPIO"
help
GPIO pin number for VSYNC signal.
config EXAMPLE_LCD_HSYNC_GPIO
int "HSYNC GPIO"
help
GPIO pin number for HSYNC signal.
config EXAMPLE_LCD_DE_GPIO
int "DE GPIO"
help
GPIO pin number for DE signal.
config EXAMPLE_LCD_PCLK_GPIO
int "PCLK GPIO"
help
GPIO pin number for PCLK signal.
config EXAMPLE_LCD_DATA0_GPIO
int "DATA0 GPIO"
help
GPIO pin number for data bus[0].
config EXAMPLE_LCD_DATA1_GPIO
int "DATA1 GPIO"
help
GPIO pin number for data bus[1].
config EXAMPLE_LCD_DATA2_GPIO
int "DATA2 GPIO"
help
GPIO pin number for data bus[2].
config EXAMPLE_LCD_DATA3_GPIO
int "DATA3 GPIO"
help
GPIO pin number for data bus[3].
config EXAMPLE_LCD_DATA4_GPIO
int "DATA4 GPIO"
help
GPIO pin number for data bus[4].
config EXAMPLE_LCD_DATA5_GPIO
int "DATA5 GPIO"
help
GPIO pin number for data bus[5].
config EXAMPLE_LCD_DATA6_GPIO
int "DATA6 GPIO"
help
GPIO pin number for data bus[6].
config EXAMPLE_LCD_DATA7_GPIO
int "DATA7 GPIO"
help
GPIO pin number for data bus[7].
config EXAMPLE_LCD_DATA8_GPIO
int "DATA8 GPIO"
help
GPIO pin number for data bus[8].
config EXAMPLE_LCD_DATA9_GPIO
int "DATA9 GPIO"
help
GPIO pin number for data bus[9].
config EXAMPLE_LCD_DATA10_GPIO
int "DATA10 GPIO"
help
GPIO pin number for data bus[10].
config EXAMPLE_LCD_DATA11_GPIO
int "DATA11 GPIO"
help
GPIO pin number for data bus[11].
config EXAMPLE_LCD_DATA12_GPIO
int "DATA12 GPIO"
help
GPIO pin number for data bus[12].
config EXAMPLE_LCD_DATA13_GPIO
int "DATA13 GPIO"
help
GPIO pin number for data bus[13].
config EXAMPLE_LCD_DATA14_GPIO
int "DATA14 GPIO"
help
GPIO pin number for data bus[14].
config EXAMPLE_LCD_DATA15_GPIO
int "DATA15 GPIO"
help
GPIO pin number for data bus[15].
endmenu
endmenu endmenu

View File

@ -1,2 +1,2 @@
dependencies: dependencies:
lvgl/lvgl: "^9.0.0" lvgl/lvgl: "~9.2.0"

View File

@ -4,65 +4,153 @@
* 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/widgets/chart.html#scatter-chart
#include "lvgl.h" #include "lvgl.h"
static void draw_event_cb(lv_event_t *e) static lv_style_t style_bullet;
static lv_obj_t *scale1;
static const lv_font_t *font_normal = &lv_font_montserrat_14;
static lv_obj_t *create_scale_box(lv_obj_t *parent, const char *text1, const char *text2, const char *text3)
{ {
lv_draw_task_t *draw_task = lv_event_get_draw_task(e); lv_obj_t *scale = lv_scale_create(parent);
lv_draw_dsc_base_t *base_dsc = lv_draw_task_get_draw_dsc(draw_task); lv_obj_center(scale);
if (base_dsc->part == LV_PART_INDICATOR) { lv_obj_set_size(scale, 300, 300);
lv_obj_t *obj = lv_event_get_target(e); lv_scale_set_mode(scale, LV_SCALE_MODE_ROUND_OUTER);
lv_chart_series_t *ser = lv_chart_get_series_next(obj, NULL); lv_scale_set_label_show(scale, false);
lv_draw_rect_dsc_t *rect_draw_dsc = lv_draw_task_get_draw_dsc(draw_task); lv_scale_set_post_draw(scale, true);
uint32_t cnt = lv_chart_get_point_count(obj); lv_obj_set_width(scale, LV_PCT(100));
lv_obj_set_style_pad_all(scale, 30, 0);
/*Make older value more transparent*/ lv_obj_t *bullet1 = lv_obj_create(parent);
rect_draw_dsc->bg_opa = (LV_OPA_COVER * base_dsc->id2) / (cnt - 1); lv_obj_set_size(bullet1, 13, 13);
lv_obj_remove_style(bullet1, NULL, LV_PART_SCROLLBAR);
lv_obj_add_style(bullet1, &style_bullet, 0);
lv_obj_set_style_bg_color(bullet1, lv_palette_main(LV_PALETTE_RED), 0);
lv_obj_t *label1 = lv_label_create(parent);
lv_label_set_text(label1, text1);
/*Make smaller values blue, higher values red*/ lv_obj_t *bullet2 = lv_obj_create(parent);
int32_t *x_array = lv_chart_get_x_array(obj, ser); lv_obj_set_size(bullet2, 13, 13);
int32_t *y_array = lv_chart_get_y_array(obj, ser); lv_obj_remove_style(bullet2, NULL, LV_PART_SCROLLBAR);
/*dsc->id is the tells drawing order, but we need the ID of the point being drawn.*/ lv_obj_add_style(bullet2, &style_bullet, 0);
uint32_t start_point = lv_chart_get_x_start_point(obj, ser); lv_obj_set_style_bg_color(bullet2, lv_palette_main(LV_PALETTE_BLUE), 0);
uint32_t p_act = (start_point + base_dsc->id2) % cnt; /*Consider start point to get the index of the array*/ lv_obj_t *label2 = lv_label_create(parent);
lv_opa_t x_opa = (x_array[p_act] * LV_OPA_50) / 200; lv_label_set_text(label2, text2);
lv_opa_t y_opa = (y_array[p_act] * LV_OPA_50) / 1000;
rect_draw_dsc->bg_color = lv_color_mix(lv_palette_main(LV_PALETTE_RED), lv_obj_t *bullet3 = lv_obj_create(parent);
lv_palette_main(LV_PALETTE_BLUE), lv_obj_set_size(bullet3, 13, 13);
x_opa + y_opa); lv_obj_remove_style(bullet3, NULL, LV_PART_SCROLLBAR);
} lv_obj_add_style(bullet3, &style_bullet, 0);
lv_obj_set_style_bg_color(bullet3, lv_palette_main(LV_PALETTE_GREEN), 0);
lv_obj_t *label3 = lv_label_create(parent);
lv_label_set_text(label3, text3);
static int32_t grid_col_dsc[] = {LV_GRID_CONTENT, LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST};
static int32_t grid_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST};
lv_obj_set_grid_dsc_array(parent, grid_col_dsc, grid_row_dsc);
lv_obj_set_grid_cell(scale, LV_GRID_ALIGN_START, 0, 2, LV_GRID_ALIGN_START, 1, 1);
lv_obj_set_grid_cell(bullet1, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_START, 2, 1);
lv_obj_set_grid_cell(bullet2, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_START, 3, 1);
lv_obj_set_grid_cell(bullet3, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_START, 4, 1);
lv_obj_set_grid_cell(label1, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_START, 2, 1);
lv_obj_set_grid_cell(label2, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_START, 3, 1);
lv_obj_set_grid_cell(label3, LV_GRID_ALIGN_STRETCH, 1, 1, LV_GRID_ALIGN_START, 4, 1);
return scale;
} }
static void add_data(lv_timer_t *timer) static void scale1_indic1_anim_cb(void *var, int32_t v)
{ {
lv_obj_t *chart = lv_timer_get_user_data(timer); lv_arc_set_value(var, v);
lv_chart_set_next_value2(chart, lv_chart_get_series_next(chart, NULL), lv_rand(0, 200), lv_rand(0, 1000));
lv_obj_t *card = lv_obj_get_parent(scale1);
lv_obj_t *label = lv_obj_get_child(card, -5);
lv_label_set_text_fmt(label, "Revenue: %"LV_PRId32" %%", v);
}
static void scale1_indic2_anim_cb(void *var, int32_t v)
{
lv_arc_set_value(var, v);
lv_obj_t *card = lv_obj_get_parent(scale1);
lv_obj_t *label = lv_obj_get_child(card, -3);
lv_label_set_text_fmt(label, "Sales: %"LV_PRId32" %%", v);
}
static void scale1_indic3_anim_cb(void *var, int32_t v)
{
lv_arc_set_value(var, v);
lv_obj_t *card = lv_obj_get_parent(scale1);
lv_obj_t *label = lv_obj_get_child(card, -1);
lv_label_set_text_fmt(label, "Costs: %"LV_PRId32" %%", v);
} }
void example_lvgl_demo_ui(lv_display_t *disp) void example_lvgl_demo_ui(lv_display_t *disp)
{ {
lv_obj_t *scr = lv_display_get_screen_active(disp); // init default theme
lv_obj_t *chart = lv_chart_create(scr); lv_theme_default_init(disp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), LV_THEME_DEFAULT_DARK,
lv_obj_set_size(chart, 400, 300); font_normal);
lv_obj_align(chart, LV_ALIGN_CENTER, 0, 0); // bullet style
lv_obj_add_event_cb(chart, draw_event_cb, LV_EVENT_DRAW_TASK_ADDED, NULL); lv_style_init(&style_bullet);
lv_obj_add_flag(chart, LV_OBJ_FLAG_SEND_DRAW_TASK_EVENTS); lv_style_set_border_width(&style_bullet, 0);
lv_obj_set_style_line_width(chart, 0, LV_PART_ITEMS); lv_style_set_radius(&style_bullet, LV_RADIUS_CIRCLE);
lv_chart_set_type(chart, LV_CHART_TYPE_SCATTER); lv_obj_t *parent = lv_display_get_screen_active(disp);
lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_X, 0, 200); // create scale widget
lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, 0, 1000); scale1 = create_scale_box(parent, "Revenue", "Sales", "Costs");
lv_chart_set_point_count(chart, 50); // create arc indicators
lv_obj_t *arc;
arc = lv_arc_create(scale1);
lv_obj_remove_style(arc, NULL, LV_PART_KNOB);
lv_obj_remove_style(arc, NULL, LV_PART_MAIN);
lv_obj_set_size(arc, lv_pct(100), lv_pct(100));
lv_obj_set_style_arc_opa(arc, 0, 0);
lv_obj_set_style_arc_width(arc, 15, LV_PART_INDICATOR);
lv_obj_set_style_arc_color(arc, lv_palette_main(LV_PALETTE_BLUE), LV_PART_INDICATOR);
lv_obj_remove_flag(arc, LV_OBJ_FLAG_CLICKABLE);
lv_chart_series_t *ser = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y); // animation
for (int i = 0; i < 50; i++) { lv_anim_t a;
lv_chart_set_next_value2(chart, ser, lv_rand(0, 200), lv_rand(0, 1000)); lv_anim_init(&a);
} lv_anim_set_values(&a, 20, 100);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_set_exec_cb(&a, scale1_indic1_anim_cb);
lv_anim_set_var(&a, arc);
lv_anim_set_duration(&a, 4100);
lv_anim_set_playback_duration(&a, 2700);
lv_anim_start(&a);
lv_timer_create(add_data, 100, chart); arc = lv_arc_create(scale1);
lv_obj_remove_style(arc, NULL, LV_PART_KNOB);
lv_obj_set_size(arc, lv_pct(100), lv_pct(100));
lv_obj_set_style_margin_all(arc, 20, 0);
lv_obj_set_style_arc_opa(arc, 0, 0);
lv_obj_set_style_arc_width(arc, 15, LV_PART_INDICATOR);
lv_obj_set_style_arc_color(arc, lv_palette_main(LV_PALETTE_RED), LV_PART_INDICATOR);
lv_obj_remove_flag(arc, LV_OBJ_FLAG_CLICKABLE);
lv_obj_center(arc);
lv_anim_set_exec_cb(&a, scale1_indic2_anim_cb);
lv_anim_set_var(&a, arc);
lv_anim_set_duration(&a, 2600);
lv_anim_set_playback_duration(&a, 3200);
lv_anim_start(&a);
arc = lv_arc_create(scale1);
lv_obj_remove_style(arc, NULL, LV_PART_KNOB);
lv_obj_set_size(arc, lv_pct(100), lv_pct(100));
lv_obj_set_style_margin_all(arc, 40, 0);
lv_obj_set_style_arc_opa(arc, 0, 0);
lv_obj_set_style_arc_width(arc, 15, LV_PART_INDICATOR);
lv_obj_set_style_arc_color(arc, lv_palette_main(LV_PALETTE_GREEN), LV_PART_INDICATOR);
lv_obj_remove_flag(arc, LV_OBJ_FLAG_CLICKABLE);
lv_obj_center(arc);
lv_anim_set_exec_cb(&a, scale1_indic3_anim_cb);
lv_anim_set_var(&a, arc);
lv_anim_set_duration(&a, 2800);
lv_anim_set_playback_duration(&a, 1800);
lv_anim_start(&a);
} }

View File

@ -24,7 +24,7 @@ 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 // Refresh Rate = 18000000/(1+40+20+800)/(1+10+5+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_H_RES 800
#define EXAMPLE_LCD_V_RES 480 #define EXAMPLE_LCD_V_RES 480
@ -32,47 +32,55 @@ static const char *TAG = "example";
#define EXAMPLE_LCD_HBP 40 #define EXAMPLE_LCD_HBP 40
#define EXAMPLE_LCD_HFP 20 #define EXAMPLE_LCD_HFP 20
#define EXAMPLE_LCD_VSYNC 1 #define EXAMPLE_LCD_VSYNC 1
#define EXAMPLE_LCD_VBP 8 #define EXAMPLE_LCD_VBP 10
#define EXAMPLE_LCD_VFP 4 #define EXAMPLE_LCD_VFP 5
#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 -1
#define EXAMPLE_PIN_NUM_HSYNC 46
#define EXAMPLE_PIN_NUM_VSYNC 3
#define EXAMPLE_PIN_NUM_DE 0
#define EXAMPLE_PIN_NUM_PCLK 9
#define EXAMPLE_PIN_NUM_DATA0 14 // B0
#define EXAMPLE_PIN_NUM_DATA1 13 // B1
#define EXAMPLE_PIN_NUM_DATA2 12 // B2
#define EXAMPLE_PIN_NUM_DATA3 11 // B3
#define EXAMPLE_PIN_NUM_DATA4 10 // B4
#define EXAMPLE_PIN_NUM_DATA5 39 // G0
#define EXAMPLE_PIN_NUM_DATA6 38 // G1
#define EXAMPLE_PIN_NUM_DATA7 45 // G2
#define EXAMPLE_PIN_NUM_DATA8 48 // G3
#define EXAMPLE_PIN_NUM_DATA9 47 // G4
#define EXAMPLE_PIN_NUM_DATA10 21 // G5
#define EXAMPLE_PIN_NUM_DATA11 1 // R0
#define EXAMPLE_PIN_NUM_DATA12 2 // R1
#define EXAMPLE_PIN_NUM_DATA13 42 // R2
#define EXAMPLE_PIN_NUM_DATA14 41 // R3
#define EXAMPLE_PIN_NUM_DATA15 40 // R4
#define EXAMPLE_PIN_NUM_DISP_EN -1 #define EXAMPLE_PIN_NUM_DISP_EN -1
#define EXAMPLE_PIN_NUM_HSYNC CONFIG_EXAMPLE_LCD_HSYNC_GPIO
#define EXAMPLE_PIN_NUM_VSYNC CONFIG_EXAMPLE_LCD_VSYNC_GPIO
#define EXAMPLE_PIN_NUM_DE CONFIG_EXAMPLE_LCD_DE_GPIO
#define EXAMPLE_PIN_NUM_PCLK CONFIG_EXAMPLE_LCD_PCLK_GPIO
#define EXAMPLE_PIN_NUM_DATA0 CONFIG_EXAMPLE_LCD_DATA0_GPIO
#define EXAMPLE_PIN_NUM_DATA1 CONFIG_EXAMPLE_LCD_DATA1_GPIO
#define EXAMPLE_PIN_NUM_DATA2 CONFIG_EXAMPLE_LCD_DATA2_GPIO
#define EXAMPLE_PIN_NUM_DATA3 CONFIG_EXAMPLE_LCD_DATA3_GPIO
#define EXAMPLE_PIN_NUM_DATA4 CONFIG_EXAMPLE_LCD_DATA4_GPIO
#define EXAMPLE_PIN_NUM_DATA5 CONFIG_EXAMPLE_LCD_DATA5_GPIO
#define EXAMPLE_PIN_NUM_DATA6 CONFIG_EXAMPLE_LCD_DATA6_GPIO
#define EXAMPLE_PIN_NUM_DATA7 CONFIG_EXAMPLE_LCD_DATA7_GPIO
#define EXAMPLE_PIN_NUM_DATA8 CONFIG_EXAMPLE_LCD_DATA8_GPIO
#define EXAMPLE_PIN_NUM_DATA9 CONFIG_EXAMPLE_LCD_DATA9_GPIO
#define EXAMPLE_PIN_NUM_DATA10 CONFIG_EXAMPLE_LCD_DATA10_GPIO
#define EXAMPLE_PIN_NUM_DATA11 CONFIG_EXAMPLE_LCD_DATA11_GPIO
#define EXAMPLE_PIN_NUM_DATA12 CONFIG_EXAMPLE_LCD_DATA12_GPIO
#define EXAMPLE_PIN_NUM_DATA13 CONFIG_EXAMPLE_LCD_DATA13_GPIO
#define EXAMPLE_PIN_NUM_DATA14 CONFIG_EXAMPLE_LCD_DATA14_GPIO
#define EXAMPLE_PIN_NUM_DATA15 CONFIG_EXAMPLE_LCD_DATA15_GPIO
#if CONFIG_EXAMPLE_DOUBLE_FB #if CONFIG_EXAMPLE_DOUBLE_FB
#define EXAMPLE_LCD_NUM_FB 2 #define EXAMPLE_LCD_NUM_FB 2
#else #else
#define EXAMPLE_LCD_NUM_FB 1 #define EXAMPLE_LCD_NUM_FB 1
#endif // CONFIG_EXAMPLE_DOUBLE_FB #endif // CONFIG_EXAMPLE_DOUBLE_FB
#if CONFIG_EXAMPLE_LCD_DATA_LINES_16
#define EXAMPLE_DATA_BUS_WIDTH 16
#define EXAMPLE_PIXEL_SIZE 2
#define EXAMPLE_LV_COLOR_FORMAT LV_COLOR_FORMAT_RGB565
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your Application /////////////////////////// //////////////////// 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_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_STACK_SIZE (4 * 1024) #define EXAMPLE_LVGL_TASK_STACK_SIZE (5 * 1024)
#define EXAMPLE_LVGL_TASK_PRIORITY 2 #define EXAMPLE_LVGL_TASK_PRIORITY 2
// LVGL library is not thread-safe, this example will call LVGL APIs from different tasks, so use a mutex to protect it // LVGL library is not thread-safe, this example will call LVGL APIs from different tasks, so use a mutex to protect it
@ -127,6 +135,12 @@ static void example_lvgl_port_task(void *arg)
_lock_acquire(&lvgl_api_lock); _lock_acquire(&lvgl_api_lock);
time_till_next_ms = lv_timer_handler(); time_till_next_ms = lv_timer_handler();
_lock_release(&lvgl_api_lock); _lock_release(&lvgl_api_lock);
// in case of task watch dog timeout, set the minimal delay to 10ms
if (time_till_next_ms < 10) {
time_till_next_ms = 10;
}
usleep(1000 * time_till_next_ms); usleep(1000 * time_till_next_ms);
} }
} }
@ -166,7 +180,7 @@ void app_main(void)
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;
esp_lcd_rgb_panel_config_t panel_config = { esp_lcd_rgb_panel_config_t panel_config = {
.data_width = 16, // RGB565 in parallel mode, thus 16bit in width .data_width = EXAMPLE_DATA_BUS_WIDTH,
.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
@ -228,7 +242,7 @@ void app_main(void)
// associate the rgb panel handle to the display // associate the rgb panel handle to the display
lv_display_set_user_data(display, panel_handle); lv_display_set_user_data(display, panel_handle);
// set color depth // set color depth
lv_display_set_color_format(display, LV_COLOR_FORMAT_RGB565); lv_display_set_color_format(display, EXAMPLE_LV_COLOR_FORMAT);
// create draw buffers // create draw buffers
void *buf1 = NULL; void *buf1 = NULL;
void *buf2 = NULL; void *buf2 = NULL;
@ -236,11 +250,11 @@ void app_main(void)
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));
// set LVGL draw buffers and direct mode // set LVGL draw buffers and direct mode
lv_display_set_buffers(display, buf1, buf2, EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES * sizeof(lv_color16_t), LV_DISPLAY_RENDER_MODE_DIRECT); lv_display_set_buffers(display, buf1, buf2, EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES * EXAMPLE_PIXEL_SIZE, LV_DISPLAY_RENDER_MODE_DIRECT);
#else #else
ESP_LOGI(TAG, "Allocate LVGL draw buffers"); ESP_LOGI(TAG, "Allocate LVGL draw buffers");
// it's recommended to allocate the draw buffer from internal memory, for better performance // 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); size_t draw_buffer_sz = EXAMPLE_LCD_H_RES * EXAMPLE_LVGL_DRAW_BUF_LINES * EXAMPLE_PIXEL_SIZE;
buf1 = heap_caps_malloc(draw_buffer_sz, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); buf1 = heap_caps_malloc(draw_buffer_sz, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
assert(buf1); assert(buf1);
// set LVGL draw buffers and partial mode // set LVGL draw buffers and partial mode
@ -269,7 +283,7 @@ void app_main(void)
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 UI");
// Lock the mutex due to the LVGL APIs are not thread-safe // Lock the mutex due to the LVGL APIs are not thread-safe
_lock_acquire(&lvgl_api_lock); _lock_acquire(&lvgl_api_lock);
example_lvgl_demo_ui(display); example_lvgl_demo_ui(display);

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD # SPDX-FileCopyrightText: 2021-2024 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
@ -22,4 +22,5 @@ def test_rgb_lcd_lvgl(dut: Dut) -> None:
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: Install LVGL tick timer') dut.expect_exact('example: Install LVGL tick timer')
dut.expect_exact('example: Display LVGL Scatter Chart') dut.expect_exact('example: Create LVGL task')
dut.expect_exact('example: Display LVGL UI')

View File

@ -2,6 +2,38 @@ CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y CONFIG_SPIRAM_SPEED_80M=y
# LCD_CAM support 16 data lines at most
CONFIG_EXAMPLE_LCD_DATA_LINES_16=y
CONFIG_LV_COLOR_DEPTH_16=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_XIP_FROM_PSRAM=y CONFIG_SPIRAM_XIP_FROM_PSRAM=y
# Default GPIO assignment
CONFIG_EXAMPLE_LCD_VSYNC_GPIO=3
CONFIG_EXAMPLE_LCD_HSYNC_GPIO=46
CONFIG_EXAMPLE_LCD_DE_GPIO=0
CONFIG_EXAMPLE_LCD_PCLK_GPIO=9
# B3:B7 <=> DATA0:DATA4
CONFIG_EXAMPLE_LCD_DATA0_GPIO=14
CONFIG_EXAMPLE_LCD_DATA1_GPIO=13
CONFIG_EXAMPLE_LCD_DATA2_GPIO=12
CONFIG_EXAMPLE_LCD_DATA3_GPIO=11
CONFIG_EXAMPLE_LCD_DATA4_GPIO=10
# G2:G7 <=> DATA5:DATA10
CONFIG_EXAMPLE_LCD_DATA5_GPIO=39
CONFIG_EXAMPLE_LCD_DATA6_GPIO=38
CONFIG_EXAMPLE_LCD_DATA7_GPIO=45
CONFIG_EXAMPLE_LCD_DATA8_GPIO=48
CONFIG_EXAMPLE_LCD_DATA9_GPIO=47
CONFIG_EXAMPLE_LCD_DATA10_GPIO=21
# R3:R7 <=> DATA11:DATA15
CONFIG_EXAMPLE_LCD_DATA11_GPIO=1
CONFIG_EXAMPLE_LCD_DATA12_GPIO=2
CONFIG_EXAMPLE_LCD_DATA13_GPIO=42
CONFIG_EXAMPLE_LCD_DATA14_GPIO=41
CONFIG_EXAMPLE_LCD_DATA15_GPIO=40