rgb_lcd: update example to support double buffer and bounce buffer

pull/9408/head
morris 2022-07-06 16:31:31 +08:00
rodzic a33a183365
commit 28c512b34c
8 zmienionych plików z 191 dodań i 47 usunięć

Wyświetl plik

@ -1,16 +1,22 @@
| Supported Targets | ESP32-S3 |
| ----------------- | -------- |
# RGB 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 a frame buffer managed by the driver itself.
# RGB LCD Panel Example
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. For more information about porting the LVGL library, please refer to [another lvgl porting example](../i80_controller/README.md).
[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.
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. For more information about porting the LVGL library, please refer to [official porting guide](https://docs.lvgl.io/master/porting/index.html). 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. |
## How to use the example
### Hardware Required
* An ESP development board, which has RGB LCD peripheral supported and PSRAM onboard
* An ESP development board, which has RGB LCD peripheral supported and **Octal PSRAM** onboard
* A general RGB panel, 16 bit-width, with HSYNC, VSYNC and DE signal
* An USB cable for power supply and programming
@ -46,7 +52,15 @@ The GPIO number used by this example can be changed in [lvgl_example_main.c](mai
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 bypass the `HSYNC` and `VSYNC` signals, by assigning `EXAMPLE_PIN_NUM_HSYNC` and `EXAMPLE_PIN_NUM_VSYNC` with `-1`.
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
Run `idf.py menuconfig` and go to `Example Configuration`:
1. Choose whether to `Use double Frame Buffer`
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)
### Build and Flash
@ -63,15 +77,19 @@ See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/l
```bash
...
I (0) cpu_start: Starting scheduler on APP CPU.
I (731) spiram: Reserving pool of 32K of internal memory for DMA/internal allocations
I (731) example: Turn off LCD backlight
I (731) gpio: GPIO[39]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (731) example: Install RGB panel driver
I (741) example: Turn on LCD backlight
I (741) example: Initialize LVGL library
I (741) example: Register display driver to LVGL
I (741) example: Install LVGL tick timer
I (741) example: Display LVGL Scatter Chart
I (856) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations
I (856) example: Create semaphores
I (866) example: Turn off LCD backlight
I (866) gpio: GPIO[4]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (876) example: Install RGB LCD panel driver
I (906) example: Register event callbacks
I (906) example: Initialize RGB LCD panel
I (906) example: Turn on LCD backlight
I (906) example: Initialize LVGL library
I (916) example: Allocate separate LVGL draw buffers from PSRAM
I (916) example: Register display driver to LVGL
I (926) example: Install LVGL tick timer
I (926) example: Display LVGL Scatter Chart
...
```
@ -80,9 +98,16 @@ I (741) example: Display LVGL Scatter Chart
* Why the LCD doesn't light up?
* Check the backlight's turn-on level, and update it in `EXAMPLE_LCD_BK_LIGHT_ON_LEVEL`
* 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 PCLK to around 12MHz (due to the bandwidth of PSRAM).
* 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
* Slow down the PCLK frequency
* Adjust other timing parameters like PCLK clock edge (by `pclk_active_neg`), sync porches like HBP (by `hsync_back_porch`) according to your LCD spec
* Adjust other timing parameters like PCLK clock edge (by `pclk_active_neg`), sync porches like VBP (by `vsync_back_porch`) according to your LCD spec
* Enable `CONFIG_SPIRAM_FETCH_INSTRUCTIONS` and `CONFIG_SPIRAM_RODATA`, which can saves some bandwidth of SPI0 from being consumed by ICache.
* LCD screen tear effect
* Using double frame buffers
* 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.
For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

Wyświetl plik

@ -0,0 +1,21 @@
menu "Example Configuration"
config EXAMPLE_DOUBLE_FB
bool "Use double Frame Buffer"
default "n"
help
Enable this option, driver will allocate two frame buffers.
config EXAMPLE_USE_BOUNCE_BUFFER
depends on !EXAMPLE_DOUBLE_FB
bool "Use bounce buffer"
help
Enable bounce buffer mode can achieve higher PCLK frequency at the cost of higher CPU consumption.
config EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM
depends on !EXAMPLE_DOUBLE_FB
bool "Avoid tearing effect"
default "y"
help
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.
endmenu

Wyświetl plik

@ -5,8 +5,10 @@
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_rgb.h"
@ -20,40 +22,57 @@ static const char *TAG = "example";
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (10 * 1000 * 1000)
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (18 * 1000 * 1000)
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 1
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_BK_LIGHT 39
#define EXAMPLE_PIN_NUM_HSYNC 47
#define EXAMPLE_PIN_NUM_VSYNC 48
#define EXAMPLE_PIN_NUM_DE 45
#define EXAMPLE_PIN_NUM_PCLK 21
#define EXAMPLE_PIN_NUM_DATA0 3 // B0
#define EXAMPLE_PIN_NUM_DATA1 4 // B1
#define EXAMPLE_PIN_NUM_DATA2 5 // B2
#define EXAMPLE_PIN_NUM_DATA3 6 // B3
#define EXAMPLE_PIN_NUM_DATA4 7 // B4
#define EXAMPLE_PIN_NUM_DATA5 8 // G0
#define EXAMPLE_PIN_NUM_DATA6 9 // G1
#define EXAMPLE_PIN_NUM_DATA7 10 // G2
#define EXAMPLE_PIN_NUM_DATA8 11 // G3
#define EXAMPLE_PIN_NUM_DATA9 12 // G4
#define EXAMPLE_PIN_NUM_DATA10 13 // G5
#define EXAMPLE_PIN_NUM_DATA11 14 // R0
#define EXAMPLE_PIN_NUM_DATA12 15 // R1
#define EXAMPLE_PIN_NUM_DATA13 16 // R2
#define EXAMPLE_PIN_NUM_DATA14 17 // R3
#define EXAMPLE_PIN_NUM_DATA15 18 // R4
#define EXAMPLE_PIN_NUM_BK_LIGHT 4
#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
// The pixel number in horizontal and vertical
#define EXAMPLE_LCD_H_RES 480
#define EXAMPLE_LCD_V_RES 272
#define EXAMPLE_LCD_H_RES 800
#define EXAMPLE_LCD_V_RES 480
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
// 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
SemaphoreHandle_t sem_vsync_end;
SemaphoreHandle_t sem_gui_ready;
#endif
extern void example_lvgl_demo_ui(lv_obj_t *scr);
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)
{
BaseType_t high_task_awoken = pdFALSE;
#if CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM
if (xSemaphoreTakeFromISR(sem_gui_ready, &high_task_awoken) == pdTRUE) {
xSemaphoreGiveFromISR(sem_vsync_end, &high_task_awoken);
}
#endif
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)
{
esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;
@ -61,7 +80,11 @@ static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_
int offsetx2 = area->x2;
int offsety1 = area->y1;
int offsety2 = area->y2;
// copy a buffer's content to a specific area of the display
#if CONFIG_EXAMPLE_AVOID_TEAR_EFFECT_WITH_SEM
xSemaphoreGive(sem_gui_ready);
xSemaphoreTake(sem_vsync_end, portMAX_DELAY);
#endif
// pass the draw buffer to the driver
esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
lv_disp_flush_ready(drv);
}
@ -77,18 +100,31 @@ 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
ESP_LOGI(TAG, "Create semaphores");
sem_vsync_end = xSemaphoreCreateBinary();
assert(sem_vsync_end);
sem_gui_ready = xSemaphoreCreateBinary();
assert(sem_gui_ready);
#endif
#if EXAMPLE_PIN_NUM_BK_LIGHT >= 0
ESP_LOGI(TAG, "Turn off LCD backlight");
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
ESP_LOGI(TAG, "Install RGB panel driver");
ESP_LOGI(TAG, "Install RGB LCD panel driver");
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_rgb_panel_config_t panel_config = {
.data_width = 16, // RGB565 in parallel mode, thus 16bit in width
.psram_trans_align = 64,
#if CONFIG_EXAMPLE_USE_BOUNCE_BUFFER
.bounce_buffer_size_px = 10 * EXAMPLE_LCD_H_RES,
#endif
.clk_src = LCD_CLK_SRC_DEFAULT,
.disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN,
.pclk_gpio_num = EXAMPLE_PIN_NUM_PCLK,
@ -126,25 +162,46 @@ void app_main(void)
.vsync_pulse_width = 1,
.flags.pclk_active_neg = true,
},
.flags.fb_in_psram = 1, // allocate frame buffer in PSRAM
.flags.fb_in_psram = true, // allocate frame buffer in PSRAM
#if CONFIG_EXAMPLE_DOUBLE_FB
.flags.double_fb = true, // allocate double frame buffer
#endif // CONFIG_EXAMPLE_DOUBLE_FB
};
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_ERROR_CHECK(esp_lcd_panel_reset(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");
gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL);
#endif
ESP_LOGI(TAG, "Initialize LVGL library");
lv_init();
// alloc draw buffers used by LVGL from PSRAM
lv_color_t *buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
void *buf1 = NULL;
void *buf2 = NULL;
#if CONFIG_EXAMPLE_DOUBLE_FB
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));
// initialize LVGL draw buffers
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES);
#else
ESP_LOGI(TAG, "Allocate separate LVGL draw buffers from PSRAM");
buf1 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
assert(buf1);
lv_color_t *buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
buf2 = heap_caps_malloc(EXAMPLE_LCD_H_RES * 100 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM);
assert(buf2);
// initialize LVGL draw buffers
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 100);
#endif // CONFIG_EXAMPLE_DOUBLE_FB
ESP_LOGI(TAG, "Register display driver to LVGL");
lv_disp_drv_init(&disp_drv);
@ -153,6 +210,9 @@ void app_main(void)
disp_drv.flush_cb = example_lvgl_flush_cb;
disp_drv.draw_buf = &disp_buf;
disp_drv.user_data = panel_handle;
#if CONFIG_EXAMPLE_DOUBLE_FB
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");

Wyświetl plik

@ -0,0 +1,28 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.esp32s3
@pytest.mark.octal_psram
@pytest.mark.parametrize(
'config',
[
'single_fb_with_bb',
'single_fb_no_bb',
'double_fb',
],
indirect=True,
)
def test_rgb_lcd_lvgl(dut: Dut) -> None:
dut.expect_exact('example: Turn off LCD backlight')
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: Turn on LCD backlight')
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: Display LVGL Scatter Chart')

Wyświetl plik

@ -0,0 +1 @@
CONFIG_EXAMPLE_DOUBLE_FB=y

Wyświetl plik

@ -0,0 +1,2 @@
CONFIG_EXAMPLE_DOUBLE_FB=n
CONFIG_EXAMPLE_USE_BOUNCE_BUFFER=n

Wyświetl plik

@ -0,0 +1,2 @@
CONFIG_EXAMPLE_DOUBLE_FB=n
CONFIG_EXAMPLE_USE_BOUNCE_BUFFER=y

Wyświetl plik

@ -1,3 +1,8 @@
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
# 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
CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y
CONFIG_SPIRAM_RODATA=y