Skip to content

Commit a6f13d9

Browse files
feat: add esp32s3 yuv2rgb conversion support (#414)
1 parent 8575d75 commit a6f13d9

File tree

6 files changed

+138
-6
lines changed

6 files changed

+138
-6
lines changed

Kconfig

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,5 +164,24 @@ menu "Camera configuration"
164164
help
165165
Maximum value of DMA buffer
166166
Larger values may fail to allocate due to insufficient contiguous memory blocks, and smaller value may cause DMA interrupt to be too frequent
167+
168+
config CAMERA_CONVERTER_ENABLED
169+
bool "Enable camera RGB/YUV converter"
170+
depends on IDF_TARGET_ESP32S3
171+
default n
172+
help
173+
Enable this option if you want to use RGB565/YUV422/YUV420/YUV411 format conversion.
167174

175+
choice CAMERA_CONV_PROTOCOL
176+
bool "Camera converter protocol"
177+
depends on CAMERA_CONVERTER_ENABLED
178+
default LCD_CAM_CONV_BT601_ENABLED
179+
help
180+
Supports format conversion under both BT601 and BT709 standards.
181+
182+
config LCD_CAM_CONV_BT601_ENABLED
183+
bool "BT601"
184+
config LCD_CAM_CONV_BT709_ENABLED
185+
bool "BT709"
186+
endchoice
168187
endmenu

driver/esp_camera.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,23 @@ static esp_err_t camera_probe(const camera_config_t *config, camera_model_t *out
236236
return ESP_OK;
237237
}
238238

239+
#if CONFIG_CAMERA_CONVERTER_ENABLED
240+
static pixformat_t get_output_data_format(camera_conv_mode_t conv_mode)
241+
{
242+
pixformat_t format = PIXFORMAT_RGB565;
243+
switch (conv_mode) {
244+
case YUV422_TO_YUV420:
245+
format = PIXFORMAT_YUV420;
246+
break;
247+
case YUV422_TO_RGB565: // default format is RGB565
248+
default:
249+
break;
250+
}
251+
ESP_LOGD(TAG, "Convert to %d format enabled", format);
252+
return format;
253+
}
254+
#endif
255+
239256
esp_err_t esp_camera_init(const camera_config_t *config)
240257
{
241258
esp_err_t err;
@@ -274,13 +291,19 @@ esp_err_t esp_camera_init(const camera_config_t *config)
274291

275292
s_state->sensor.status.framesize = frame_size;
276293
s_state->sensor.pixformat = pix_format;
294+
277295
ESP_LOGD(TAG, "Setting frame size to %dx%d", resolution[frame_size].width, resolution[frame_size].height);
278296
if (s_state->sensor.set_framesize(&s_state->sensor, frame_size) != 0) {
279297
ESP_LOGE(TAG, "Failed to set frame size");
280298
err = ESP_ERR_CAMERA_FAILED_TO_SET_FRAME_SIZE;
281299
goto fail;
282300
}
283301
s_state->sensor.set_pixformat(&s_state->sensor, pix_format);
302+
#if CONFIG_CAMERA_CONVERTER_ENABLED
303+
if(config->conv_mode) {
304+
s_state->sensor.pixformat = get_output_data_format(config->conv_mode); // If conversion enabled, change the out data format by conversion mode
305+
}
306+
#endif
284307

285308
if (s_state->sensor.id.PID == OV2640_PID) {
286309
s_state->sensor.set_gainceiling(&s_state->sensor, GAINCEILING_2X);

driver/include/esp_camera.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
#include "driver/ledc.h"
7171
#include "sensor.h"
7272
#include "sys/time.h"
73+
#include "sdkconfig.h"
7374

7475
#ifdef __cplusplus
7576
extern "C" {
@@ -91,6 +92,19 @@ typedef enum {
9192
CAMERA_FB_IN_DRAM /*!< Frame buffer is placed in internal DRAM */
9293
} camera_fb_location_t;
9394

95+
#if CONFIG_CAMERA_CONVERTER_ENABLED
96+
/**
97+
* @brief Camera RGB\YUV conversion mode
98+
*/
99+
typedef enum {
100+
CONV_DISABLE,
101+
RGB565_TO_YUV422,
102+
103+
YUV422_TO_RGB565,
104+
YUV422_TO_YUV420
105+
} camera_conv_mode_t;
106+
#endif
107+
94108
/**
95109
* @brief Configuration structure for camera initialization
96110
*/
@@ -124,6 +138,9 @@ typedef struct {
124138
size_t fb_count; /*!< Number of frame buffers to be allocated. If more than one, then each frame will be acquired (double speed) */
125139
camera_fb_location_t fb_location; /*!< The location where the frame buffer will be allocated */
126140
camera_grab_mode_t grab_mode; /*!< When buffers should be filled */
141+
#if CONFIG_CAMERA_CONVERTER_ENABLED
142+
camera_conv_mode_t conv_mode; /*!< RGB<->YUV Conversion mode */
143+
#endif
127144
} camera_config_t;
128145

129146
/**

driver/include/sensor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ typedef enum {
6969
typedef enum {
7070
PIXFORMAT_RGB565, // 2BPP/RGB565
7171
PIXFORMAT_YUV422, // 2BPP/YUV422
72+
PIXFORMAT_YUV420, // 1.5BPP/YUV420
7273
PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
7374
PIXFORMAT_JPEG, // JPEG/COMPRESSED
7475
PIXFORMAT_RGB888, // 3BPP/RGB888

target/esp32s3/ll_cam.c

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
175175
}
176176

177177
GDMA.channel[cam->dma_num].in.conf1.in_check_owner = 0;
178+
// GDMA.channel[cam->dma_num].in.conf1.in_ext_mem_bk_size = 2;
178179

179180
GDMA.channel[cam->dma_num].in.peri_sel.sel = 5;
180181
//GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15
@@ -183,8 +184,52 @@ static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
183184
return ESP_OK;
184185
}
185186

187+
#if CONFIG_CAMERA_CONVERTER_ENABLED
188+
static esp_err_t ll_cam_converter_config(cam_obj_t *cam, const camera_config_t *config)
189+
{
190+
esp_err_t ret = ESP_OK;
191+
192+
switch (config->conv_mode) {
193+
case YUV422_TO_YUV420:
194+
if (config->pixel_format != PIXFORMAT_YUV422) {
195+
ret = ESP_FAIL;
196+
} else {
197+
ESP_LOGI(TAG, "YUV422 to YUV420 mode");
198+
LCD_CAM.cam_rgb_yuv.cam_conv_yuv2yuv_mode = 1;
199+
LCD_CAM.cam_rgb_yuv.cam_conv_yuv_mode = 0;
200+
LCD_CAM.cam_rgb_yuv.cam_conv_trans_mode = 1;
201+
}
202+
break;
203+
case YUV422_TO_RGB565:
204+
if (config->pixel_format != PIXFORMAT_YUV422) {
205+
ret = ESP_FAIL;
206+
} else {
207+
ESP_LOGI(TAG, "YUV422 to RGB565 mode");
208+
LCD_CAM.cam_rgb_yuv.cam_conv_yuv2yuv_mode = 3;
209+
LCD_CAM.cam_rgb_yuv.cam_conv_yuv_mode = 0;
210+
LCD_CAM.cam_rgb_yuv.cam_conv_trans_mode = 0;
211+
}
212+
break;
213+
default:
214+
break;
215+
}
216+
#if CONFIG_LCD_CAM_CONV_BT709_ENABLED
217+
LCD_CAM.cam_rgb_yuv.cam_conv_protocol_mode = 1;
218+
#else
219+
LCD_CAM.cam_rgb_yuv.cam_conv_protocol_mode = 0;
220+
#endif
221+
LCD_CAM.cam_rgb_yuv.cam_conv_data_out_mode = 0;
222+
LCD_CAM.cam_rgb_yuv.cam_conv_data_in_mode = 0;
223+
LCD_CAM.cam_rgb_yuv.cam_conv_mode_8bits_on = 1;
224+
LCD_CAM.cam_rgb_yuv.cam_conv_bypass = 1;
225+
cam->conv_mode = config->conv_mode;
226+
return ret;
227+
}
228+
#endif
229+
186230
esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
187231
{
232+
esp_err_t ret = ESP_OK;
188233
if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN) == 0) {
189234
REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
190235
REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
@@ -220,15 +265,21 @@ esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
220265

221266
LCD_CAM.cam_rgb_yuv.val = 0;
222267

268+
#if CONFIG_CAMERA_CONVERTER_ENABLED
269+
if (config->conv_mode) {
270+
ret = ll_cam_converter_config(cam, config);
271+
if(ret != ESP_OK) {
272+
return ret;
273+
}
274+
}
275+
#endif
276+
223277
LCD_CAM.cam_ctrl.cam_update = 1;
224278
LCD_CAM.cam_ctrl1.cam_start = 1;
225279

226-
esp_err_t err = ll_cam_dma_init(cam);
227-
if(err != ESP_OK) {
228-
return err;
229-
}
280+
ret = ll_cam_dma_init(cam);
230281

231-
return ESP_OK;
282+
return ret;
232283
}
233284

234285
void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
@@ -422,6 +473,7 @@ size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in,
422473
}
423474
return len / 2;
424475
}
476+
425477

426478
// just memcpy
427479
memcpy(out, in, len);
@@ -438,8 +490,22 @@ esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_
438490
}
439491
cam->fb_bytes_per_pixel = 1; // frame buffer stores Y8
440492
} else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
441-
cam->in_bytes_per_pixel = 2; // camera sends YU/YV
493+
#if CONFIG_CAMERA_CONVERTER_ENABLED
494+
switch (cam->conv_mode) {
495+
case YUV422_TO_YUV420:
496+
cam->in_bytes_per_pixel = 1.5; // for DMA receive
497+
cam->fb_bytes_per_pixel = 1.5; // frame buffer stores YUV420
498+
break;
499+
case YUV422_TO_RGB565:
500+
default:
501+
cam->in_bytes_per_pixel = 2; // for DMA receive
442502
cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
503+
break;
504+
}
505+
#else
506+
cam->in_bytes_per_pixel = 2; // for DMA receive
507+
cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
508+
#endif
443509
} else if (pix_format == PIXFORMAT_JPEG) {
444510
cam->in_bytes_per_pixel = 1;
445511
cam->fb_bytes_per_pixel = 1;

target/private_include/ll_cam.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,14 @@ typedef struct {
116116
//for RGB/YUV modes
117117
uint16_t width;
118118
uint16_t height;
119+
#if CONFIG_CAMERA_CONVERTER_ENABLED
120+
float in_bytes_per_pixel;
121+
float fb_bytes_per_pixel;
122+
camera_conv_mode_t conv_mode;
123+
#else
119124
uint8_t in_bytes_per_pixel;
120125
uint8_t fb_bytes_per_pixel;
126+
#endif
121127
uint32_t fb_size;
122128

123129
cam_state_t state;

0 commit comments

Comments
 (0)