Merge branch 'doc/esp_lcd_class_diagram' into 'master'

RGB LCD multi-framebuffer support

Closes IDF-5309, IDF-5939, and IDF-6336

See merge request espressif/esp-idf!20975
This commit is contained in:
morris 2022-12-07 11:25:15 +08:00
commit 171b849831
12 changed files with 392 additions and 76 deletions

View File

@ -14,7 +14,7 @@ if(CONFIG_SOC_I2S_LCD_I80_VARIANT)
endif()
if(CONFIG_SOC_LCDCAM_SUPPORTED)
list(APPEND srcs "src/esp_lcd_panel_io_i80.c" "src/esp_lcd_rgb_panel.c")
list(APPEND srcs "src/esp_lcd_panel_io_i80.c" "src/esp_lcd_panel_rgb.c")
endif()
idf_component_register(SRCS ${srcs}

View File

@ -0,0 +1,83 @@
# esp_lcd Driver Design
## Class Diagram
`esp_lcd` driver focuses on two parts: panel driver and IO driver. The panel driver is a bunch of operations on the **frame-buffer**, no matter where the frame-buffer is located. The IO driver is mainly consumed by the controller-based LCD panel drivers (e.g. ST7789). Usually such LCD controller can support various IO interfaces (e.g. I80, SPI, I2C, etc). So we define an abstract interface for the IO driver.
```mermaid
classDiagram
class esp_lcd_panel_t {
<<interface>>
+reset() esp_err_t
+init() esp_err_t
+draw_bitmap(int x_start, int y_start, int x_end, int y_end, const void *color_data) esp_err_t
+mirror(bool x_axis, bool y_axis) esp_err_t
+swap_xy(bool swap_axes) esp_err_t
+set_gap(int x_gap, int y_gap) esp_err_t
+invert_color(bool invert_color_data) esp_err_t
+disp_on_off(bool on_off) esp_err_t
}
esp_lcd_rgb_panel_t --|> esp_lcd_panel_t : Inheritance
class esp_lcd_rgb_panel_t {
-int panel_id
-size_t data_width
-int disp_gpio
-intr_handle_t intr
-uint8_t* frame_buffer
-gdma_channel_handle_t gdma_channel
-dma_descriptor_t* dma_nodes
-on_vsync(void* user_data) bool
}
esp_lcd_panel_model_t --|> esp_lcd_panel_t : Inheritance
esp_lcd_panel_model_t "1" --> "1" esp_lcd_panel_io_t : Use
class esp_lcd_panel_model_t {
-esp_lcd_panel_io_t* io
-int reset_gpio_num
}
class esp_lcd_panel_io_t {
<<interface>>
+rx_param(int lcd_cmd, void *param, size_t param_size)
+tx_param(int lcd_cmd, const void *param, size_t param_size)
+tx_color(int lcd_cmd, const void *color, size_t color_size)
}
esp_lcd_panel_io_i2c_t --|> esp_lcd_panel_io_t : Inheritance
class esp_lcd_panel_io_i2c_t {
-int i2c_bus_id
-int ctrl_phase_cmd
-int ctrl_phase_data
-on_color_trans_done(void* user_data) bool
}
esp_lcd_panel_io_spi_t --|> esp_lcd_panel_io_t : Inheritance
class esp_lcd_panel_io_spi_t {
-spi_device_handle_t spi_dev
-int dc_gpio_num
-spi_transaction_t trans_worker
-on_color_trans_done(void* user_data) bool
}
esp_lcd_panel_io_i80_t --|> esp_lcd_panel_io_t : Inheritance
class esp_lcd_panel_io_i80_t {
-esp_lcd_i80_bus_t* bus
-int cs_gpio_num
-int dc_level
-size_t pclk_hz
-QueueHandle_t trans_queue
-QueueHandle_t done_queue
-on_color_trans_done(void* user_data) bool
}
esp_lcd_i80_bus_t "1" --> "1..*" esp_lcd_panel_io_i80_t : Has
class esp_lcd_i80_bus_t {
-int bus_id
-size_t data_width
-intr_handle_t intr
-gdma_cannel_handle_t dma_chan
-dma_descriptor_t* dma_nodes
-list_t i80_devices
}
```

View File

@ -118,6 +118,7 @@ typedef struct {
size_t data_width; /*!< Number of data lines */
size_t bits_per_pixel; /*!< Frame buffer color depth, in bpp, specially, if set to zero, it will default to `data_width`.
When using a Serial RGB interface, this value could be different from `data_width` */
size_t num_fbs; /*!< Number of screen-sized frame buffers that allocated by the driver. By default (set to either 0 or 1) only one frame buffer will be used. Maximum number of buffers are 3 */
size_t bounce_buffer_size_px; /*!< If it's non-zero, the driver allocates two DRAM bounce buffers for DMA use.
DMA fetching from DRAM bounce buffer is much faster than PSRAM frame buffer. */
size_t sram_trans_align; /*!< Alignment of buffers (frame buffer or bounce buffer) that allocated in SRAM */
@ -133,7 +134,7 @@ typedef struct {
uint32_t refresh_on_demand: 1; /*!< If this flag is enabled, the host only refresh the frame buffer when `esp_lcd_panel_draw_bitmap` is called.
This is useful when the LCD screen has a GRAM and can refresh the LCD by itself. */
uint32_t fb_in_psram: 1; /*!< If this flag is enabled, the frame buffer will be allocated from PSRAM, preferentially */
uint32_t double_fb: 1; /*!< If this flag is enabled, the driver will allocate two screen sized frame buffer */
uint32_t double_fb: 1; /*!< If this flag is enabled, the driver will allocate two screen sized frame buffer, same as num_fbs=2 */
uint32_t no_fb: 1; /*!< If this flag is enabled, the driver won't allocate frame buffer.
Instead, user should fill in the bounce buffer manually in the `on_bounce_empty` callback */
uint32_t bb_invalidate_cache: 1; /*!< If this flag is enabled, in bounce back mode we'll do a cache invalidate on the read data, freeing the cache.

View File

@ -49,6 +49,10 @@
#define LCD_RGB_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED
#endif
#define RGB_LCD_PANEL_MAX_FB_NUM 3 // maximum supported frame buffer number
#define RGB_LCD_PANEL_BOUNCE_BUF_NUM 2 // bounce buffer number
#define RGB_LCD_PANEL_DMA_LINKS_REPLICA MAX(RGB_LCD_PANEL_MAX_FB_NUM, RGB_LCD_PANEL_BOUNCE_BUF_NUM)
#define RGB_PANEL_SWAP_XY 0
#define RGB_PANEL_MIRROR_Y 1
#define RGB_PANEL_MIRROR_X 2
@ -84,6 +88,7 @@ struct esp_rgb_panel_t {
lcd_hal_context_t hal; // Hal layer object
size_t data_width; // Number of data lines
size_t fb_bits_per_pixel; // Frame buffer color depth, in bpp
size_t num_fbs; // Number of frame buffers
size_t output_bits_per_pixel; // Color depth seen from the output data line. Default to fb_bits_per_pixel, but can be changed by YUV-RGB conversion
size_t sram_trans_align; // Alignment for framebuffer that allocated in SRAM
size_t psram_trans_align; // Alignment for framebuffer that allocated in PSRAM
@ -91,7 +96,7 @@ struct esp_rgb_panel_t {
intr_handle_t intr; // LCD peripheral interrupt handle
esp_pm_lock_handle_t pm_lock; // Power management lock
size_t num_dma_nodes; // Number of DMA descriptors that used to carry the frame buffer
uint8_t *fbs[2]; // Frame buffers
uint8_t *fbs[RGB_LCD_PANEL_MAX_FB_NUM]; // Frame buffers
uint8_t cur_fb_index; // Current frame buffer index (0 or 1)
size_t fb_size; // Size of frame buffer
int data_gpio_nums[SOC_LCD_RGB_DATA_WIDTH]; // GPIOs used for data lines, we keep these GPIOs for action like "invert_color"
@ -99,7 +104,7 @@ struct esp_rgb_panel_t {
esp_lcd_rgb_timing_t timings; // RGB timing parameters (e.g. pclk, sync pulse, porch width)
size_t bb_size; // If not-zero, the driver uses two bounce buffers allocated from internal memory
int bounce_pos_px; // Position in whatever source material is used for the bounce buffer, in pixels
uint8_t *bounce_buffer[2]; // Pointer to the bounce buffers
uint8_t *bounce_buffer[RGB_LCD_PANEL_BOUNCE_BUF_NUM]; // Pointer to the bounce buffers
gdma_channel_handle_t dma_chan; // DMA channel handle
esp_lcd_rgb_panel_vsync_cb_t on_vsync; // VSYNC event callback
esp_lcd_rgb_panel_bounce_buf_fill_cb_t on_bounce_empty; // callback used to fill a bounce buffer rather than copying from the frame buffer
@ -112,13 +117,12 @@ struct esp_rgb_panel_t {
struct {
uint32_t disp_en_level: 1; // The level which can turn on the screen by `disp_gpio_num`
uint32_t stream_mode: 1; // If set, the LCD transfers data continuously, otherwise, it stops refreshing the LCD when transaction done
uint32_t no_fb: 1; // No frame buffer allocated in the driver
uint32_t fb_in_psram: 1; // Whether the frame buffer is in PSRAM
uint32_t need_update_pclk: 1; // Whether to update the PCLK before start a new transaction
uint32_t need_restart: 1; // Whether to restart the LCD controller and the DMA
uint32_t bb_invalidate_cache: 1; // Whether to do cache invalidation in bounce buffer mode
} flags;
dma_descriptor_t *dma_links[2]; // fbs[0] <-> dma_links[0], fbs[1] <-> dma_links[1]
dma_descriptor_t *dma_links[RGB_LCD_PANEL_DMA_LINKS_REPLICA]; // fbs[0] <-> dma_links[0], fbs[1] <-> dma_links[1], etc
dma_descriptor_t dma_restart_node; // DMA descriptor used to restart the transfer
dma_descriptor_t dma_nodes[]; // DMA descriptors pool
};
@ -132,7 +136,7 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(const esp_lcd_rgb_panel_confi
rgb_panel->sram_trans_align = sram_trans_align;
// alloc frame buffer
if (!rgb_panel_config->flags.no_fb) {
if (rgb_panel->num_fbs > 0) {
// fb_in_psram is only an option, if there's no PSRAM on board, we fallback to alloc from SRAM
if (rgb_panel_config->flags.fb_in_psram) {
#if CONFIG_SPIRAM_USE_MALLOC || CONFIG_SPIRAM_USE_CAPS_ALLOC
@ -141,7 +145,7 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(const esp_lcd_rgb_panel_confi
}
#endif
}
for (int i = 0; i < (rgb_panel_config->flags.double_fb ? 2 : 1); i++) {
for (int i = 0; i < rgb_panel->num_fbs; i++) {
if (fb_in_psram) {
// the low level malloc function will help check the validation of alignment
rgb_panel->fbs[i] = heap_caps_aligned_calloc(psram_trans_align, 1, rgb_panel->fb_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
@ -154,7 +158,7 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(const esp_lcd_rgb_panel_confi
// alloc bounce buffer
if (rgb_panel->bb_size) {
for (int i = 0; i < 2; i++) {
for (int i = 0; i < RGB_LCD_PANEL_BOUNCE_BUF_NUM; i++) {
// bounce buffer must come from SRAM
rgb_panel->bounce_buffer[i] = heap_caps_aligned_calloc(sram_trans_align, 1, rgb_panel->bb_size, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
ESP_RETURN_ON_FALSE(rgb_panel->bounce_buffer[i], ESP_ERR_NO_MEM, TAG, "no mem for bounce buffer");
@ -173,11 +177,10 @@ static esp_err_t lcd_rgb_panel_destory(esp_rgb_panel_t *rgb_panel)
periph_module_disable(lcd_periph_signals.panels[rgb_panel->panel_id].module);
lcd_com_remove_device(LCD_COM_DEVICE_TYPE_RGB, rgb_panel->panel_id);
}
if (rgb_panel->fbs[0]) {
free(rgb_panel->fbs[0]);
}
if (rgb_panel->fbs[1]) {
free(rgb_panel->fbs[1]);
for (size_t i = 0; i < rgb_panel->num_fbs; i++) {
if (rgb_panel->fbs[i]) {
free(rgb_panel->fbs[i]);
}
}
if (rgb_panel->bounce_buffer[0]) {
free(rgb_panel->bounce_buffer[0]);
@ -211,7 +214,11 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
ESP_GOTO_ON_FALSE(rgb_panel_config->data_width == 16 || rgb_panel_config->data_width == 8,
ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported data width %d", rgb_panel_config->data_width);
ESP_GOTO_ON_FALSE(!(rgb_panel_config->flags.double_fb && rgb_panel_config->flags.no_fb),
ESP_ERR_INVALID_ARG, err, TAG, "invalid frame buffer number");
ESP_ERR_INVALID_ARG, err, TAG, "double_fb conflicts with no_fb");
ESP_GOTO_ON_FALSE(!(rgb_panel_config->num_fbs > 0 && rgb_panel_config->num_fbs != 2 && rgb_panel_config->flags.double_fb),
ESP_ERR_INVALID_ARG, err, TAG, "num_fbs conflicts with double_fb");
ESP_GOTO_ON_FALSE(!(rgb_panel_config->num_fbs > 0 && rgb_panel_config->flags.no_fb),
ESP_ERR_INVALID_ARG, err, TAG, "num_fbs conflicts with no_fb");
ESP_GOTO_ON_FALSE(!(rgb_panel_config->flags.no_fb && rgb_panel_config->bounce_buffer_size_px == 0),
ESP_ERR_INVALID_ARG, err, TAG, "must set bounce buffer if there's no frame buffer");
ESP_GOTO_ON_FALSE(!(rgb_panel_config->flags.refresh_on_demand && rgb_panel_config->bounce_buffer_size_px),
@ -221,7 +228,19 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
ESP_ERR_INVALID_ARG, err, TAG, "bounce buffer mode is not IRAM Safe");
#endif
// determine number of framebuffers
size_t num_fbs = 1;
if (rgb_panel_config->flags.no_fb) {
num_fbs = 0;
} else if (rgb_panel_config->flags.double_fb) {
num_fbs = 2;
} else if (rgb_panel_config->num_fbs > 0) {
num_fbs = rgb_panel_config->num_fbs;
}
ESP_GOTO_ON_FALSE(num_fbs <= RGB_LCD_PANEL_MAX_FB_NUM, ESP_ERR_INVALID_ARG, err, TAG, "too many frame buffers");
// bpp defaults to the number of data lines, but for serial RGB interface, they're not equal
// e.g. for serial RGB 8-bit interface, data lines are 8, whereas the bpp is 24 (RGB888)
size_t fb_bits_per_pixel = rgb_panel_config->data_width;
if (rgb_panel_config->bits_per_pixel) { // override bpp if it's set
fb_bits_per_pixel = rgb_panel_config->bits_per_pixel;
@ -247,11 +266,11 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
}
// DMA descriptors must be placed in internal SRAM (requested by DMA)
// multiply 2 because of double frame buffer mode (two frame buffer) and bounce buffer mode (two bounce buffer)
rgb_panel = heap_caps_calloc(1, sizeof(esp_rgb_panel_t) + num_dma_nodes * sizeof(dma_descriptor_t) * 2,
rgb_panel = heap_caps_calloc(1, sizeof(esp_rgb_panel_t) + num_dma_nodes * sizeof(dma_descriptor_t) * RGB_LCD_PANEL_DMA_LINKS_REPLICA,
MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
ESP_GOTO_ON_FALSE(rgb_panel, ESP_ERR_NO_MEM, err, TAG, "no mem for rgb panel");
rgb_panel->num_dma_nodes = num_dma_nodes;
rgb_panel->num_fbs = num_fbs;
rgb_panel->fb_size = fb_size;
rgb_panel->bb_size = bb_size;
rgb_panel->panel_id = -1;
@ -303,7 +322,6 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
rgb_panel->output_bits_per_pixel = fb_bits_per_pixel; // by default, the output bpp is the same as the frame buffer bpp
rgb_panel->disp_gpio_num = rgb_panel_config->disp_gpio_num;
rgb_panel->flags.disp_en_level = !rgb_panel_config->flags.disp_active_low;
rgb_panel->flags.no_fb = rgb_panel_config->flags.no_fb;
rgb_panel->flags.bb_invalidate_cache = rgb_panel_config->flags.bb_invalidate_cache;
rgb_panel->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
// fill function table
@ -318,9 +336,12 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
rgb_panel->base.set_gap = rgb_panel_set_gap;
// return base class
*ret_panel = &(rgb_panel->base);
ESP_LOGD(TAG, "new rgb panel(%d) @%p, fb0 @%p, fb1 @%p, fb_size=%zu, bb0 @%p, bb1 @%p, bb_size=%zu",
rgb_panel->panel_id, rgb_panel, rgb_panel->fbs[0], rgb_panel->fbs[1], rgb_panel->fb_size,
ESP_LOGD(TAG, "new rgb panel(%d) @%p, num_fbs=%zu, fb_size=%zu, bb0 @%p, bb1 @%p, bb_size=%zu",
rgb_panel->panel_id, rgb_panel, rgb_panel->num_fbs, rgb_panel->fb_size,
rgb_panel->bounce_buffer[0], rgb_panel->bounce_buffer[1], rgb_panel->bb_size);
for (size_t i = 0; i < rgb_panel->num_fbs; i++) {
ESP_LOGD(TAG, "fb[%zu] @%p", i, rgb_panel->fbs[i]);
}
return ESP_OK;
err:
@ -379,8 +400,8 @@ esp_err_t esp_lcd_rgb_panel_restart(esp_lcd_panel_handle_t panel)
esp_err_t esp_lcd_rgb_panel_get_frame_buffer(esp_lcd_panel_handle_t panel, uint32_t fb_num, void **fb0, ...)
{
ESP_RETURN_ON_FALSE(panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(fb_num && fb_num <= 2, ESP_ERR_INVALID_ARG, TAG, "invalid frame buffer number");
esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base);
ESP_RETURN_ON_FALSE(fb_num && fb_num <= rgb_panel->num_fbs, ESP_ERR_INVALID_ARG, TAG, "invalid frame buffer number");
void **fb_itor = fb0;
va_list args;
va_start(args, fb0);
@ -533,7 +554,7 @@ static inline void copy_pixel_24bpp(uint8_t *to, const uint8_t *from)
static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data)
{
esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base);
ESP_RETURN_ON_FALSE(!rgb_panel->flags.no_fb, ESP_ERR_NOT_SUPPORTED, TAG, "no frame buffer installed");
ESP_RETURN_ON_FALSE(rgb_panel->num_fbs > 0, ESP_ERR_NOT_SUPPORTED, TAG, "no frame buffer installed");
assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position");
// check if we need to copy the draw buffer (pointed by the color_data) to the driver's frame buffer
@ -542,6 +563,8 @@ static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
rgb_panel->cur_fb_index = 0;
} else if (color_data == rgb_panel->fbs[1]) {
rgb_panel->cur_fb_index = 1;
} else if (color_data == rgb_panel->fbs[2]) {
rgb_panel->cur_fb_index = 2;
} else {
// we do the copy only if the color_data is different from either frame buffer
do_copy = true;
@ -783,8 +806,9 @@ static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
if (!rgb_panel->bb_size) {
if (rgb_panel->flags.stream_mode) {
// the DMA will convey the new frame buffer next time
rgb_panel->dma_nodes[rgb_panel->num_dma_nodes - 1].next = rgb_panel->dma_links[rgb_panel->cur_fb_index];
rgb_panel->dma_nodes[rgb_panel->num_dma_nodes * 2 - 1].next = rgb_panel->dma_links[rgb_panel->cur_fb_index];
for (int i = 0; i < RGB_LCD_PANEL_DMA_LINKS_REPLICA; i++) {
rgb_panel->dma_nodes[rgb_panel->num_dma_nodes * (i + 1) - 1].next = rgb_panel->dma_links[rgb_panel->cur_fb_index];
}
}
}
@ -930,7 +954,7 @@ static IRAM_ATTR bool lcd_rgb_panel_fill_bounce_buffer(esp_rgb_panel_t *panel, u
{
bool need_yield = false;
int bytes_per_pixel = panel->fb_bits_per_pixel / 8;
if (panel->flags.no_fb) {
if (panel->num_fbs == 0) {
if (panel->on_bounce_empty) {
// We don't have a frame buffer here; we need to call a callback to refill the bounce buffer
need_yield = panel->on_bounce_empty(&panel->base, buffer, panel->bounce_pos_px, panel->bb_size, panel->user_ctx);
@ -954,7 +978,7 @@ static IRAM_ATTR bool lcd_rgb_panel_fill_bounce_buffer(esp_rgb_panel_t *panel, u
if (panel->bounce_pos_px >= panel->fb_size / bytes_per_pixel) {
panel->bounce_pos_px = 0;
}
if (!panel->flags.no_fb) {
if (panel->num_fbs > 0) {
// Preload the next bit of buffer from psram
Cache_Start_DCache_Preload((uint32_t)&panel->fbs[panel->cur_fb_index][panel->bounce_pos_px * bytes_per_pixel],
panel->bb_size, 0);
@ -979,34 +1003,36 @@ static IRAM_ATTR bool lcd_rgb_panel_eof_handler(gdma_channel_handle_t dma_chan,
static esp_err_t lcd_rgb_panel_create_trans_link(esp_rgb_panel_t *panel)
{
panel->dma_links[0] = &panel->dma_nodes[0];
panel->dma_links[1] = &panel->dma_nodes[panel->num_dma_nodes];
for (int i = 0; i < RGB_LCD_PANEL_DMA_LINKS_REPLICA; i++) {
panel->dma_links[i] = &panel->dma_nodes[panel->num_dma_nodes * i];
}
// chain DMA descriptors
for (int i = 0; i < panel->num_dma_nodes * 2; i++) {
for (int i = 0; i < panel->num_dma_nodes * RGB_LCD_PANEL_DMA_LINKS_REPLICA; i++) {
panel->dma_nodes[i].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_CPU;
panel->dma_nodes[i].next = &panel->dma_nodes[i + 1];
}
if (panel->bb_size) {
// loop end back to start
panel->dma_nodes[panel->num_dma_nodes * 2 - 1].next = &panel->dma_nodes[0];
panel->dma_nodes[panel->num_dma_nodes * RGB_LCD_PANEL_BOUNCE_BUF_NUM - 1].next = &panel->dma_nodes[0];
// mount the bounce buffers to the DMA descriptors
lcd_com_mount_dma_data(panel->dma_links[0], panel->bounce_buffer[0], panel->bb_size);
lcd_com_mount_dma_data(panel->dma_links[1], panel->bounce_buffer[1], panel->bb_size);
} else {
if (panel->flags.stream_mode) {
// circle DMA descriptors chain for each frame buffer
panel->dma_nodes[panel->num_dma_nodes - 1].next = &panel->dma_nodes[0];
panel->dma_nodes[panel->num_dma_nodes * 2 - 1].next = &panel->dma_nodes[panel->num_dma_nodes];
for (int i = 0; i < RGB_LCD_PANEL_DMA_LINKS_REPLICA; i++) {
panel->dma_nodes[panel->num_dma_nodes * (i + 1) - 1].next = &panel->dma_nodes[panel->num_dma_nodes * i];
}
} else {
// one-off DMA descriptors chain
panel->dma_nodes[panel->num_dma_nodes - 1].next = NULL;
panel->dma_nodes[panel->num_dma_nodes * 2 - 1].next = NULL;
for (int i = 0; i < RGB_LCD_PANEL_DMA_LINKS_REPLICA; i++) {
panel->dma_nodes[panel->num_dma_nodes * (i + 1) - 1].next = NULL;
}
}
// mount the frame buffer to the DMA descriptors
lcd_com_mount_dma_data(panel->dma_links[0], panel->fbs[0], panel->fb_size);
if (panel->fbs[1]) {
lcd_com_mount_dma_data(panel->dma_links[1], panel->fbs[1], panel->fb_size);
for (size_t i = 0; i < panel->num_fbs; i++) {
lcd_com_mount_dma_data(panel->dma_links[i], panel->fbs[i], panel->fb_size);
}
}

View File

@ -101,7 +101,6 @@ api-reference/peripherals/usb_device
api-reference/peripherals/sdspi_host
api-reference/peripherals/dac
api-reference/peripherals/touch_element
api-reference/peripherals/lcd
api-reference/peripherals/secure_element
api-reference/peripherals/ledc
api-reference/peripherals/sdio_slave

View File

@ -9,15 +9,223 @@ ESP chips can generate various kinds of timings that needed by common LCDs on th
Functional Overview
-------------------
In ``esp_lcd``, an LCD panel is represented by :cpp:type:`esp_lcd_panel_handle_t`, which plays the role of an **abstract frame buffer**, regardless of the frame memory is allocated inside ESP chip or in external LCD controller. Based on the location of the frame buffer, the LCD panel allocation functions are mainly grouped into the following categories:
In ``esp_lcd``, an LCD panel is represented by :cpp:type:`esp_lcd_panel_handle_t`, which plays the role of an **abstract frame buffer**, regardless of the frame memory is allocated inside ESP chip or in external LCD controller. Based on the location of the frame buffer and the hardware connection interface, the LCD panel drivers are mainly grouped into the following categories:
- ``RGB LCD panel`` - is simply based on a group of specific synchronous signals indicating where to start and stop a frame.
- ``Controller based LCD panel`` involves multiple steps to get a panel handle, like bus allocation, IO device registration and controller driver install.
.. list::
After we get the LCD handle, the remaining LCD operations are similar for different LCD interfaces and vendors.
- Controller based LCD driver involves multiple steps to get a panel handle, like bus allocation, IO device registration and controller driver install. The frame buffer is located in the controller's internal GRAM (Graphical RAM). ESP-IDF provides only a limited number of LCD controller drivers out of the box (e.g. ST7789, SSD1306), :ref:`more_controller_based_lcd_drivers` are maintained in the `Espressif Component Registry <https://components.espressif.com/>_`.
- :ref:`spi_lcd_panel` describes the steps to install the SPI LCD IO driver and then get the panel handle.
- :ref:`i2c_lcd_panel` describes the steps to install the I2C LCD IO driver and then get the panel handle.
:SOC_LCD_I80_SUPPORTED: - :ref:`i80_lcd_panel` describes the steps to install the I80 LCD IO driver and then get the panel handle.
:SOC_LCD_RGB_SUPPORTED: - :ref:`rgb_lcd_panel` - is based on a group of specific synchronous signals indicating where to start and stop a frame. The frame buffer is allocated on the ESP side. The driver install steps are much simplified because we don't need to install any IO interface driver in this case.
- :ref:`lcd_panel_operations` - provides a set of APIs to operate the LCD panel, like turning on/off the display, setting the orientation, etc. These operations are common for either controller-based LCD panel driver or RGB LCD panel driver.
.. _spi_lcd_panel:
SPI Interfaced LCD
------------------
#. Create an SPI bus. Please refer to :doc:`SPI Master API doc </api-reference/peripherals/spi_master>` for more details.
.. code-block:: c
spi_bus_config_t buscfg = {
.sclk_io_num = EXAMPLE_PIN_NUM_SCLK,
.mosi_io_num = EXAMPLE_PIN_NUM_MOSI,
.miso_io_num = EXAMPLE_PIN_NUM_MISO,
.quadwp_io_num = -1, // Quad SPI LCD driver is not yet supported
.quadhd_io_num = -1, // Quad SPI LCD driver is not yet supported
.max_transfer_sz = EXAMPLE_LCD_H_RES * 80 * sizeof(uint16_t), // transfer 80 lines of pixels (assume pixel is RGB565) at most in one SPI transaction
};
ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO)); // Enable the DMA feature
#. Allocate an LCD IO device handle from the SPI bus. In this step, you need to provide the following information:
- :cpp:member:`esp_lcd_panel_io_spi_config_t::dc_gpio_num`: Sets the gpio number for the DC signal line (some LCD calls this ``RS`` line). The LCD driver will use this GPIO to switch between sending command and sending data.
- :cpp:member:`esp_lcd_panel_io_spi_config_t::cs_gpio_num`: Sets the gpio number for the CS signal line. The LCD driver will use this GPIO to select the LCD chip. If the SPI bus only has one device attached (i.e. this LCD), you can set the gpio number to ``-1`` to occupy the bus exclusively.
- :cpp:member:`esp_lcd_panel_io_spi_config_t::pclk_hz` sets the frequency of the pixel clock, in Hz. The value should not exceed the range recommended in the LCD spec.
- :cpp:member:`esp_lcd_panel_io_spi_config_t::spi_mode` sets the SPI mode. The LCD driver will use this mode to communicate with the LCD. For the meaning of the SPI mode, please refer to the :doc:`SPI Master API doc </api-reference/peripherals/spi_master>`.
- :cpp:member:`esp_lcd_panel_io_spi_config_t::lcd_cmd_bits` and :cpp:member:`esp_lcd_panel_io_spi_config_t::lcd_param_bits` set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
- :cpp:member:`esp_lcd_panel_io_spi_config_t::trans_queue_depth` sets the depth of the SPI transaction queue. A bigger value means more transactions can be queued up, but it also consumes more memory.
.. code-block:: c
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_spi_config_t io_config = {
.dc_gpio_num = EXAMPLE_PIN_NUM_LCD_DC,
.cs_gpio_num = EXAMPLE_PIN_NUM_LCD_CS,
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
.spi_mode = 0,
.trans_queue_depth = 10,
};
// Attach the LCD to the SPI bus
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));
#. Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the SPI IO device handle that allocated in the last step, and some panel specific configurations:
- :cpp:member:`esp_lcd_panel_dev_config_t::reset_gpio_num` sets the LCD's hardware reset GPIO number. If the LCD does not have a hardware reset pin, set this to ``-1``.
- :cpp:member:`esp_lcd_panel_dev_config_t::rgb_endian` sets the endian of the RGB color data.
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of the pixel color data. The LCD driver will use this value to calculate the number of bytes to send to the LCD controller chip.
.. code-block:: c
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = EXAMPLE_PIN_NUM_RST,
.rgb_endian = LCD_RGB_ENDIAN_BGR,
.bits_per_pixel = 16,
};
// Create LCD panel handle for ST7789, with the SPI IO device handle
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));
.. _i2c_lcd_panel:
I2C Interfaced LCD
------------------
#. Create I2C bus. Please refer to :doc:`I2C API doc </api-reference/peripherals/i2c>` for more details.
.. code-block:: c
i2c_config_t i2c_conf = {
.mode = I2C_MODE_MASTER, // I2C LCD is a master node
.sda_io_num = EXAMPLE_PIN_NUM_SDA,
.scl_io_num = EXAMPLE_PIN_NUM_SCL,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
};
ESP_ERROR_CHECK(i2c_param_config(I2C_HOST, &i2c_conf));
ESP_ERROR_CHECK(i2c_driver_install(I2C_HOST, I2C_MODE_MASTER, 0, 0, 0));
#. Allocate an LCD IO device handle from the I2C bus. In this step, you need to provide the following information:
- :cpp:member:`esp_lcd_panel_io_i2c_config_t::dev_addr` sets the I2C device address of the LCD controller chip. The LCD driver will use this address to communicate with the LCD controller chip.
- :cpp:member:`esp_lcd_panel_io_i2c_config_t::lcd_cmd_bits` and :cpp:member:`esp_lcd_panel_io_i2c_config_t::lcd_param_bits` set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
.. code-block:: c
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_i2c_config_t io_config = {
.dev_addr = EXAMPLE_I2C_HW_ADDR,
.control_phase_bytes = 1, // refer to LCD spec
.dc_bit_offset = 6, // refer to LCD spec
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
.lcd_param_bits = EXAMPLE_LCD_CMD_BITS,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)I2C_HOST, &io_config, &io_handle));
#. Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the I2C IO device handle that allocated in the last step, and some panel specific configurations:
- :cpp:member:`esp_lcd_panel_dev_config_t::reset_gpio_num` sets the LCD's hardware reset GPIO number. If the LCD does not have a hardware reset pin, set this to ``-1``.
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of the pixel color data. The LCD driver will use this value to calculate the number of bytes to send to the LCD controller chip.
.. code-block:: c
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_panel_dev_config_t panel_config = {
.bits_per_pixel = 1,
.reset_gpio_num = EXAMPLE_PIN_NUM_RST,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(io_handle, &panel_config, &panel_handle));
.. only:: SOC_LCD_I80_SUPPORTED
.. _i80_lcd_panel:
I80 Interfaced LCD
------------------
#. Create I80 bus by :cpp:func:`esp_lcd_new_i80_bus`. You need to set up the following parameters for an Intel 8080 parallel bus:
- :cpp:member:`esp_lcd_i80_bus_config_t::clk_src` sets the clock source of the I80 bus. Note, the default clock source may be different between ESP targets.
- :cpp:member:`esp_lcd_i80_bus_config_t::wr_gpio_num` sets the GPIO number of the pixel clock (also referred as ``WR`` in some LCD spec)
- :cpp:member:`esp_lcd_i80_bus_config_t::dc_gpio_num` sets the GPIO number of the data/command select pin (also referred as ``RS`` in some LCD spec)
- :cpp:member:`esp_lcd_i80_bus_config_t::bus_width` sets the bit width of the data bus (only support ``8`` or ``16``)
- :cpp:member:`esp_lcd_i80_bus_config_t::data_gpio_nums` is the array of the GPIO number of the data bus. The number of GPIOs should be equal to the :cpp:member:`esp_lcd_i80_bus_config_t::bus_width` value.
- :cpp:member:`esp_lcd_i80_bus_config_t::max_transfer_bytes` sets the maximum number of bytes that can be transferred in one transaction.
.. code-block:: c
esp_lcd_i80_bus_handle_t i80_bus = NULL;
esp_lcd_i80_bus_config_t bus_config = {
.clk_src = LCD_CLK_SRC_DEFAULT,
.dc_gpio_num = EXAMPLE_PIN_NUM_DC,
.wr_gpio_num = EXAMPLE_PIN_NUM_PCLK,
.data_gpio_nums = {
EXAMPLE_PIN_NUM_DATA0,
EXAMPLE_PIN_NUM_DATA1,
EXAMPLE_PIN_NUM_DATA2,
EXAMPLE_PIN_NUM_DATA3,
EXAMPLE_PIN_NUM_DATA4,
EXAMPLE_PIN_NUM_DATA5,
EXAMPLE_PIN_NUM_DATA6,
EXAMPLE_PIN_NUM_DATA7,
},
.bus_width = 8,
.max_transfer_bytes = EXAMPLE_LCD_H_RES * 100 * sizeof(uint16_t), // transfer 100 lines of pixels (assume pixel is RGB565) at most in one transaction
.psram_trans_align = EXAMPLE_PSRAM_DATA_ALIGNMENT,
.sram_trans_align = 4,
};
ESP_ERROR_CHECK(esp_lcd_new_i80_bus(&bus_config, &i80_bus));
#. Allocate an LCD IO device handle from the I80 bus. In this step, you need to provide the following information:
- :cpp:member:`esp_lcd_panel_io_i80_config_t::cs_gpio_num` sets the GPIO number of the chip select pin.
- :cpp:member:`esp_lcd_panel_io_i80_config_t::pclk_hz` sets the pixel clock frequency in Hz. Higher pixel clock frequency will result in higher refresh rate, but may cause flickering if the DMA bandwidth is not sufficient or the LCD controller chip does not support high pixel clock frequency.
- :cpp:member:`esp_lcd_panel_io_i80_config_t::lcd_cmd_bits` and :cpp:member:`esp_lcd_panel_io_i80_config_t::lcd_param_bits` set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
- :cpp:member:`esp_lcd_panel_io_i80_config_t::trans_queue_depth` sets the maximum number of transactions that can be queued in the LCD IO device. A bigger value means more transactions can be queued up, but it also consumes more memory.
.. code-block:: c
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_i80_config_t io_config = {
.cs_gpio_num = EXAMPLE_PIN_NUM_CS,
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
.trans_queue_depth = 10,
.dc_levels = {
.dc_idle_level = 0,
.dc_cmd_level = 0,
.dc_dummy_level = 0,
.dc_data_level = 1,
},
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i80(i80_bus, &io_config, &io_handle));
#. Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the I80 IO device handle that allocated in the last step, and some panel specific configurations:
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of the pixel color data. The LCD driver will use this value to calculate the number of bytes to send to the LCD controller chip.
- :cpp:member:`esp_lcd_panel_dev_config_t::reset_gpio_num` sets the GPIO number of the reset pin. If the LCD controller chip does not have a reset pin, you can set this value to ``-1``.
- :cpp:member:`esp_lcd_panel_dev_config_t::rgb_endian` sets the endian of the pixel color data.
.. code-block:: c
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = EXAMPLE_PIN_NUM_RST,
.rgb_endian = LCD_RGB_ENDIAN_RGB,
.bits_per_pixel = 16,
};
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));
.. _more_controller_based_lcd_drivers:
.. only:: not SOC_LCD_I80_SUPPORTED
.. _more_controller_based_lcd_drivers:
More Controller Based LCD Drivers
---------------------------------
More LCD panel drivers and touch drivers are available in `IDF Component Registry <https://components.espressif.com/search/lcd>`_. The list of available and planned drivers with links is in this `table <https://github.com/espressif/esp-bsp/blob/master/LCD.md>`_.
.. only:: SOC_LCD_RGB_SUPPORTED
.. _rgb_lcd_panel:
RGB Interfaced LCD
------------------
@ -31,8 +239,8 @@ After we get the LCD handle, the remaining LCD operations are similar for differ
- :cpp:member:`esp_lcd_rgb_panel_config_t::bounce_buffer_size_px` set the size of bounce buffer. This is only necessary for a so-called "bounce buffer" mode. Please refer to :ref:`bounce_buffer_with_single_psram_frame_buffer` for more information.
- :cpp:member:`esp_lcd_rgb_panel_config_t::timings` sets the LCD panel specific timing parameters. All required parameters are listed in the :cpp:type:`esp_lcd_rgb_timing_t`, including the LCD resolution and blanking porches. Please fill them according to the datasheet of your LCD.
- :cpp:member:`esp_lcd_rgb_panel_config_t::fb_in_psram` sets whether to allocate the frame buffer from PSRAM or not. Please refer to :ref:`single_frame_buffer_in_psram` for more information.
- :cpp:member:`esp_lcd_rgb_panel_config_t::double_fb` sets whether to enable the double frame buffer mode. Please refer to :ref:`double_frame_buffer_in_psram` for more information.
- :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` sets whether to use the :ref:`bounce_buffer_only` mode.
- :cpp:member:`esp_lcd_rgb_panel_config_t::num_fbs` sets the number of frame buffers allocated by the driver. For backward compatibility, ``0`` means to allocate ``one`` frame buffer. Please use :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` if you don't want to allocate any frame buffer.
- :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` if sets, no frame buffer will be allocated. This is also called the :ref:`bounce_buffer_only` mode.
RGB LCD Frame Buffer Operation Modes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -137,6 +345,7 @@ After we get the LCD handle, the remaining LCD operations are similar for differ
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
.num_fbs = 2, // allocate double frame buffer
.clk_src = LCD_CLK_SRC_DEFAULT,
.disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN,
.pclk_gpio_num = EXAMPLE_PIN_NUM_PCLK,
@ -164,7 +373,6 @@ After we get the LCD handle, the remaining LCD operations are similar for differ
.vsync_pulse_width = 1,
},
.flags.fb_in_psram = true, // allocate frame buffer from PSRAM
.flags.double_fb = true, // allocate double frame buffer
};
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
@ -225,6 +433,20 @@ After we get the LCD handle, the remaining LCD operations are similar for differ
It should never happen in a well-designed embedded application, but it can in theory be possible that the DMA cannot deliver data as fast as the LCD consumes it. In the {IDF_TARGET_NAME} hardware, this leads to the LCD simply outputting dummy bytes while DMA waits for data. If we were to run DMA in a stream fashion, this would mean a de-sync between the LCD address the DMA reads the data for and the LCD address the LCD peripheral thinks it outputs data for, leading to a **permanently** shifted image.
In order to stop this from happening, you can either enable the :ref:`CONFIG_LCD_RGB_RESTART_IN_VSYNC` option, so the driver can restart the DMA in the VBlank interrupt automatically or call :cpp:func:`esp_lcd_rgb_panel_restart` to restart the DMA manually. Note :cpp:func:`esp_lcd_rgb_panel_restart` doesn't restart the DMA immediately, the DMA will still be restarted in the next VSYNC event.
.. _lcd_panel_operations:
.. only:: not SOC_LCD_RGB_SUPPORTED
.. _lcd_panel_operations:
LCD Panel IO Operations
-----------------------
* :cpp:func:`esp_lcd_panel_reset` can reset the LCD panel.
* Use :cpp:func:`esp_lcd_panel_swap_xy` and :cpp:func:`esp_lcd_panel_mirror`, you can rotate the LCD screen.
* :cpp:func:`esp_lcd_panel_disp_on_off` can turn on or off the LCD screen (different from LCD backlight).
* :cpp:func:`esp_lcd_panel_draw_bitmap` is the most significant function, that will do the magic to draw the user provided color buffer to the LCD screen, where the draw window is also configurable.
Application Example
-------------------
@ -238,11 +460,6 @@ LCD examples are located under: :example:`peripherals/lcd`:
:SOC_LCD_RGB_SUPPORTED: * RGB panel example with scatter chart UI - :example:`peripherals/lcd/rgb_panel`
* I2C interfaced OLED display scrolling text - :example:`peripherals/lcd/i2c_oled`
Other LCD drivers
-----------------
Drivers for some LCD and touch controllers are available in `IDF Component Registry <https://components.espressif.com/search/lcd>`_. The list of available and planned drivers with links is in this `table <https://github.com/espressif/esp-bsp/blob/master/LCD.md>`__.
API Reference
-------------

View File

@ -70,9 +70,7 @@ examples/peripherals/i2s/i2s_recorder:
examples/peripherals/lcd/i2c_oled:
disable:
- if: IDF_TARGET == "esp32c6"
temporary: true
reason: target esp32c6 is not supported yet
- if: SOC_I2C_SUPPORTED != 1
examples/peripherals/lcd/i80_controller:
disable:
@ -82,18 +80,6 @@ examples/peripherals/lcd/rgb_panel:
disable:
- if: SOC_LCD_RGB_SUPPORTED != 1
examples/peripherals/lcd/spi_lcd_touch:
disable:
- if: IDF_TARGET == "esp32c6"
temporary: true
reason: target esp32c6 is not supported yet
examples/peripherals/lcd/tjpgd:
disable:
- if: IDF_TARGET == "esp32c6"
temporary: true
reason: target esp32c6 is not supported yet
examples/peripherals/ledc/ledc_basic:
disable:
- if: IDF_TARGET == "esp32c6"

View File

@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
# I2C OLED example

View File

@ -52,6 +52,12 @@ static const char *TAG = "example";
#define EXAMPLE_LCD_H_RES 800
#define EXAMPLE_LCD_V_RES 480
#if CONFIG_EXAMPLE_DOUBLE_FB
#define EXAMPLE_LCD_NUM_FB 2
#else
#define EXAMPLE_LCD_NUM_FB 1
#endif // CONFIG_EXAMPLE_DOUBLE_FB
#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
@ -122,6 +128,7 @@ void app_main(void)
esp_lcd_rgb_panel_config_t panel_config = {
.data_width = 16, // RGB565 in parallel mode, thus 16bit in width
.psram_trans_align = 64,
.num_fbs = EXAMPLE_LCD_NUM_FB,
#if CONFIG_EXAMPLE_USE_BOUNCE_BUFFER
.bounce_buffer_size_px = 10 * EXAMPLE_LCD_H_RES,
#endif
@ -163,9 +170,6 @@ void app_main(void)
.flags.pclk_active_neg = true,
},
.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));

View File

@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
# SPI LCD and Touch Panel Example

View File

@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- |
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
## LCD tjpgd example

View File

@ -16,7 +16,7 @@
#include "driver/gpio.h"
#include "pretty_effect.h"
// Using SPI2 in the example, as it aslo supports octal modes on some targets
// Using SPI2 in the example, as it also supports octal modes on some targets
#define LCD_HOST SPI2_HOST
// To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many.
// More means more memory use, but less overhead for setting up / finishing transfers. Make sure 240
@ -31,7 +31,7 @@
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (20 * 1000 * 1000)
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 0
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_DATA0 23 /*!< for 1-line SPI, this also refered as MOSI */
#define EXAMPLE_PIN_NUM_DATA0 23 /*!< for 1-line SPI, this also refereed as MOSI */
#define EXAMPLE_PIN_NUM_PCLK 19
#define EXAMPLE_PIN_NUM_CS 22
#define EXAMPLE_PIN_NUM_DC 21