图35.3.1.1 CAMERA实验程序流程图
35.3.2 CAMERA函数解析
本章实验要使用到乐鑫官方的esp32-camera驱动库,此驱动库承载ESP32系列Soc兼容的图像传感器驱动程序。此外,它还提供了一些工具,允许将捕获的帧数据转换为更常见的BMP和JPEG格式。要使用此功能,需要导入必要的头文件:
#include "esp_camera.h"
接下来,作者将介绍一些常用的ESP32-S3中的CAMERA函数,这些函数的描述及其作用如下:
1,初始化摄像头驱动
该函数用于检测并配置摄像头,其函数原型如下所示:
esp_err_t esp_camera_init(const camera_config_t *config);
该函数的形参描述,如下表所示:
表35.3.2.1 函数esp_camera_init ()形参描述
该函数的返回值描述,如下表所示:
表35.3.2.2 函数esp_camera_init ()
返回值描述该函数使用camera_config_t类型的结构体变量传入,该结构体的定义如下所示:
| 成员变量 | |
| | |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| 像素数据格式 PIXFORMAT_+YUV422| GRAYSCALE| RGB565| JPEG |
| 输出图像大小 FRAMESIZE_+QVGA| CIF| VGA| SVGA| XGA| SXGA| UXGA |
| |
| 要分配的帧缓冲区数。如果不止一个,则将获取每个帧(双倍速度) |
| |
| |
| |
表35.3.2.3 camera_config_t结构体参数值描述
完成上述结构体参数配置之后,可以将结构传递给 esp_camera_init () 函数,用以实例化CAMERA。
2,获取摄像头图像传感器
该函数用于获取指向图像传感器控制结构的指针,其函数原型如下所示:
sensor_t * esp_camera_sensor_get(void);
该函数的形参描述,如下表所示:
表35.3.2.4 函数esp_camera_sensor_get ()形参描述
该函数的返回值描述,如下表所示:
表35.3.2.5 函数esp_camera_sensor_get()返回值描述
35.3.3 CAMERA驱动解析
在IDF版的25_1_camera例程中,作者在25_1_camera \components\BSP路径下新增了一个CAMERA文件夹以及25_1_camera \components\esp32-camera路径下新增了一个乐鑫官方的esp32-camera驱动库,分别用于存放camera.c、camera.h和esp32-camera库文件。其中,camera.h负责声明CAMERA相关的函数和变量,而camera.c和esp32-camera库文件则实现了CAMERA的驱动。下面,我们将详细解析这几个文件的实现内容。
1,camera.h文件
/* 引脚声明 */
#define CAM_PIN_PWDN GPIO_NUM_NC
#define CAM_PIN_RESET GPIO_NUM_NC
#define CAM_PIN_XCLK GPIO_NUM_NC
#define CAM_PIN_SIOD GPIO_NUM_39
#define CAM_PIN_SIOC GPIO_NUM_38
#define CAM_PIN_D7 GPIO_NUM_18
#define CAM_PIN_D6 GPIO_NUM_17
#define CAM_PIN_D5 GPIO_NUM_16
#define CAM_PIN_D4 GPIO_NUM_15
#define CAM_PIN_D3 GPIO_NUM_7
#define CAM_PIN_D2 GPIO_NUM_6
#define CAM_PIN_D1 GPIO_NUM_5
#define CAM_PIN_D0 GPIO_NUM_4
#define CAM_PIN_VSYNC GPIO_NUM_47
#define CAM_PIN_HREF GPIO_NUM_48
#define CAM_PIN_PCLK GPIO_NUM_45
#define CAM_PWDN(x) do{ x ? \
(xl9555_pin_write(OV_PWDN_IO, 1)): \
(xl9555_pin_write(OV_PWDN_IO, 0)); \
}while(0)
#define CAM_RST(x) do{ x ? \
(xl9555_pin_write(OV_RESET_IO, 1)): \
(xl9555_pin_write(OV_RESET_IO, 0)); \
}while(0)
2,camera.c文件
/* 摄像头配置 */
static camera_config_t camera_config = {
/* 引脚配置 */
.pin_pwdn = CAM_PIN_PWDN,
.pin_reset = CAM_PIN_RESET,
.pin_xclk = CAM_PIN_XCLK,
.pin_sccb_sda = CAM_PIN_SIOD,
.pin_sccb_scl = CAM_PIN_SIOC,
.pin_d7 = CAM_PIN_D7,
.pin_d6 = CAM_PIN_D6,
.pin_d5 = CAM_PIN_D5,
.pin_d4 = CAM_PIN_D4,
.pin_d3 = CAM_PIN_D3,
.pin_d2 = CAM_PIN_D2,
.pin_d1 = CAM_PIN_D1,
.pin_d0 = CAM_PIN_D0,
.pin_vsync = CAM_PIN_VSYNC,
.pin_href = CAM_PIN_HREF,
.pin_pclk = CAM_PIN_PCLK,
/* 图像配置 */
.xclk_freq_hz = 24000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.fb_location = CAMERA_FB_IN_PSRAM,
/* 图像输出模式 */
.pixel_format = PIXFORMAT_RGB565,
/* 图像输出大小 */
.frame_size = FRAMESIZE_QVGA,
/* 0-63,对于OV系列相机传感器,数量越少意味着质量越高 */
.jpeg_quality = 12,
/* 当使用jpeg模式时,如果fb_count超过一个,则驱动程序将在连续模式下工作 */
.fb_count = 2,
.grab_mode = CAMERA_GRAB_WHEN_EMPTY,
};
/**
* @retval 无
*/
uint8_t camera_init(void)
{
esp_err_t err = ESP_OK;
if (CAM_PIN_PWDN == GPIO_NUM_NC)
{
CAM_PWDN(0);
}
if (CAM_PIN_RESET == GPIO_NUM_NC)
{
CAM_RST(0);
vTaskDelay(20);
CAM_RST(1);
vTaskDelay(20);
}
/* 摄像头初始化 */
err = esp_camera_init(&camera_config);
if (err != ESP_OK)
{
return 1;
}
sensor_t * s = esp_camera_sensor_get();
if (s->id.PID == OV3660_PID)
{
s->set_vflip(s, 1); /* 向后翻转 */
s->set_brightness(s, 1); /* 亮度提高 */
s->set_saturation(s, -2); /* 降低饱和度 */
}
else if (s->id.PID == OV5640_PID)
{
s->set_vflip(s, 1); /* 向后翻转 */
}
return err;
}
首先定义一个camera_config_t类型的局部结构体变量,然后给结构体中的成员赋值,再调用esp_camera_init(&camera_config)函数进行初始化。如果摄像头模块是OV3660或者是OV5640,还需要进行配置。
35.3.4 CMakeLists.txt文件
打开本实验BSP下的CMakeLists.txt文件,其内容如下所示:
set(src_dirs
CAMERA
IIC
LCD
LED
SPI
XL9555)
set(include_dirs
CAMERA
IIC
LCD
LED
SPI
XL9555)
set(requires
driver
esp_lcd
esp32-camera)
idf_component_register(SRC_DIRS ${src_dirs}
INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})
component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
上述的红色CAMERA驱动以及esp_ camera依赖库需要由开发者自行添加,以确保CAMERA驱动能够顺利集成到构建系统中。这一步骤是必不可少的,它确保了CAMERA驱动的正确性和可用性,为后续的开发工作提供了坚实的基础。
35.3.5 实验应用代码
打开main/main.c文件,该文件定义了工程入口函数,名为app_main。该函数代码如下。
i2c_obj_t i2c0_master;
/**
* @brief 程序入口
* @param 无
* @retval 无
*/
void app_main(void)
{
uint8_t x = 0;
esp_err_t ret;
ret = nvs_flash_init(); /* 初始化NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
led_init(); /* 初始化LED */
i2c0_master = iic_init(I2C_NUM_0); /* 初始化IIC0 */
spi2_init(); /* 初始化SPI2 */
xl9555_init(i2c0_master); /* 初始化XL9555 */
lcd_init(); /* 初始化LCD */
lcd_show_string(30, 50, 200, 16, 16, "ESP32", RED);
lcd_show_string(30, 70, 200, 16, 16, "CAMERA TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
/* 初始化摄像头 */
while (camera_init())
{
lcd_show_string(30, 110, 200, 16, 16, "CAMERA Fail!", BLUE);
vTaskDelay(500);
}
lcd_clear(BLACK);
while (1)
{
camera_show(0,0); /* 显示图像 */
x++;
if (x % 30 == 0)
{
LED_TOGGLE();
}
vTaskDelay(5);
}
}
从上述源码可知,我们首先初始化各个外设,如IIC、SPI、XL9555、摄像头和LCD等驱动,然后调用camera_show()函数在SPILCD显示屏上显示摄像头图像。
35.4 下载验证
程序下载到开发板后,LCD显示屏不断更新摄像头输出的图像数据,如下图所示。