一、摄像头驱动库下载
其实乐鑫官方已经提供了一些常用型号的camera驱动,该驱动库叫 esp32-camera
esp32-camera库github下载链接:
espressif/esp32-camera
https://github.com/espressif/esp32-camera/tree/master目前最新的驱动库支持的camera型号有:

二、摄像头驱动使用
1.添加驱动库到ESP32 IDF工程
把刚才在乐鑫官方Github里面下载的摄像头驱动库解压放到工程的components文件夹里面:
2.在CMakeList文件里面添加这个esp32-camera依赖库

3.使用esp32-camera驱动库驱动摄像头
以OV2640型号摄像头为例:
(1)摄像头引脚定义
/* 引脚定义 */
#define CAM_PIN_PWDN GPIO_NUM_NC //电源关闭或像素输出使能,用于控制芯片的电源状态或像素数据的输出。(掉电/省电模式,高电平有效)
#define CAM_PIN_RESET GPIO_NUM_NC //复位引脚,用于将芯片状态重置到初始状态。(引脚为低电平时,用于复位整个传感器芯片)
#define CAM_PIN_XCLK GPIO_NUM_15 //外部时钟输入端口,可接外部晶振。
#define CAM_PIN_SIOD GPIO_NUM_4 //SIO_C与SIO_D使用的通讯协议SCCB跟I2C十分类似,我们完全可以直接用I2C硬件外设来控制。
#define CAM_PIN_SIOC GPIO_NUM_5
//#define CAM_PIN_SIOD -1 //设置为-1时,ESP32 CAMERA库会用用硬件I2C硬件外设来控制。(需要提前初始化IIC)
//#define CAM_PIN_SIOC -1
#define CAM_PIN_D7 GPIO_NUM_16 //像素数据输出端口0-7。
#define CAM_PIN_D6 GPIO_NUM_17
#define CAM_PIN_D5 GPIO_NUM_18
#define CAM_PIN_D4 GPIO_NUM_12
#define CAM_PIN_D3 GPIO_NUM_10
#define CAM_PIN_D2 GPIO_NUM_8
#define CAM_PIN_D1 GPIO_NUM_9
#define CAM_PIN_D0 GPIO_NUM_11
#define CAM_PIN_VSYNC GPIO_NUM_6 //帧同步信号,当VSYNC信号线表示为有效电平时,表示新的一帧数据传输完成。
#define CAM_PIN_HREF GPIO_NUM_7 //行同步信号,当HSYNC信号线表示为有效电平时,表示新的一行数据传输完成。
#define CAM_PIN_PCLK GPIO_NUM_13 //像素时钟,用于同步数据传输。即在该时钟边沿时,主机会对数据线上的信号进行采样。
#define CAM_PWDN(x) do{gpio_set_level(CAM_PIN_PWDN, x);}while(0)
#define CAM_RST(x) do{gpio_set_level(CAM_PIN_RESET, x);}while(0)
(2)初始化摄像头
/*
函数功能:初始化摄像头
函数形参:无
*/
esp_err_t camera_ov2640_init(void)
{
esp_err_t ret = ESP_OK;
//摄像头配置
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, //SIO_C与SIO_D使用的通讯协议SCCB跟I2C十分类似,我们完全可以直接用I2C硬件外设来控制。(当引脚为-1时,可以改为现有的I2C控制)
.pin_sccb_scl = CAM_PIN_SIOC,
.pin_d7 = CAM_PIN_D7, //像素数据输出端口0-7。
.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, // 帧同步信号,当VSYNC信号线表示为有效电平时,表示新的一帧数据传输完成。
.pin_href = CAM_PIN_HREF, // 行同步信号,当HSYNC信号线表示为有效电平时,表示新的一行数据传输完成。
.pin_pclk = CAM_PIN_PCLK, // 像素时钟,用于同步数据传输。即在该时钟边沿时,主机会对数据线上的信号进行采样。
.xclk_freq_hz = 16*1000*1000, // XCLK时钟频率,当出现cam_hal: EV-VSYNC-OVF报错时,可以适当减小xclk_freq_hz
.ledc_timer = LEDC_TIMER_0, // 用于生成 XCLK 的定时器。
.ledc_channel = LEDC_CHANNEL_0, // 用于生成 XCLK 的定时器通道。
.fb_location = CAMERA_FB_IN_PSRAM,
.pixel_format = PIXFORMAT_RGB565, // 图像输出模式 PIXFORMAT_RGB565
.frame_size = FRAMESIZE_240X240 , // 图像输出大小 FRAMESIZE_240X240
.jpeg_quality = 50, // 0-63,对于OV系列相机传感器,数量越少意味着质量越高
.fb_count = 2, // 当使用jpeg模式时,如果fb_count超过一个,则驱动程序将在连续模式下工作,适当增加帧缓存个数可以提高显示刷新速度,缺点就是消耗大量的内存空间
.grab_mode = CAMERA_GRAB_WHEN_EMPTY, // 使用CAMERA_GRAB_WHEN_EMPTY可以稍微对减少拖影有效果
//.sccb_i2c_port = I2C_NUM_1, // (当SIO_C与SIO_D引脚为-1时,可以改为现有的I2C控制,但是要注意要提前初始化I2C的频率不能太高,否则可能超过摄像头的支持的频率)
};
if(CAM_PIN_PWDN != GPIO_NUM_NC)
{
CAM_PWDN(0); //退出省电模式,使能像素输出。
}
if(CAM_PIN_RESET != GPIO_NUM_NC)
{
CAM_RST(0); //复位相机内部芯片。
vTaskDelay(50);
CAM_RST(1);
vTaskDelay(50);
}
ret = esp_camera_init(&camera_config); //摄像头初始化
if (ret != ESP_OK)
{
return 1;
}
ESP_LOGI(TAG, "camera_ov2640_init ok !");
return ESP_OK;
}
(3)获取摄像头画面数据帧
获取摄像头画面数据主要就是使用esp_camera_fb_get 这个接口,调用该接口就可以获取到一帧的摄像头的画面,然后把画面帧数据以消息队列的形式发送给LCD显示任务显示。
/*
函数功能:实时获取摄像头输出的视频数据帧,并通过消息队列发送给显示任务
函数形参:
arg:创建任务时传入的参数
*/
void camera_task_process(void *arg)
{
ESP_LOGI("Camera Display","camera_task_process ok! ");
arg = arg;
while (1)
{
camera_frame = esp_camera_fb_get();
if(camera_frame)
xQueueSend(xQueueLCDFrame, &camera_frame,portMAX_DELAY); //pdMS_TO_TICKS(200)
else
vTaskDelay(100);
}
}
(4)显示摄像头画面
/*
函数功能:将消息队列里面的摄像头视频帧实时显示到lcd显示屏。
函数形参:
arg:创建任务时传入的参数
*/
void lcd_task_process(void *arg)
{
uint8_t fps=0,flag=0,refresh_lcd = 1;;
uint8_t dispalyBuff[16];
st7789v_clearScreen(WHITE,USE_HORIZONTAL);
ESP_LOGI("Camera Display","lcd_task_process ok! ");
while(1)
{
if(lcd_refresh_fram != 0)
{
flag = !flag;
fps = lcd_refresh_fram;
lcd_refresh_fram = 0;
sprintf((char *)dispalyBuff,"%3.0dfps",fps);
st7789v_displayString(150,250,dispalyBuff,RED,WHITE,24,0,USE_HORIZONTAL);
//ESP_LOGI("Camera Display","%d fps",fps);
}
if(xQueueReceive(xQueueLCDFrame, &lcd_frameI, portMAX_DELAY))
{
lcd_fram ++ ;
if(lcd_frameI->width > LCD_W || lcd_frameI->height > LCD_H)
{
printf("camera_width > LCD_W or camera_height > LCD_H \r\n");
goto err;
}
st7789v_LcdShowPicture(0,0,lcd_frameI->width,lcd_frameI->height,lcd_frameI->buf,USE_HORIZONTAL);
}
else
{
vTaskDelay(pdMS_TO_TICKS(10));
}
err:
esp_camera_fb_return(lcd_frameI);
lcd_frameI = NULL;
}
}
(5)测试用例
TaskHandle_t lcdTaskHandle = NULL;
TaskHandle_t cameraTaskHandle = NULL;
QueueHandle_t xQueueLCDFrame = NULL;
camera_fb_t *lcd_frameI = NULL;
camera_fb_t *camera_frame = NULL;
/*
函数功能:创建摄像头视频帧数据处理任务和LCD显示任务
函数形参:无
*/
void start_show_camera_frame_to_lcd(void)
{
xQueueLCDFrame = xQueueCreate(5, sizeof(camera_fb_t *));
SemaphoreCarPlate = xSemaphoreCreateBinary();
camera_ov2640_init();
xTaskCreatePinnedToCore(camera_task_process, "camera_task_process", 5 * 1024, NULL, 5, &cameraTaskHandle, 0);
xTaskCreatePinnedToCore(lcd_task_process, "lcd_task_process", 6 * 1024, NULL, 5, &lcdTaskHandle, 1);
ESP_LOGI("Camera Display","start_show_camera_frame_to_lcd ok! ");
}
如果运行报错:cam_dma_config(300): frame buffer malloc faile;cam_dma_config failed

三、LCD显示摄像头画面效果
https://live.csdn.net/v/505401
https://live.csdn.net/v/505401

