ESP32-S3模组上跑通esp32-camera(16)
接前一篇文章:ESP32-S3模组上跑通esp32-camera(15)
本文内容参考:
esp32-camera入门(基于ESP-IDF)_esp32 camera-CSDN博客
OV5640手册解读-CSDN博客
ESP32_CAM CameraWebServer例程源码解析笔记(一)_void startcameraserver();-CSDN博客
esp32-cam驱动程序阅读 - 哔哩哔哩
特此致谢!
一、OV5640初始化
2. 相机初始化及图像传感器配置
上一回解析完了ll_cam_set_pin函数。本回回到cam_init函数中,继续解析后续代码。为了便于理解和回顾,再次贴出cam_init函数代码,在components\esp32-camera\driver\cam_hal.c中,如下:
esp_err_t cam_init(const camera_config_t *config)
{CAM_CHECK(NULL != config, "config pointer is invalid", ESP_ERR_INVALID_ARG);esp_err_t ret = ESP_OK;cam_obj = (cam_obj_t *)heap_caps_calloc(1, sizeof(cam_obj_t), MALLOC_CAP_DMA);CAM_CHECK(NULL != cam_obj, "lcd_cam object malloc error", ESP_ERR_NO_MEM);cam_obj->swap_data = 0;cam_obj->vsync_pin = config->pin_vsync;cam_obj->vsync_invert = true;ll_cam_set_pin(cam_obj, config);ret = ll_cam_config(cam_obj, config);CAM_CHECK_GOTO(ret == ESP_OK, "ll_cam initialize failed", err);#if CAMERA_DBG_PIN_ENABLEPIN_FUNC_SELECT(GPIO_PIN_MUX_REG[DBG_PIN_NUM], PIN_FUNC_GPIO);gpio_set_direction(DBG_PIN_NUM, GPIO_MODE_OUTPUT);gpio_set_pull_mode(DBG_PIN_NUM, GPIO_FLOATING);
#endifESP_LOGI(TAG, "cam init ok");return ESP_OK;err:free(cam_obj);cam_obj = NULL;return ESP_FAIL;
}
接下来是以下代码片段:
ret = ll_cam_config(cam_obj, config);CAM_CHECK_GOTO(ret == ESP_OK, "ll_cam initialize failed", err);
ll_cam_config函数也在components\esp32-camera\target\esp32s3\ll_cam.c中,代码如下:
esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
{esp_err_t ret = ESP_OK;if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN) == 0) {REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST);REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST);}LCD_CAM.cam_ctrl.val = 0;LCD_CAM.cam_ctrl.cam_clkm_div_b = 0;LCD_CAM.cam_ctrl.cam_clkm_div_a = 0;LCD_CAM.cam_ctrl.cam_clkm_div_num = 160000000 / config->xclk_freq_hz;LCD_CAM.cam_ctrl.cam_clk_sel = 3;//Select Camera module source clock. 0: no clock. 1: APLL. 2: CLK160. 3: no clock.LCD_CAM.cam_ctrl.cam_stop_en = 0;LCD_CAM.cam_ctrl.cam_vsync_filter_thres = 4; // Filter by LCD_CAM clockLCD_CAM.cam_ctrl.cam_update = 0;LCD_CAM.cam_ctrl.cam_byte_order = cam->swap_data;LCD_CAM.cam_ctrl.cam_bit_order = 0;LCD_CAM.cam_ctrl.cam_line_int_en = 0;LCD_CAM.cam_ctrl.cam_vs_eof_en = 0; //1: CAM_VSYNC to generate in_suc_eof. 0: in_suc_eof is controlled by reg_cam_rec_data_cyclelenLCD_CAM.cam_ctrl1.val = 0;LCD_CAM.cam_ctrl1.cam_rec_data_bytelen = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE - 1; // Cannot be assigned to 0, and it is easy to overflowLCD_CAM.cam_ctrl1.cam_line_int_num = 0; // The number of hsyncs that generate hs interruptsLCD_CAM.cam_ctrl1.cam_clk_inv = 0;LCD_CAM.cam_ctrl1.cam_vsync_filter_en = 1;LCD_CAM.cam_ctrl1.cam_2byte_en = 0;LCD_CAM.cam_ctrl1.cam_de_inv = 0;LCD_CAM.cam_ctrl1.cam_hsync_inv = 0;LCD_CAM.cam_ctrl1.cam_vsync_inv = 0;LCD_CAM.cam_ctrl1.cam_vh_de_mode_en = 0;LCD_CAM.cam_rgb_yuv.val = 0;#if CONFIG_CAMERA_CONVERTER_ENABLEDif (config->conv_mode) {ret = ll_cam_converter_config(cam, config);if(ret != ESP_OK) {return ret;}}
#endifLCD_CAM.cam_ctrl.cam_update = 1;LCD_CAM.cam_ctrl1.cam_start = 1;ret = ll_cam_dma_init(cam);return ret;
}
ll_cam_config函数比较长,仍然是一段一段来看。先来看第1段:
if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN) == 0) {REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST);REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST);}
SYSTEM_PERIP_CLK_EN1_REG宏定义在工程目录下搜索是搜不到的,需要在ESP-IDF的安装路径下搜索才能找到。是在soc\esp32s3\include\soc\system_reg.h中,定义如下:
/** SYSTEM_PERIP_CLK_EN1_REG register* peripheral clock gating register*/
#define SYSTEM_PERIP_CLK_EN1_REG (DR_REG_SYSTEM_BASE + 0x14)
SYSTEM_LCD_CAM_CLK_EN宏也是在soc\esp32s3\include\soc\system_reg.h中,定义如下:
/* SYSTEM_LCD_CAM_CLK_EN : R/W ;bitpos:[8] ;default: 1'b0 ; */
/*description: .*/
#define SYSTEM_LCD_CAM_CLK_EN (BIT(8))
#define SYSTEM_LCD_CAM_CLK_EN_M (BIT(8))
#define SYSTEM_LCD_CAM_CLK_EN_V 0x1
#define SYSTEM_LCD_CAM_CLK_EN_S 8
SYSTEM_PERIP_RST_EN1_REG宏也是在soc\esp32s3\include\soc\system_reg.h中,定义如下:
#define SYSTEM_PERIP_RST_EN1_REG (DR_REG_SYSTEM_BASE + 0x24)
SYSTEM_LCD_CAM_RST宏也是在soc\esp32s3\include\soc\system_reg.h中,定义如下:
/* SYSTEM_LCD_CAM_RST : R/W ;bitpos:[8] ;default: 1'b1 ; */
/*description: .*/
#define SYSTEM_LCD_CAM_RST (BIT(8))
#define SYSTEM_LCD_CAM_RST_M (BIT(8))
#define SYSTEM_LCD_CAM_RST_V 0x1
#define SYSTEM_LCD_CAM_RST_S 8
由于这部分内容不是本系列重点,因此这里就不深入分析了,从字面意思上就能知道这段代码的作用:使能ESP32-S3 camera接口的时钟,以及进行复位。
接下来是第2段代码片段:
LCD_CAM.cam_ctrl.val = 0;LCD_CAM.cam_ctrl.cam_clkm_div_b = 0;LCD_CAM.cam_ctrl.cam_clkm_div_a = 0;LCD_CAM.cam_ctrl.cam_clkm_div_num = 160000000 / config->xclk_freq_hz;LCD_CAM.cam_ctrl.cam_clk_sel = 3;//Select Camera module source clock. 0: no clock. 1: APLL. 2: CLK160. 3: no clock.LCD_CAM.cam_ctrl.cam_stop_en = 0;LCD_CAM.cam_ctrl.cam_vsync_filter_thres = 4; // Filter by LCD_CAM clockLCD_CAM.cam_ctrl.cam_update = 0;LCD_CAM.cam_ctrl.cam_byte_order = cam->swap_data;LCD_CAM.cam_ctrl.cam_bit_order = 0;LCD_CAM.cam_ctrl.cam_line_int_en = 0;LCD_CAM.cam_ctrl.cam_vs_eof_en = 0; //1: CAM_VSYNC to generate in_suc_eof. 0: in_suc_eof is controlled by reg_cam_rec_data_cyclelenLCD_CAM.cam_ctrl1.val = 0;LCD_CAM.cam_ctrl1.cam_rec_data_bytelen = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE - 1; // Cannot be assigned to 0, and it is easy to overflowLCD_CAM.cam_ctrl1.cam_line_int_num = 0; // The number of hsyncs that generate hs interruptsLCD_CAM.cam_ctrl1.cam_clk_inv = 0;LCD_CAM.cam_ctrl1.cam_vsync_filter_en = 1;LCD_CAM.cam_ctrl1.cam_2byte_en = 0;LCD_CAM.cam_ctrl1.cam_de_inv = 0;LCD_CAM.cam_ctrl1.cam_hsync_inv = 0;LCD_CAM.cam_ctrl1.cam_vsync_inv = 0;LCD_CAM.cam_ctrl1.cam_vh_de_mode_en = 0;LCD_CAM.cam_rgb_yuv.val = 0;#if CONFIG_CAMERA_CONVERTER_ENABLEDif (config->conv_mode) {ret = ll_cam_converter_config(cam, config);if(ret != ESP_OK) {return ret;}}
#endifLCD_CAM.cam_ctrl.cam_update = 1;LCD_CAM.cam_ctrl1.cam_start = 1;
这个LCD_CAM笔者在当前工程下没有搜索到,满以为在ESP-IDF安装目录下能够搜索到,结果也没有搜索到,只是搜到了一处外部引用代码,在components\soc\esp32s3\include\soc\lcd_cam_struct.h中,如下:
extern lcd_cam_dev_t LCD_CAM;
笔者推测,LCD_CAM可能引自于components\soc\esp32s3\ld\esp32s3.peripherals.ld文件:
而0x60041000正好就是DR_REG_LCD_CAM_BASE,即LCD_CAM系列寄存器的基地址。
LCD_CAM的类型是lcd_cam_dev_t,其在components\soc\esp32s3\include\soc\lcd_cam_struct.h中定义(就在外部引用LCD_CAM那一行的上边),如下:
typedef struct lcd_cam_dev_t {volatile lcd_cam_lcd_clock_reg_t lcd_clock;volatile lcd_cam_cam_ctrl_reg_t cam_ctrl;volatile lcd_cam_cam_ctrl1_reg_t cam_ctrl1;volatile lcd_cam_cam_rgb_yuv_reg_t cam_rgb_yuv;volatile lcd_cam_lcd_rgb_yuv_reg_t lcd_rgb_yuv;volatile lcd_cam_lcd_user_reg_t lcd_user;volatile lcd_cam_lcd_misc_reg_t lcd_misc;volatile lcd_cam_lcd_ctrl_reg_t lcd_ctrl;volatile lcd_cam_lcd_ctrl1_reg_t lcd_ctrl1;volatile lcd_cam_lcd_ctrl2_reg_t lcd_ctrl2;volatile lcd_cam_lcd_cmd_val_reg_t lcd_cmd_val;uint32_t reserved_02c;volatile lcd_cam_lcd_dly_mode_reg_t lcd_dly_mode;uint32_t reserved_034;volatile lcd_cam_lcd_data_dout_mode_reg_t lcd_data_dout_mode;uint32_t reserved_03c[10];volatile lcd_cam_lc_dma_int_ena_reg_t lc_dma_int_ena;volatile lcd_cam_lc_dma_int_raw_reg_t lc_dma_int_raw;volatile lcd_cam_lc_dma_int_st_reg_t lc_dma_int_st;volatile lcd_cam_lc_dma_int_clr_reg_t lc_dma_int_clr;uint32_t reserved_074[34];volatile lcd_cam_lc_reg_date_reg_t lc_reg_date;
} lcd_cam_dev_t;
根据定义的形式以及上边那个基地址,可以推断出,这个lcd_cam_dev_t LCD_CAM实际上就是一个指向LCD_CAM寄存器基地址的值(注意,并不是指针),其各个成员实际就指向了LCD_CAM寄存器组中的各个寄存器(定义是按照顺序来的)。
那么,上边这一段代码片段的意思就是:
LCD_CAM.cam_ctrl.val = 0;LCD_CAM.cam_ctrl.cam_clkm_div_b = 0;LCD_CAM.cam_ctrl.cam_clkm_div_a = 0;LCD_CAM.cam_ctrl.cam_clkm_div_num = 160000000 / config->xclk_freq_hz;LCD_CAM.cam_ctrl.cam_clk_sel = 3;//Select Camera module source clock. 0: no clock. 1: APLL. 2: CLK160. 3: no clock.LCD_CAM.cam_ctrl.cam_stop_en = 0;LCD_CAM.cam_ctrl.cam_vsync_filter_thres = 4; // Filter by LCD_CAM clockLCD_CAM.cam_ctrl.cam_update = 0;LCD_CAM.cam_ctrl.cam_byte_order = cam->swap_data;LCD_CAM.cam_ctrl.cam_bit_order = 0;LCD_CAM.cam_ctrl.cam_line_int_en = 0;LCD_CAM.cam_ctrl.cam_vs_eof_en = 0; //1: CAM_VSYNC to generate in_suc_eof. 0: in_suc_eof is controlled by reg_cam_rec_data_cyclelenLCD_CAM.cam_ctrl1.val = 0;LCD_CAM.cam_ctrl1.cam_rec_data_bytelen = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE - 1; // Cannot be assigned to 0, and it is easy to overflowLCD_CAM.cam_ctrl1.cam_line_int_num = 0; // The number of hsyncs that generate hs interruptsLCD_CAM.cam_ctrl1.cam_clk_inv = 0;LCD_CAM.cam_ctrl1.cam_vsync_filter_en = 1;LCD_CAM.cam_ctrl1.cam_2byte_en = 0;LCD_CAM.cam_ctrl1.cam_de_inv = 0;LCD_CAM.cam_ctrl1.cam_hsync_inv = 0;LCD_CAM.cam_ctrl1.cam_vsync_inv = 0;LCD_CAM.cam_ctrl1.cam_vh_de_mode_en = 0;LCD_CAM.cam_rgb_yuv.val = 0;#if CONFIG_CAMERA_CONVERTER_ENABLEDif (config->conv_mode) {ret = ll_cam_converter_config(cam, config);if(ret != ESP_OK) {return ret;}}
#endifLCD_CAM.cam_ctrl.cam_update = 1;LCD_CAM.cam_ctrl1.cam_start = 1;
给寄存器组中的各个寄存器(的对应位)赋值。
ll_cam_config函数的后续代码,在下一回中继续解析。