From 58e1e16faa0842750c8eadcf7dde54ecccb6de67 Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Wed, 15 Oct 2025 11:33:28 +0800 Subject: [PATCH] feat(rgb_lcd): allow users to manage the lifecycle of the frame buffer --- components/esp_lcd/rgb/esp_lcd_panel_rgb.c | 79 ++++++++++++++----- .../esp_lcd/rgb/include/esp_lcd_panel_rgb.h | 16 ++++ .../test_apps/rgb_lcd/main/test_rgb_panel.c | 54 ++++++++++--- .../api-reference/peripherals/lcd/rgb_lcd.rst | 42 ++++++++++ .../api-reference/peripherals/lcd/rgb_lcd.rst | 42 ++++++++++ .../lcd/rgb_panel/main/rgb_lcd_example_main.c | 2 +- 6 files changed, 206 insertions(+), 29 deletions(-) diff --git a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c index 6ef0c538e3..e5def4c9ce 100644 --- a/components/esp_lcd/rgb/esp_lcd_panel_rgb.c +++ b/components/esp_lcd/rgb/esp_lcd_panel_rgb.c @@ -72,7 +72,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 ESP_LOG_ATTR_TAG(TAG, "lcd.rgb"); @@ -112,12 +111,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 @@ -154,10 +153,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; @@ -166,25 +166,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++) { @@ -226,8 +254,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"); // by default, we guess the input color format according to the data lines number, usually 16 lines means RGB565, 24 lines means RGB888 lcd_color_format_t in_color_format = 0; @@ -370,7 +399,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"); @@ -516,6 +545,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); diff --git a/components/esp_lcd/rgb/include/esp_lcd_panel_rgb.h b/components/esp_lcd/rgb/include/esp_lcd_panel_rgb.h index 4657d1bba2..2831052be4 100644 --- a/components/esp_lcd/rgb/include/esp_lcd_panel_rgb.h +++ b/components/esp_lcd/rgb/include/esp_lcd_panel_rgb.h @@ -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 @@ -144,6 +147,7 @@ typedef struct { lcd_color_format_t out_color_format; /*!< Format of the output data (color space and pixel format), which is the format that the panel device can accept */ 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 */ @@ -252,6 +256,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 Configure how to convert the color format between RGB and YUV * diff --git a/components/esp_lcd/test_apps/rgb_lcd/main/test_rgb_panel.c b/components/esp_lcd/test_apps/rgb_lcd/main/test_rgb_panel.c index 1eff5b2d03..bd3aac9201 100644 --- a/components/esp_lcd/test_apps/rgb_lcd/main/test_rgb_panel.c +++ b/components/esp_lcd/test_apps/rgb_lcd/main/test_rgb_panel.c @@ -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, lcd_color_format_t in_color_format, size_t bb_pixels, bool refresh_on_demand, +static esp_lcd_panel_handle_t test_rgb_panel_initialization(size_t data_width, lcd_color_format_t in_color_format, 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, l .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, LCD_COLOR_FMT_RGB565, 0, false, NULL, NULL); + esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 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, LCD_COLOR_FMT_RGB888, 0, false, NULL, NULL); + esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(8, LCD_COLOR_FMT_RGB888, 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, LCD_COLOR_FMT_RGB565, 0, true, test_rgb_panel_trans_done, cur_task); + esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 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, LCD_COLOR_FMT_RGB565, 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, LCD_COLOR_FMT_RGB565, 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, LCD_COLOR_FMT_RGB565, 0, false, NULL, NULL); + esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 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, LCD_COLOR_FMT_RGB565, 0, false, NULL, NULL); + esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 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, LCD_COLOR_FMT_RGB565, 0, false, NULL, NULL); + esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 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, LCD_COLOR_FMT_RGB565, 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, LCD_COLOR_FMT_RGB565, 0, false, test_rgb_panel_count_in_callback, &callback_calls); + esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, LCD_COLOR_FMT_RGB565, 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); diff --git a/docs/en/api-reference/peripherals/lcd/rgb_lcd.rst b/docs/en/api-reference/peripherals/lcd/rgb_lcd.rst index b099a090b9..cb78891514 100644 --- a/docs/en/api-reference/peripherals/lcd/rgb_lcd.rst +++ b/docs/en/api-reference/peripherals/lcd/rgb_lcd.rst @@ -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 diff --git a/docs/zh_CN/api-reference/peripherals/lcd/rgb_lcd.rst b/docs/zh_CN/api-reference/peripherals/lcd/rgb_lcd.rst index 255cffb95b..1fd896b25f 100644 --- a/docs/zh_CN/api-reference/peripherals/lcd/rgb_lcd.rst +++ b/docs/zh_CN/api-reference/peripherals/lcd/rgb_lcd.rst @@ -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 diff --git a/examples/peripherals/lcd/rgb_panel/main/rgb_lcd_example_main.c b/examples/peripherals/lcd/rgb_panel/main/rgb_lcd_example_main.c index 0b0366158b..aedf4c3365 100644 --- a/examples/peripherals/lcd/rgb_panel/main/rgb_lcd_example_main.c +++ b/examples/peripherals/lcd/rgb_panel/main/rgb_lcd_example_main.c @@ -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);