ESP32在IDF v5.3.1版本下实现驱动摄像头(OV2640为例)

一、摄像头驱动库下载

其实乐鑫官方已经提供了一些常用型号的camera驱动,该驱动库叫 esp32-camera

esp32-camera库github下载链接:

espressif/esp32-camerahttps://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

解决办法:
ESP32 S3 OV2640 camera显示到LCD上出现:cam_dma_config(300): frame buffer malloc faile;cam_dma_config failed_camera config failed with error 0xffffffff-CSDN博客https://blog.csdn.net/qq_34885669/article/details/144918879?spm=1001.2014.3001.5502

三、LCD显示摄像头画面效果

https://live.csdn.net/v/505401https://live.csdn.net/v/505401

相关推荐
小曹要微笑1 天前
I2C总线技术解析(纯文字版)
单片机·嵌入式硬件·esp32·iic
小曹要微笑1 天前
ESP32-S3 OTA 解析(纯技术干货版)
esp32·esp32s3·ota
飞睿科技4 天前
开发者指南:乐鑫Matter SDK在智能家居中的实战应用与优势解析
物联网·esp32·智能家居·sdk·乐鑫科技
hazy1k7 天前
MSPM0L1306 从零到入门: 第九章 ADC-电压采集
stm32·单片机·嵌入式硬件·mcu·物联网·51单片机·esp32
程楠楠&M8 天前
h5页面 调用手机,pda摄像头
智能手机·h5·摄像头·vue3.0
hazy1k8 天前
MSPM0L1306 从零到入门:第七章 通用定时器(GPTIM) —— 成为时间的主宰
stm32·单片机·嵌入式硬件·mcu·物联网·esp32·ti
hazy1k9 天前
MSPM0L1306 从零到入门:第六章 UART —— 让单片机与世界“对话”
stm32·单片机·嵌入式硬件·物联网·51单片机·esp32·iot
ZRT000110 天前
Ubuntu 22.04安装ESP-IDF
linux·ubuntu·esp32·esp-idf
赖small强11 天前
【Linux驱动开发】ESP-Hosted-FG 深度解析指南
linux·驱动开发·esp32·esp-hosted-fg