mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user