Merge branch 'feat/rgb_lcd_memory' into 'master'

feat(rgb_lcd): allow users to manage the lifecycle of the frame buffer

Closes IDF-13917

See merge request espressif/esp-idf!41457
This commit is contained in:
Chen Ji Chang
2025-10-30 16:07:15 +08:00
6 changed files with 206 additions and 29 deletions
+61 -18
View File
@@ -70,7 +70,6 @@
#error "Unsupported GDMA bus type for RGB LCD"
#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
static const char *TAG = "lcd_panel.rgb";
@@ -109,12 +108,12 @@ struct esp_rgb_panel_t {
#endif
size_t num_dma_nodes; // Number of DMA descriptors that used to carry the frame buffer
gdma_channel_handle_t dma_chan; // DMA channel handle
gdma_link_list_handle_t dma_fb_links[RGB_LCD_PANEL_MAX_FB_NUM]; // DMA link lists for multiple frame buffers
gdma_link_list_handle_t dma_fb_links[ESP_RGB_LCD_PANEL_MAX_FB_NUM]; // DMA link lists for multiple frame buffers
gdma_link_list_handle_t dma_bb_link; // DMA link list for bounce buffer
#if RGB_LCD_NEEDS_SEPARATE_RESTART_LINK
gdma_link_list_handle_t dma_restart_link; // DMA link list for restarting the DMA
#endif
uint8_t *fbs[RGB_LCD_PANEL_MAX_FB_NUM]; // Frame buffers
uint8_t *fbs[ESP_RGB_LCD_PANEL_MAX_FB_NUM]; // Frame buffers
uint8_t *bounce_buffer[RGB_LCD_PANEL_BOUNCE_BUF_NUM]; // Pointer to the bounce buffers
size_t fb_size; // Size of frame buffer, in bytes
size_t bb_size; // Size of the bounce buffer, in bytes. If not-zero, the driver uses two bounce buffers allocated from internal memory
@@ -151,10 +150,11 @@ struct esp_rgb_panel_t {
uint32_t need_restart: 1; // Whether to restart the LCD controller and the DMA
uint32_t fb_behind_cache: 1; // Whether the frame buffer is behind the cache
uint32_t bb_behind_cache: 1; // Whether the bounce buffer is behind the cache
uint32_t user_fb: 1; // Whether the frame buffer is provided by user
} flags;
};
static esp_err_t lcd_rgb_panel_alloc_frame_buffers(esp_rgb_panel_t *rgb_panel)
static esp_err_t lcd_rgb_panel_alloc_frame_buffers(esp_rgb_panel_t *rgb_panel, const esp_lcd_rgb_panel_config_t *panel_config)
{
bool fb_in_psram = rgb_panel->flags.fb_in_psram;
@@ -163,25 +163,53 @@ static esp_err_t lcd_rgb_panel_alloc_frame_buffers(esp_rgb_panel_t *rgb_panel)
uint32_t ext_mem_cache_line_size = cache_hal_get_cache_line_size(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_DATA);
// alloc frame buffer
uint8_t user_fb_count = 0;
for (int i = 0; i < rgb_panel->num_fbs; i++) {
if (fb_in_psram) {
// the allocated buffer is also aligned to the cache line size
rgb_panel->fbs[i] = heap_caps_aligned_calloc(rgb_panel->ext_mem_align, 1, rgb_panel->fb_size,
MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(rgb_panel->fbs[i], ESP_ERR_NO_MEM, TAG, "no mem for frame buffer");
rgb_panel->flags.fb_behind_cache = ext_mem_cache_line_size > 0;
if (panel_config->user_fbs[i] != NULL) {
// Frame buffer is provided by user, no need to allocate
rgb_panel->flags.user_fb = true;
// Check user frame buffer DMA accessibility
if (!esp_ptr_dma_capable(panel_config->user_fbs[i]) && !esp_ptr_dma_ext_capable(panel_config->user_fbs[i])) {
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "frame buffer %d is not DMA accessible", i);
}
// Check if user frame buffer is in PSRAM or internal memory
if (esp_ptr_external_ram(panel_config->user_fbs[i])) {
ESP_RETURN_ON_FALSE(((uintptr_t)panel_config->user_fbs[i] & (rgb_panel->ext_mem_align - 1)) == 0,
ESP_ERR_INVALID_ARG, TAG, "frame buffer %d is not aligned to "PRIu32"", i, rgb_panel->ext_mem_align);
rgb_panel->flags.fb_behind_cache = ext_mem_cache_line_size > 0;
} else {
ESP_RETURN_ON_FALSE(((uintptr_t)panel_config->user_fbs[i] & (rgb_panel->int_mem_align - 1)) == 0,
ESP_ERR_INVALID_ARG, TAG, "frame buffer %d is not aligned to "PRIu32"", i, rgb_panel->int_mem_align);
rgb_panel->flags.fb_behind_cache = int_mem_cache_line_size > 0;
}
rgb_panel->fbs[i] = (uint8_t *)panel_config->user_fbs[i];
user_fb_count++;
} else {
rgb_panel->fbs[i] = heap_caps_aligned_calloc(rgb_panel->int_mem_align, 1, rgb_panel->fb_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(rgb_panel->fbs[i], ESP_ERR_NO_MEM, TAG, "no mem for frame buffer");
rgb_panel->flags.fb_behind_cache = int_mem_cache_line_size > 0;
// Allocate frame buffer by driver
if (fb_in_psram) {
// the allocated buffer is also aligned to the cache line size
rgb_panel->fbs[i] = heap_caps_aligned_calloc(rgb_panel->ext_mem_align, 1, rgb_panel->fb_size,
MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(rgb_panel->fbs[i], ESP_ERR_NO_MEM, TAG, "no mem for frame buffer");
rgb_panel->flags.fb_behind_cache = ext_mem_cache_line_size > 0;
} else {
rgb_panel->fbs[i] = heap_caps_aligned_calloc(rgb_panel->int_mem_align, 1, rgb_panel->fb_size,
MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(rgb_panel->fbs[i], ESP_ERR_NO_MEM, TAG, "no mem for frame buffer");
rgb_panel->flags.fb_behind_cache = int_mem_cache_line_size > 0;
}
}
// flush data from cache to the physical memory
if (rgb_panel->flags.fb_behind_cache) {
esp_cache_msync(rgb_panel->fbs[i], rgb_panel->fb_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
}
}
if (user_fb_count > 0 && user_fb_count != rgb_panel->num_fbs) {
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "user frame buffer count conflicts with num_fbs");
}
// alloc bounce buffer
if (rgb_panel->bb_size) {
for (int i = 0; i < RGB_LCD_PANEL_BOUNCE_BUF_NUM; i++) {
@@ -223,8 +251,9 @@ static esp_err_t lcd_rgb_panel_destroy(esp_rgb_panel_t *rgb_panel)
gdma_disconnect(rgb_panel->dma_chan);
gdma_del_channel(rgb_panel->dma_chan);
}
for (size_t i = 0; i < RGB_LCD_PANEL_MAX_FB_NUM; i++) {
if (rgb_panel->fbs[i]) {
for (size_t i = 0; i < ESP_RGB_LCD_PANEL_MAX_FB_NUM; i++) {
if (rgb_panel->fbs[i] && !rgb_panel->flags.user_fb) {
// Only free frame buffer if it was allocated by the driver
free(rgb_panel->fbs[i]);
}
if (rgb_panel->dma_fb_links[i]) {
@@ -286,7 +315,7 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
} else if (rgb_panel_config->num_fbs > 0) {
num_fbs = rgb_panel_config->num_fbs;
}
ESP_RETURN_ON_FALSE(num_fbs <= RGB_LCD_PANEL_MAX_FB_NUM, ESP_ERR_INVALID_ARG, TAG, "too many frame buffers");
ESP_RETURN_ON_FALSE(num_fbs <= ESP_RGB_LCD_PANEL_MAX_FB_NUM, ESP_ERR_INVALID_ARG, 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)
@@ -357,7 +386,7 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
rgb_panel->flags.fb_in_psram = rgb_panel_config->flags.fb_in_psram;
ESP_GOTO_ON_ERROR(lcd_rgb_create_dma_channel(rgb_panel), err, TAG, "install DMA failed");
// allocate frame buffers + bounce buffers
ESP_GOTO_ON_ERROR(lcd_rgb_panel_alloc_frame_buffers(rgb_panel), err, TAG, "alloc frame buffers failed");
ESP_GOTO_ON_ERROR(lcd_rgb_panel_alloc_frame_buffers(rgb_panel, rgb_panel_config), err, TAG, "alloc frame buffers failed");
// initialize DMA descriptor link
ESP_GOTO_ON_ERROR(lcd_rgb_panel_init_trans_link(rgb_panel), err, TAG, "init DMA link failed");
@@ -543,6 +572,20 @@ esp_err_t esp_lcd_rgb_panel_set_yuv_conversion(esp_lcd_panel_handle_t panel, con
return ESP_OK;
}
void *esp_lcd_rgb_alloc_draw_buffer(esp_lcd_panel_handle_t panel, size_t size, uint32_t caps)
{
ESP_RETURN_ON_FALSE(panel, NULL, TAG, "invalid argument");
esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base);
void *buf = NULL;
// alloc from external memory
if (caps & MALLOC_CAP_SPIRAM) {
buf = heap_caps_aligned_calloc(rgb_panel->ext_mem_align, 1, size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA);
} else {
buf = heap_caps_aligned_calloc(rgb_panel->int_mem_align, 1, size, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
}
return buf;
}
static esp_err_t rgb_panel_del(esp_lcd_panel_t *panel)
{
esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base);
@@ -18,6 +18,9 @@ extern "C" {
#endif
#if SOC_LCD_RGB_SUPPORTED
#define ESP_RGB_LCD_PANEL_MAX_FB_NUM 3 // maximum supported frame buffer number
/**
* @brief LCD RGB timing structure
* @verbatim
@@ -142,6 +145,7 @@ typedef struct {
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 */
void *user_fbs[ESP_RGB_LCD_PANEL_MAX_FB_NUM]; /*!< Array of user-provided frame buffers. If not NULL, the driver will use these buffers instead of allocating its own */
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 dma_burst_size; /*!< DMA burst size, in bytes */
@@ -250,6 +254,18 @@ esp_err_t esp_lcd_rgb_panel_get_frame_buffer(esp_lcd_panel_handle_t panel, uint3
*/
esp_err_t esp_lcd_rgb_panel_refresh(esp_lcd_panel_handle_t panel);
/**
* @brief Allocate a draw buffer for RGB LCD panel
*
* @note This function differs from the normal 'heap_caps_*' functions in that it can also automatically handle the alignment required by DMA burst, cache line size, etc.
*
* @param[in] panel LCD panel handle, returned from `esp_lcd_new_rgb_panel`
* @param[in] size Size of memory to be allocated
* @param[in] caps Bitwise OR of MALLOC_CAP_* flags indicating the type of memory desired for the allocation
* @return Pointer to a new buffer of size 'size' with capabilities 'caps', or NULL if allocation failed
*/
void *esp_lcd_rgb_alloc_draw_buffer(esp_lcd_panel_handle_t panel, size_t size, uint32_t caps);
/**
* @brief LCD color conversion profile
*/
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -25,7 +25,7 @@
#define TEST_IMG_SIZE (100 * 100 * sizeof(uint16_t))
static esp_lcd_panel_handle_t test_rgb_panel_initialization(size_t data_width, size_t bpp, size_t bb_pixels, bool refresh_on_demand,
static esp_lcd_panel_handle_t test_rgb_panel_initialization(size_t data_width, size_t bpp, size_t bb_pixels, bool refresh_on_demand, bool user_fb,
esp_lcd_rgb_panel_vsync_cb_t vsync_cb, void *user_data)
{
esp_lcd_panel_handle_t panel_handle = NULL;
@@ -73,6 +73,12 @@ static esp_lcd_panel_handle_t test_rgb_panel_initialization(size_t data_width, s
.flags.refresh_on_demand = refresh_on_demand,
};
if (user_fb) {
void *frame_buffer = heap_caps_aligned_calloc(64, 1, TEST_LCD_H_RES * TEST_LCD_V_RES * sizeof(uint16_t), MALLOC_CAP_SPIRAM);
TEST_ASSERT_NOT_NULL(frame_buffer);
panel_config.user_fbs[0] = frame_buffer;
}
TEST_ESP_OK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
esp_lcd_rgb_panel_event_callbacks_t cbs = {
@@ -91,7 +97,7 @@ TEST_CASE("lcd_rgb_panel_stream_mode", "[lcd]")
TEST_ASSERT_NOT_NULL(img);
printf("initialize RGB panel with stream mode\r\n");
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, false, NULL, NULL);
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, false, false, NULL, NULL);
printf("flush random color block\r\n");
for (int i = 0; i < 200; i++) {
uint8_t color_byte = esp_random() & 0xFF;
@@ -113,7 +119,7 @@ TEST_CASE("lcd_rgb_panel_8bit_interface", "[lcd]")
printf("initialize RGB panel with stream mode\r\n");
// bpp for RGB888 is 24
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(8, 24, 0, false, NULL, NULL);
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(8, 24, 0, false, false, NULL, NULL);
uint8_t color_byte = esp_random() & 0xFF;
printf("flush random color block 0x%x\r\n", color_byte);
int x_start = esp_random() % (TEST_LCD_H_RES - 100);
@@ -141,7 +147,7 @@ TEST_CASE("lcd_rgb_panel_refresh_on_demand", "[lcd]")
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
printf("initialize RGB panel with non-stream mode\r\n");
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, true, test_rgb_panel_trans_done, cur_task);
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, true, false, test_rgb_panel_trans_done, cur_task);
printf("flush random color block\r\n");
for (int i = 0; i < 200; i++) {
uint8_t color_byte = esp_random() & 0xFF;
@@ -166,7 +172,7 @@ TEST_CASE("lcd_rgb_panel_bounce_buffer", "[lcd]")
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
printf("initialize RGB panel with non-stream mode\r\n");
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 20 * TEST_LCD_H_RES, false, test_rgb_panel_trans_done, cur_task);
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 20 * TEST_LCD_H_RES, false, false, test_rgb_panel_trans_done, cur_task);
printf("flush random color block\r\n");
for (int i = 0; i < 200; i++) {
uint8_t color_byte = esp_random() & 0xFF;
@@ -189,7 +195,7 @@ TEST_CASE("lcd_rgb_panel_update_pclk", "[lcd]")
TEST_ASSERT_NOT_NULL(img);
printf("initialize RGB panel with stream mode\r\n");
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, false, NULL, NULL);
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, false, false, NULL, NULL);
printf("flush one clock block to the LCD\r\n");
uint8_t color_byte = esp_random() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100);
@@ -217,7 +223,7 @@ TEST_CASE("lcd_rgb_panel_restart", "[lcd]")
TEST_ASSERT_NOT_NULL(img);
printf("initialize RGB panel with stream mode\r\n");
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, false, NULL, NULL);
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, false, false, NULL, NULL);
printf("flush one clock block to the LCD\r\n");
uint8_t color_byte = esp_random() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100);
@@ -247,7 +253,7 @@ TEST_CASE("lcd_rgb_panel_rotate", "[lcd]")
memset(img, color_byte, w * h * sizeof(uint16_t));
printf("initialize RGB panel with stream mode\r\n");
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, false, NULL, NULL);
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, false, false, NULL, NULL);
printf("Update the rotation of panel\r\n");
for (size_t i = 0; i < 8; i++) {
@@ -266,6 +272,34 @@ TEST_CASE("lcd_rgb_panel_rotate", "[lcd]")
free(img);
}
TEST_CASE("lcd_rgb_panel_user_frame_buffer", "[lcd]")
{
uint8_t *img = malloc(TEST_IMG_SIZE);
TEST_ASSERT_NOT_NULL(img);
printf("initialize RGB panel with stream mode\r\n");
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, false, true, NULL, NULL);
printf("flush one clock block to the LCD\r\n");
uint8_t color_byte = esp_random() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100);
int y_start = esp_random() % (TEST_LCD_V_RES - 100);
memset(img, color_byte, TEST_IMG_SIZE);
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img);
printf("The LCD driver should keep flushing the color block in the background (as it's in stream mode)\r\n");
vTaskDelay(pdMS_TO_TICKS(1000));
// get the user frame buffer to free it
void *user_frame_buffer;
esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 1, &user_frame_buffer);
TEST_ASSERT_NOT_NULL(user_frame_buffer);
printf("delete RGB panel\r\n");
TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
free(img);
free(user_frame_buffer);
}
#if CONFIG_LCD_RGB_ISR_IRAM_SAFE
TEST_LCD_CALLBACK_ATTR static bool test_rgb_panel_count_in_callback(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx)
{
@@ -291,7 +325,7 @@ TEST_CASE("lcd_rgb_panel_iram_safe", "[lcd]")
uint32_t callback_calls = 0;
printf("initialize RGB panel with stream mode\r\n");
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, false, test_rgb_panel_count_in_callback, &callback_calls);
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, false, false, test_rgb_panel_count_in_callback, &callback_calls);
printf("flush one clock block to the LCD\r\n");
uint8_t color_byte = esp_random() & 0xFF;
int x_start = esp_random() % (TEST_LCD_H_RES - 100);
@@ -150,6 +150,48 @@ To prevent tearing effects, the simplest method is to use two screen-sized frame
};
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
.. _user_custom_frame_buffer:
User Custom Frame Buffer
^^^^^^^^^^^^^^^^^^^^^^^^
User can provide their own frame buffer instead of letting the driver allocate it. In this mode, user needs to manage the lifecycle of the frame buffer by themselves.
.. code:: c
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_rgb_panel_config_t panel_config = {
.data_width = 16, // RGB565 in parallel mode, thus 16 bits in width
.clk_src = LCD_CLK_SRC_DEFAULT,
.disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN,
.pclk_gpio_num = EXAMPLE_PIN_NUM_PCLK,
.vsync_gpio_num = EXAMPLE_PIN_NUM_VSYNC,
.hsync_gpio_num = EXAMPLE_PIN_NUM_HSYNC,
.de_gpio_num = EXAMPLE_PIN_NUM_DE,
.data_gpio_nums = {
EXAMPLE_PIN_NUM_DATA0,
EXAMPLE_PIN_NUM_DATA1,
EXAMPLE_PIN_NUM_DATA2,
// other GPIOs
// The number of GPIOs here should be the same to the value of "data_width" above
...
},
// The timing parameters should refer to your LCD spec
.timings = {
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
.h_res = EXAMPLE_LCD_H_RES,
.v_res = EXAMPLE_LCD_V_RES,
.hsync_back_porch = 40,
.hsync_front_porch = 20,
.hsync_pulse_width = 1,
.vsync_back_porch = 8,
.vsync_front_porch = 4,
.vsync_pulse_width = 1,
},
.user_fbs[0] = user_frame_buffer, // use user custom frame buffer
};
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
.. _bounce_buffer_with_single_psram_frame_buffer:
Bounce Buffer with Single PSRAM Frame Buffer
@@ -150,6 +150,48 @@ PSRAM 中的双 frame buffer
};
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
.. _user_custom_frame_buffer:
用户自定义 frame buffer
^^^^^^^^^^^^^^^^^^^^^^^^^
用户可以提供自己分配的 frame buffer,而不是让驱动程序分配。该模式下,用户需要自己管理 frame buffer 的生命周期。
.. code:: c
esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_rgb_panel_config_t panel_config = {
.data_width = 16, // 并行模式下像素格式为 RGB565,数据宽度为 16 位
.clk_src = LCD_CLK_SRC_DEFAULT,
.disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN,
.pclk_gpio_num = EXAMPLE_PIN_NUM_PCLK,
.vsync_gpio_num = EXAMPLE_PIN_NUM_VSYNC,
.hsync_gpio_num = EXAMPLE_PIN_NUM_HSYNC,
.de_gpio_num = EXAMPLE_PIN_NUM_DE,
.data_gpio_nums = {
EXAMPLE_PIN_NUM_DATA0,
EXAMPLE_PIN_NUM_DATA1,
EXAMPLE_PIN_NUM_DATA2,
// 其他 GPIO
// 此处 GPIO 的数量应与上文中 "data_width" 的值相同
...
},
// 参照 LCD 规格书,填写时序参数
.timings = {
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
.h_res = EXAMPLE_LCD_H_RES,
.v_res = EXAMPLE_LCD_V_RES,
.hsync_back_porch = 40,
.hsync_front_porch = 20,
.hsync_pulse_width = 1,
.vsync_back_porch = 8,
.vsync_front_porch = 4,
.vsync_pulse_width = 1,
},
.user_fbs[0] = user_frame_buffer, // 使用用户自定义 frame buffer
};
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
.. _bounce_buffer_with_single_psram_frame_buffer:
bounce buffer 与 PSRAM frame buffer
@@ -256,7 +256,7 @@ void app_main(void)
ESP_LOGI(TAG, "Allocate LVGL draw buffers");
// 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 * EXAMPLE_PIXEL_SIZE;
buf1 = heap_caps_malloc(draw_buffer_sz, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
buf1 = esp_lcd_rgb_alloc_draw_buffer(panel_handle, draw_buffer_sz, 0);
assert(buf1);
// set LVGL draw buffers and partial mode
lv_display_set_buffers(display, buf1, buf2, draw_buffer_sz, LV_DISPLAY_RENDER_MODE_PARTIAL);