mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/rgb_lcd_yuv_converter' into 'master'
RGB-YUV converter Closes IDF-4598 See merge request espressif/esp-idf!19094
This commit is contained in:
commit
e2634b5223
@ -21,7 +21,8 @@ repos:
|
|||||||
.+test_idf_monitor\/tests\/.+|
|
.+test_idf_monitor\/tests\/.+|
|
||||||
.*_pb2.py|
|
.*_pb2.py|
|
||||||
.*.pb-c.h|
|
.*.pb-c.h|
|
||||||
.*.pb-c.c
|
.*.pb-c.c|
|
||||||
|
.*.yuv
|
||||||
)$
|
)$
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
exclude: *whitespace_excludes
|
exclude: *whitespace_excludes
|
||||||
|
@ -115,7 +115,7 @@ typedef struct {
|
|||||||
lcd_clock_source_t clk_src; /*!< Clock source for the RGB LCD peripheral */
|
lcd_clock_source_t clk_src; /*!< Clock source for the RGB LCD peripheral */
|
||||||
esp_lcd_rgb_timing_t timings; /*!< RGB timing parameters, including the screen resolution */
|
esp_lcd_rgb_timing_t timings; /*!< RGB timing parameters, including the screen resolution */
|
||||||
size_t data_width; /*!< Number of data lines */
|
size_t data_width; /*!< Number of data lines */
|
||||||
size_t bits_per_pixel; /*!< Color depth, in bpp, specially, if set to zero, it will default to `data_width`.
|
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` */
|
When using a Serial RGB interface, this value could be different from `data_width` */
|
||||||
size_t bounce_buffer_size_px; /*!< If it's non-zero, the driver allocates two DRAM bounce buffers for DMA use.
|
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. */
|
DMA fetching from DRAM bounce buffer is much faster than PSRAM frame buffer. */
|
||||||
@ -209,6 +209,39 @@ 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);
|
esp_err_t esp_lcd_rgb_panel_refresh(esp_lcd_panel_handle_t panel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LCD color conversion profile
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
lcd_color_space_t color_space; /*!< Color space of the image */
|
||||||
|
lcd_color_range_t color_range; /*!< Color range of the image */
|
||||||
|
lcd_yuv_sample_t yuv_sample; /*!< YUV sample format of the image */
|
||||||
|
} esp_lcd_color_conv_profile_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configuration of YUG-RGB conversion
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
lcd_yuv_conv_std_t std; /*!< YUV conversion standard: BT601, BT709 */
|
||||||
|
esp_lcd_color_conv_profile_t src; /*!< Color conversion profile of the input image */
|
||||||
|
esp_lcd_color_conv_profile_t dst; /*!< Color conversion profile of the output image */
|
||||||
|
} esp_lcd_yuv_conv_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configure how to convert the color format between RGB and YUV
|
||||||
|
*
|
||||||
|
* @note Pass in `config` as NULL will disable the RGB-YUV converter.
|
||||||
|
* @note The hardware converter can only parse a "packed" storage format, while "planar" and "semi-planar" format is not supported.
|
||||||
|
*
|
||||||
|
* @param[in] panel LCD panel handle, returned from `esp_lcd_new_rgb_panel`
|
||||||
|
* @param[in] config Configuration of RGB-YUV conversion
|
||||||
|
* @return
|
||||||
|
* - ESP_ERR_INVALID_ARG: Configure RGB-YUV conversion failed because of invalid argument
|
||||||
|
* - ESP_ERR_NOT_SUPPORTED: Configure RGB-YUV conversion failed because the conversion mode is not supported by the hardware
|
||||||
|
* - ESP_OK: Configure RGB-YUV conversion successfully
|
||||||
|
*/
|
||||||
|
esp_err_t esp_lcd_rgb_panel_set_yuv_conversion(esp_lcd_panel_handle_t panel, const esp_lcd_yuv_conv_config_t *config);
|
||||||
|
|
||||||
#endif // SOC_LCD_RGB_SUPPORTED
|
#endif // SOC_LCD_RGB_SUPPORTED
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -83,7 +83,8 @@ struct esp_rgb_panel_t {
|
|||||||
int panel_id; // LCD panel ID
|
int panel_id; // LCD panel ID
|
||||||
lcd_hal_context_t hal; // Hal layer object
|
lcd_hal_context_t hal; // Hal layer object
|
||||||
size_t data_width; // Number of data lines
|
size_t data_width; // Number of data lines
|
||||||
size_t bits_per_pixel; // Color depth, in bpp
|
size_t fb_bits_per_pixel; // Frame buffer color depth, in bpp
|
||||||
|
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 sram_trans_align; // Alignment for framebuffer that allocated in SRAM
|
||||||
size_t psram_trans_align; // Alignment for framebuffer that allocated in PSRAM
|
size_t psram_trans_align; // Alignment for framebuffer that allocated in PSRAM
|
||||||
int disp_gpio_num; // Display control GPIO, which is used to perform action like "disp_off"
|
int disp_gpio_num; // Display control GPIO, which is used to perform action like "disp_off"
|
||||||
@ -220,13 +221,13 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// bpp defaults to the number of data lines, but for serial RGB interface, they're not equal
|
// bpp defaults to the number of data lines, but for serial RGB interface, they're not equal
|
||||||
size_t bits_per_pixel = rgb_panel_config->data_width;
|
size_t fb_bits_per_pixel = rgb_panel_config->data_width;
|
||||||
if (rgb_panel_config->bits_per_pixel) { // override bpp if it's set
|
if (rgb_panel_config->bits_per_pixel) { // override bpp if it's set
|
||||||
bits_per_pixel = rgb_panel_config->bits_per_pixel;
|
fb_bits_per_pixel = rgb_panel_config->bits_per_pixel;
|
||||||
}
|
}
|
||||||
// calculate buffer size
|
// calculate buffer size
|
||||||
size_t fb_size = rgb_panel_config->timings.h_res * rgb_panel_config->timings.v_res * bits_per_pixel / 8;
|
size_t fb_size = rgb_panel_config->timings.h_res * rgb_panel_config->timings.v_res * fb_bits_per_pixel / 8;
|
||||||
size_t bb_size = rgb_panel_config->bounce_buffer_size_px * bits_per_pixel / 8;
|
size_t bb_size = rgb_panel_config->bounce_buffer_size_px * fb_bits_per_pixel / 8;
|
||||||
if (bb_size) {
|
if (bb_size) {
|
||||||
// we want the bounce can always end in the second buffer
|
// we want the bounce can always end in the second buffer
|
||||||
ESP_GOTO_ON_FALSE(fb_size % (2 * bb_size) == 0, ESP_ERR_INVALID_ARG, err, TAG,
|
ESP_GOTO_ON_FALSE(fb_size % (2 * bb_size) == 0, ESP_ERR_INVALID_ARG, err, TAG,
|
||||||
@ -297,7 +298,8 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
|
|||||||
memcpy(rgb_panel->data_gpio_nums, rgb_panel_config->data_gpio_nums, SOC_LCD_RGB_DATA_WIDTH);
|
memcpy(rgb_panel->data_gpio_nums, rgb_panel_config->data_gpio_nums, SOC_LCD_RGB_DATA_WIDTH);
|
||||||
rgb_panel->timings = rgb_panel_config->timings;
|
rgb_panel->timings = rgb_panel_config->timings;
|
||||||
rgb_panel->data_width = rgb_panel_config->data_width;
|
rgb_panel->data_width = rgb_panel_config->data_width;
|
||||||
rgb_panel->bits_per_pixel = bits_per_pixel;
|
rgb_panel->fb_bits_per_pixel = fb_bits_per_pixel;
|
||||||
|
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->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.disp_en_level = !rgb_panel_config->flags.disp_active_low;
|
||||||
rgb_panel->flags.no_fb = rgb_panel_config->flags.no_fb;
|
rgb_panel->flags.no_fb = rgb_panel_config->flags.no_fb;
|
||||||
@ -387,6 +389,57 @@ esp_err_t esp_lcd_rgb_panel_refresh(esp_lcd_panel_handle_t panel)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_lcd_rgb_panel_set_yuv_conversion(esp_lcd_panel_handle_t panel, const esp_lcd_yuv_conv_config_t *config)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(panel, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base);
|
||||||
|
lcd_hal_context_t *hal = &rgb_panel->hal;
|
||||||
|
bool en_conversion = config != NULL;
|
||||||
|
|
||||||
|
// bits per pixel for different YUV sample
|
||||||
|
const uint8_t bpp_yuv[] = {
|
||||||
|
[LCD_YUV_SAMPLE_422] = 16,
|
||||||
|
[LCD_YUV_SAMPLE_420] = 12,
|
||||||
|
[LCD_YUV_SAMPLE_411] = 12,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (en_conversion) {
|
||||||
|
if (memcmp(&config->src, &config->dst, sizeof(config->src)) == 0) {
|
||||||
|
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "conversion source and destination are the same");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->src.color_space == LCD_COLOR_SPACE_YUV && config->dst.color_space == LCD_COLOR_SPACE_RGB) { // YUV->RGB
|
||||||
|
lcd_ll_set_convert_mode_yuv_to_rgb(hal->dev, config->src.yuv_sample);
|
||||||
|
// Note, the RGB->YUV conversion only support RGB565
|
||||||
|
rgb_panel->output_bits_per_pixel = 16;
|
||||||
|
} else if (config->src.color_space == LCD_COLOR_SPACE_RGB && config->dst.color_space == LCD_COLOR_SPACE_YUV) { // RGB->YUV
|
||||||
|
lcd_ll_set_convert_mode_rgb_to_yuv(hal->dev, config->dst.yuv_sample);
|
||||||
|
rgb_panel->output_bits_per_pixel = bpp_yuv[config->dst.yuv_sample];
|
||||||
|
} else if (config->src.color_space == LCD_COLOR_SPACE_YUV && config->dst.color_space == LCD_COLOR_SPACE_YUV) { // YUV->YUV
|
||||||
|
lcd_ll_set_convert_mode_yuv_to_yuv(hal->dev, config->src.yuv_sample, config->dst.yuv_sample);
|
||||||
|
rgb_panel->output_bits_per_pixel = bpp_yuv[config->dst.yuv_sample];
|
||||||
|
} else {
|
||||||
|
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "unsupported conversion mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
// set conversion standard
|
||||||
|
lcd_ll_set_yuv_convert_std(hal->dev, config->std);
|
||||||
|
// set conversion data width
|
||||||
|
lcd_ll_set_convert_data_width(hal->dev, rgb_panel->data_width);
|
||||||
|
// set color range
|
||||||
|
lcd_ll_set_input_color_range(hal->dev, config->src.color_range);
|
||||||
|
lcd_ll_set_output_color_range(hal->dev, config->dst.color_range);
|
||||||
|
} else {
|
||||||
|
// output bpp equals to frame buffer bpp
|
||||||
|
rgb_panel->output_bits_per_pixel = rgb_panel->fb_bits_per_pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable or disable RGB-YUV conversion
|
||||||
|
lcd_ll_enable_rgb_yuv_convert(hal->dev, en_conversion);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static esp_err_t rgb_panel_del(esp_lcd_panel_t *panel)
|
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);
|
esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base);
|
||||||
@ -425,7 +478,7 @@ static esp_err_t rgb_panel_init(esp_lcd_panel_t *panel)
|
|||||||
// configure blank region timing
|
// configure blank region timing
|
||||||
lcd_ll_set_blank_cycles(rgb_panel->hal.dev, 1, 1); // RGB panel always has a front and back blank (porch region)
|
lcd_ll_set_blank_cycles(rgb_panel->hal.dev, 1, 1); // RGB panel always has a front and back blank (porch region)
|
||||||
lcd_ll_set_horizontal_timing(rgb_panel->hal.dev, rgb_panel->timings.hsync_pulse_width,
|
lcd_ll_set_horizontal_timing(rgb_panel->hal.dev, rgb_panel->timings.hsync_pulse_width,
|
||||||
rgb_panel->timings.hsync_back_porch, rgb_panel->timings.h_res * rgb_panel->bits_per_pixel / rgb_panel->data_width,
|
rgb_panel->timings.hsync_back_porch, rgb_panel->timings.h_res * rgb_panel->output_bits_per_pixel / rgb_panel->data_width,
|
||||||
rgb_panel->timings.hsync_front_porch);
|
rgb_panel->timings.hsync_front_porch);
|
||||||
lcd_ll_set_vertical_timing(rgb_panel->hal.dev, rgb_panel->timings.vsync_pulse_width,
|
lcd_ll_set_vertical_timing(rgb_panel->hal.dev, rgb_panel->timings.vsync_pulse_width,
|
||||||
rgb_panel->timings.vsync_back_porch, rgb_panel->timings.v_res,
|
rgb_panel->timings.vsync_back_porch, rgb_panel->timings.v_res,
|
||||||
@ -500,7 +553,7 @@ static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
|
|||||||
y_end = MIN(y_end, v_res);
|
y_end = MIN(y_end, v_res);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bytes_per_pixel = rgb_panel->bits_per_pixel / 8;
|
int bytes_per_pixel = rgb_panel->fb_bits_per_pixel / 8;
|
||||||
int pixels_per_line = rgb_panel->timings.h_res;
|
int pixels_per_line = rgb_panel->timings.h_res;
|
||||||
uint32_t bytes_per_line = bytes_per_pixel * pixels_per_line;
|
uint32_t bytes_per_line = bytes_per_pixel * pixels_per_line;
|
||||||
uint8_t *fb = rgb_panel->fbs[rgb_panel->cur_fb_index];
|
uint8_t *fb = rgb_panel->fbs[rgb_panel->cur_fb_index];
|
||||||
@ -862,7 +915,7 @@ static esp_err_t lcd_rgb_panel_select_clock_src(esp_rgb_panel_t *panel, lcd_cloc
|
|||||||
static IRAM_ATTR bool lcd_rgb_panel_fill_bounce_buffer(esp_rgb_panel_t *panel, uint8_t *buffer)
|
static IRAM_ATTR bool lcd_rgb_panel_fill_bounce_buffer(esp_rgb_panel_t *panel, uint8_t *buffer)
|
||||||
{
|
{
|
||||||
bool need_yield = false;
|
bool need_yield = false;
|
||||||
int bytes_per_pixel = panel->bits_per_pixel / 8;
|
int bytes_per_pixel = panel->fb_bits_per_pixel / 8;
|
||||||
if (panel->flags.no_fb) {
|
if (panel->flags.no_fb) {
|
||||||
if (panel->on_bounce_empty) {
|
if (panel->on_bounce_empty) {
|
||||||
// We don't have a frame buffer here; we need to call a callback to refill the bounce buffer
|
// We don't have a frame buffer here; we need to call a callback to refill the bounce buffer
|
||||||
|
@ -4,6 +4,9 @@ cmake_minimum_required(VERSION 3.16)
|
|||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
project(rgb_lcd_panel_test)
|
project(rgb_lcd_panel_test)
|
||||||
|
|
||||||
|
target_add_binary_data(rgb_lcd_panel_test.elf "resources/pictures/hello.yuv" BINARY)
|
||||||
|
target_add_binary_data(rgb_lcd_panel_test.elf "resources/pictures/world.yuv" BINARY)
|
||||||
|
|
||||||
if(CONFIG_COMPILER_DUMP_RTL_FILES)
|
if(CONFIG_COMPILER_DUMP_RTL_FILES)
|
||||||
add_custom_target(check_test_app_sections ALL
|
add_custom_target(check_test_app_sections ALL
|
||||||
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
|
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
set(srcs "test_app_main.c"
|
set(srcs "test_app_main.c"
|
||||||
"test_rgb_panel.c")
|
"test_rgb_panel.c")
|
||||||
|
|
||||||
|
if(CONFIG_SOC_LCD_SUPPORT_RGB_YUV_CONV)
|
||||||
|
list(APPEND srcs "test_yuv_rgb_conv.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||||
# the component can be registered as WHOLE_ARCHIVE
|
# the component can be registered as WHOLE_ARCHIVE
|
||||||
idf_component_register(SRCS ${srcs}
|
idf_component_register(SRCS ${srcs}
|
||||||
|
105
components/esp_lcd/test_apps/rgb_lcd/main/test_yuv_rgb_conv.c
Normal file
105
components/esp_lcd/test_apps/rgb_lcd/main/test_yuv_rgb_conv.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "unity.h"
|
||||||
|
#include "esp_lcd_panel_rgb.h"
|
||||||
|
#include "esp_lcd_panel_ops.h"
|
||||||
|
#include "esp_random.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "spi_flash_mmap.h"
|
||||||
|
#include "test_rgb_board.h"
|
||||||
|
|
||||||
|
#define TEST_IMG_SIZE (320 * 320 * sizeof(uint16_t))
|
||||||
|
|
||||||
|
// YUV images are embedded in the firmware binary
|
||||||
|
extern const uint8_t image_hello_yuv_start[] asm("_binary_hello_yuv_start");
|
||||||
|
extern const uint8_t image_hello_yuv_end[] asm("_binary_hello_yuv_end");
|
||||||
|
extern const uint8_t image_world_yuv_start[] asm("_binary_world_yuv_start");
|
||||||
|
extern const uint8_t image_world_yuv_end[] asm("_binary_world_yuv_end");
|
||||||
|
|
||||||
|
TEST_CASE("lcd_rgb_panel_yuv422_conversion", "[lcd]")
|
||||||
|
{
|
||||||
|
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||||
|
esp_lcd_rgb_panel_config_t panel_config = {
|
||||||
|
.data_width = 16,
|
||||||
|
.psram_trans_align = 64,
|
||||||
|
.bits_per_pixel = 16, // YUV422: 16bits per pixel
|
||||||
|
.clk_src = LCD_CLK_SRC_DEFAULT,
|
||||||
|
.disp_gpio_num = TEST_LCD_DISP_EN_GPIO,
|
||||||
|
.pclk_gpio_num = TEST_LCD_PCLK_GPIO,
|
||||||
|
.vsync_gpio_num = TEST_LCD_VSYNC_GPIO,
|
||||||
|
.hsync_gpio_num = TEST_LCD_HSYNC_GPIO,
|
||||||
|
.de_gpio_num = TEST_LCD_DE_GPIO,
|
||||||
|
.data_gpio_nums = {
|
||||||
|
TEST_LCD_DATA0_GPIO,
|
||||||
|
TEST_LCD_DATA1_GPIO,
|
||||||
|
TEST_LCD_DATA2_GPIO,
|
||||||
|
TEST_LCD_DATA3_GPIO,
|
||||||
|
TEST_LCD_DATA4_GPIO,
|
||||||
|
TEST_LCD_DATA5_GPIO,
|
||||||
|
TEST_LCD_DATA6_GPIO,
|
||||||
|
TEST_LCD_DATA7_GPIO,
|
||||||
|
TEST_LCD_DATA8_GPIO,
|
||||||
|
TEST_LCD_DATA9_GPIO,
|
||||||
|
TEST_LCD_DATA10_GPIO,
|
||||||
|
TEST_LCD_DATA11_GPIO,
|
||||||
|
TEST_LCD_DATA12_GPIO,
|
||||||
|
TEST_LCD_DATA13_GPIO,
|
||||||
|
TEST_LCD_DATA14_GPIO,
|
||||||
|
TEST_LCD_DATA15_GPIO,
|
||||||
|
},
|
||||||
|
.timings = {
|
||||||
|
.pclk_hz = TEST_LCD_PIXEL_CLOCK_HZ,
|
||||||
|
.h_res = TEST_LCD_H_RES,
|
||||||
|
.v_res = TEST_LCD_V_RES,
|
||||||
|
.hsync_back_porch = 68,
|
||||||
|
.hsync_front_porch = 20,
|
||||||
|
.hsync_pulse_width = 5,
|
||||||
|
.vsync_back_porch = 18,
|
||||||
|
.vsync_front_porch = 4,
|
||||||
|
.vsync_pulse_width = 1,
|
||||||
|
},
|
||||||
|
.flags.fb_in_psram = 1, // allocate frame buffer in PSRAM
|
||||||
|
};
|
||||||
|
|
||||||
|
printf("Create RGB LCD panel\r\n");
|
||||||
|
TEST_ESP_OK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
|
||||||
|
TEST_ESP_OK(esp_lcd_panel_reset(panel_handle));
|
||||||
|
|
||||||
|
printf("Set YUV-RGB conversion profile\r\n");
|
||||||
|
esp_lcd_yuv_conv_config_t conv_config = {
|
||||||
|
.std = LCD_YUV_CONV_STD_BT601,
|
||||||
|
.src = {
|
||||||
|
.color_range = LCD_COLOR_RANGE_FULL,
|
||||||
|
.color_space = LCD_COLOR_SPACE_RGB,
|
||||||
|
},
|
||||||
|
.dst = {
|
||||||
|
.color_range = LCD_COLOR_RANGE_FULL,
|
||||||
|
.color_space = LCD_COLOR_SPACE_RGB,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_lcd_rgb_panel_set_yuv_conversion(panel_handle, &conv_config));
|
||||||
|
|
||||||
|
conv_config.src.color_space = LCD_COLOR_SPACE_YUV;
|
||||||
|
conv_config.src.yuv_sample = LCD_YUV_SAMPLE_422;
|
||||||
|
TEST_ESP_OK(esp_lcd_rgb_panel_set_yuv_conversion(panel_handle, &conv_config));
|
||||||
|
|
||||||
|
TEST_ESP_OK(esp_lcd_panel_init(panel_handle));
|
||||||
|
|
||||||
|
printf("Draw YUV images\r\n");
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 320, 320, image_hello_yuv_start));
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
TEST_ESP_OK(esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, 320, 320, image_world_yuv_start));
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
|
||||||
|
}
|
5
components/esp_lcd/test_apps/rgb_lcd/resources/README.md
Normal file
5
components/esp_lcd/test_apps/rgb_lcd/resources/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# How to generate the YUV image from the PNG image
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ffmpeg -i hello.png -pix_fmt uyvy422 hello.yuv
|
||||||
|
```
|
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
File diff suppressed because one or more lines are too long
@ -28,6 +28,10 @@ extern "C" {
|
|||||||
#define LCD_LL_CLK_FRAC_DIV_AB_MAX 64 // LCD_CLK = LCD_CLK_S / (N + b/a), the a/b register is 6 bit-width
|
#define LCD_LL_CLK_FRAC_DIV_AB_MAX 64 // LCD_CLK = LCD_CLK_S / (N + b/a), the a/b register is 6 bit-width
|
||||||
#define LCD_LL_PCLK_DIV_MAX 64 // LCD_PCLK = LCD_CLK / MO, the MO register is 6 bit-width
|
#define LCD_LL_PCLK_DIV_MAX 64 // LCD_PCLK = LCD_CLK / MO, the MO register is 6 bit-width
|
||||||
|
|
||||||
|
#define LCD_LL_COLOR_RANGE_TO_REG(range) (uint8_t[]){0,1}[(range)]
|
||||||
|
#define LCD_LL_CONV_STD_TO_REG(std) (uint8_t[]){0,1}[(std)]
|
||||||
|
#define LCD_LL_YUV_SAMPLE_TO_REG(sample) (uint8_t[]){0,1,2}[(sample)]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Enable clock gating
|
* @brief Enable clock gating
|
||||||
*
|
*
|
||||||
@ -144,6 +148,92 @@ static inline void lcd_ll_enable_rgb_yuv_convert(lcd_cam_dev_t *dev, bool en)
|
|||||||
dev->lcd_rgb_yuv.lcd_conv_bypass = en;
|
dev->lcd_rgb_yuv.lcd_conv_bypass = en;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set convert data line width
|
||||||
|
*
|
||||||
|
* @param dev LCD register base address
|
||||||
|
* @param width data line width (8 or 16)
|
||||||
|
*/
|
||||||
|
static inline void lcd_ll_set_convert_data_width(lcd_cam_dev_t *dev, uint32_t width)
|
||||||
|
{
|
||||||
|
HAL_ASSERT(width == 8 || width == 16);
|
||||||
|
dev->lcd_rgb_yuv.lcd_conv_mode_8bits_on = (width == 8) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the color range of input data
|
||||||
|
*
|
||||||
|
* @param dev LCD register base address
|
||||||
|
* @param range Color range
|
||||||
|
*/
|
||||||
|
static inline void lcd_ll_set_input_color_range(lcd_cam_dev_t *dev, lcd_color_range_t range)
|
||||||
|
{
|
||||||
|
dev->lcd_rgb_yuv.lcd_conv_data_in_mode = LCD_LL_COLOR_RANGE_TO_REG(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the color range of output data
|
||||||
|
*
|
||||||
|
* @param dev LCD register base address
|
||||||
|
* @param range Color range
|
||||||
|
*/
|
||||||
|
static inline void lcd_ll_set_output_color_range(lcd_cam_dev_t *dev, lcd_color_range_t range)
|
||||||
|
{
|
||||||
|
dev->lcd_rgb_yuv.lcd_conv_data_out_mode = LCD_LL_COLOR_RANGE_TO_REG(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set YUV conversion standard
|
||||||
|
*
|
||||||
|
* @param dev LCD register base address
|
||||||
|
* @param std YUV conversion standard
|
||||||
|
*/
|
||||||
|
static inline void lcd_ll_set_yuv_convert_std(lcd_cam_dev_t *dev, lcd_yuv_conv_std_t std)
|
||||||
|
{
|
||||||
|
dev->lcd_rgb_yuv.lcd_conv_protocol_mode = LCD_LL_CONV_STD_TO_REG(std);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the converter mode: RGB565 to YUV
|
||||||
|
*
|
||||||
|
* @param dev LCD register base address
|
||||||
|
* @param yuv_sample YUV sample mode
|
||||||
|
*/
|
||||||
|
static inline void lcd_ll_set_convert_mode_rgb_to_yuv(lcd_cam_dev_t *dev, lcd_yuv_sample_t yuv_sample)
|
||||||
|
{
|
||||||
|
dev->lcd_rgb_yuv.lcd_conv_trans_mode = 1;
|
||||||
|
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = LCD_LL_YUV_SAMPLE_TO_REG(yuv_sample);
|
||||||
|
dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the converter mode: YUV to RGB565
|
||||||
|
*
|
||||||
|
* @param dev LCD register base address
|
||||||
|
* @param yuv_sample YUV sample mode
|
||||||
|
*/
|
||||||
|
static inline void lcd_ll_set_convert_mode_yuv_to_rgb(lcd_cam_dev_t *dev, lcd_yuv_sample_t yuv_sample)
|
||||||
|
{
|
||||||
|
dev->lcd_rgb_yuv.lcd_conv_trans_mode = 0;
|
||||||
|
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = LCD_LL_YUV_SAMPLE_TO_REG(yuv_sample);
|
||||||
|
dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the converter mode: YUV to YUV
|
||||||
|
*
|
||||||
|
* @param dev LCD register base address
|
||||||
|
* @param src_sample Source YUV sample mode
|
||||||
|
* @param dst_sample Destination YUV sample mode
|
||||||
|
*/
|
||||||
|
static inline void lcd_ll_set_convert_mode_yuv_to_yuv(lcd_cam_dev_t *dev, lcd_yuv_sample_t src_sample, lcd_yuv_sample_t dst_sample)
|
||||||
|
{
|
||||||
|
HAL_ASSERT(src_sample != dst_sample);
|
||||||
|
dev->lcd_rgb_yuv.lcd_conv_trans_mode = 1;
|
||||||
|
dev->lcd_rgb_yuv.lcd_conv_yuv_mode = LCD_LL_YUV_SAMPLE_TO_REG(src_sample);
|
||||||
|
dev->lcd_rgb_yuv.lcd_conv_yuv2yuv_mode = LCD_LL_YUV_SAMPLE_TO_REG(dst_sample);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set clock cycles of each transaction phases
|
* @brief Set clock cycles of each transaction phases
|
||||||
*
|
*
|
||||||
|
@ -28,6 +28,39 @@ typedef enum {
|
|||||||
LCD_RGB_ENDIAN_BGR, /*!< RGB data endian: BGR */
|
LCD_RGB_ENDIAN_BGR, /*!< RGB data endian: BGR */
|
||||||
} lcd_color_rgb_endian_t;
|
} lcd_color_rgb_endian_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LCD color space
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LCD_COLOR_SPACE_RGB, /*!< Color space: RGB */
|
||||||
|
LCD_COLOR_SPACE_YUV, /*!< Color space: YUV */
|
||||||
|
} lcd_color_space_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LCD color range
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LCD_COLOR_RANGE_LIMIT, /*!< Limited color range */
|
||||||
|
LCD_COLOR_RANGE_FULL, /*!< Full color range */
|
||||||
|
} lcd_color_range_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief YUV sampling method
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LCD_YUV_SAMPLE_422, /*!< YUV 4:2:2 sampling */
|
||||||
|
LCD_YUV_SAMPLE_420, /*!< YUV 4:2:0 sampling */
|
||||||
|
LCD_YUV_SAMPLE_411, /*!< YUV 4:1:1 sampling */
|
||||||
|
} lcd_yuv_sample_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The standard used for conversion between RGB and YUV
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LCD_YUV_CONV_STD_BT601, /*!< YUV<->RGB conversion standard: BT.601 */
|
||||||
|
LCD_YUV_CONV_STD_BT709, /*!< YUV<->RGB conversion standard: BT.709 */
|
||||||
|
} lcd_yuv_conv_std_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -603,6 +603,10 @@ config SOC_LCD_RGB_DATA_WIDTH
|
|||||||
int
|
int
|
||||||
default 16
|
default 16
|
||||||
|
|
||||||
|
config SOC_LCD_SUPPORT_RGB_YUV_CONV
|
||||||
|
bool
|
||||||
|
default y
|
||||||
|
|
||||||
config SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH
|
config SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH
|
||||||
int
|
int
|
||||||
default 128
|
default 128
|
||||||
|
@ -240,6 +240,7 @@
|
|||||||
#define SOC_LCD_RGB_PANELS (1U) /*!< Support one RGB LCD panel */
|
#define SOC_LCD_RGB_PANELS (1U) /*!< Support one RGB LCD panel */
|
||||||
#define SOC_LCD_I80_BUS_WIDTH (16) /*!< Intel 8080 bus width */
|
#define SOC_LCD_I80_BUS_WIDTH (16) /*!< Intel 8080 bus width */
|
||||||
#define SOC_LCD_RGB_DATA_WIDTH (16) /*!< Number of LCD data lines */
|
#define SOC_LCD_RGB_DATA_WIDTH (16) /*!< Number of LCD data lines */
|
||||||
|
#define SOC_LCD_SUPPORT_RGB_YUV_CONV (1) /*!< Support color format conversion between RGB and YUV */
|
||||||
|
|
||||||
/*-------------------------- RTC CAPS --------------------------------------*/
|
/*-------------------------- RTC CAPS --------------------------------------*/
|
||||||
#define SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH (128)
|
#define SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH (128)
|
||||||
|
Loading…
Reference in New Issue
Block a user