OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 25|回复: 0

《ESP32-P4开发指南— V1.0》第三十七章 MIPICAMERA实验

[复制链接]

1240

主题

1254

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5331
金钱
5331
注册时间
2019-5-8
在线时间
1371 小时
发表于 昨天 14:41 | 显示全部楼层 |阅读模式
第三十七章 MIPICAMERA实验

1)实验平台:正点原子DNESP32P4开发板

2)章节摘自【正点原子】ESP32-P4开发指南— V1.0

3)购买链接:https://detail.tmall.com/item.htm?id=873309579825

4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32P4.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子DNESP32S3开发板技术交流群:132780729


2.jpg

3.png

本章,我们将介绍MIPI摄像头OV5645,学习如何在ESP32-P4上驱动MIPI摄像头,实现摄像头图像数据采集并进行显示。
本章分为如下几个小节:
37.1 OV5645摄像头介绍及ESP32-P4的CAM相关外设介绍
37.2 硬件设计
37.3 程序设计
37.4 下载验证


37.1 OV5645摄像头介绍及ESP32-P4的MIPI_CSI介绍

37.1.1 OV5645摄像头介绍
OV5645是正点原子推出的一款高性能超清2K摄像头模块。该模块具有一颗1/4英寸CMOS 2592x1944图像传感器OV5645。同时,模块集成了有源晶振和LDO芯片。模块接口有间距2mm的2*11pin排针和间距为0.5mm的22pin的FPC接口,可供用户根据不同的场景选择使用。
模块实物图如下图所示。

第三十七章 MIPICAMERA实验377.png 第三十七章 MIPICAMERA实验378.png
图37.1.1.1 OV5645摄像头实物图

OV5645模块具有以下特点:
●像素大小:1.4微米 x 1.4微米
●500万像素(2592x1944)
●可编程控制帧速率,镜像和翻转,裁剪和窗口
●支持图像大小:2K(2560x1440)、1080P(1920x1080)、720P(1280x720)等        
●双线串行总线控制(SCCB)
●双通道MIPI串行输出
●集成有源晶振,无需外部提供时钟
●集成LDO,仅需提供3.3V电源即可正常工作
●自动图像控制功能:自动曝光控制(AEC)、自动白平衡(AWB)、自动带通滤波器(ABF)、自动黑电平校准(ABLC)、自动50/60Hz亮度检测
●可编程控制:帧率、图像大小、水平反射镜、垂直翻转、裁剪和平移
●支持RawRGB、RGB565、RGB555、RGB444、YUV422、YUV420、YCbCr422和压缩图像(JPEG)输出格式
●电源核心:1.5±5%;模拟量:2.6V ~ 3.0V;输入/输出:1.7V ~ 3.0V
●工作温度范围:-30℃ ~ 70℃,稳定成像温度范围:0 ~ 50℃
OV5645的内部结构如下图所示:


第三十七章 MIPICAMERA实验869.png
图 错误!文档中没有指定样式的文字。.1.2 OV5645内部结构图

由上图可知,时序发生器timing generator控制着感光阵列image array、放大器AMP、AD转换以及输出外部时序信号(VSYNC、HREF和PCLK),外部时钟XVCLK经过PLL锁相环后输出的时钟作为系统的控制时钟;感光阵列将光信号转化成模拟信号,经过增益放大器之后进入10位AD转换器;AD 转换器将模拟信号转化成数字信号,并且经过ISP进行相关图像处理,最终输出所配置格式视频数据流。增益放大器控制以及 ISP 等都可以通过寄存器(registers)来配置,配置寄存器的接口就是SCCB接口,该接口协议兼容IIC协议。
SCCB(Serial Camera Control Bus,串行摄像头控制总线)是由OV(OmniVision的简称)公司定义和发展的三线式串行总线,该总线控制着摄像头大部分的功能,包括图像数据格式、分辨率以及图像处理参数等。OV公司为了减少传感器引脚的封装,现在 SCCB 总线大多采用两线式接口总线。
OV5645使用的是两线式SCCB接口总线,该接口总线包括SIO_C串行时钟输入线和SIO_D 串行双向数据线,分别相当于IIC协议的SCL信号线和SDA信号线。我们在前面提到过SCCB协议兼容IIC协议,是因为SCCB协议和IIC协议非常相似,有关IIC协议的详细介绍请大家参考“IIC_EXIO”章节。
OV5645是用16位(两个字节)表示寄存器地址。OV5645 SCCB的写传输协议如下图所示:


第三十七章 MIPICAMERA实验1554.png
图 错误!文档中没有指定样式的文字。.3 SCCB 写传输协议

上图中的ID Address(W)由7位器件地址和1位读写控制位构成(0:写;1:读),而OV5645的器件地址为0x3C,所以在写传输协议中,ID Address(W)= 0x78(器件地址左移1位,低位补0);Sub-address(H)为高8位寄存器地址,Sub-address(L)为低 8 位寄存器地址,在OV5645众多寄存器中,有些寄存器是可改写的,有些是只读的,只有可改写的寄存器才能正确写入;Write Data为8位写数据,每一个寄存器地址对应8位的配置数据。上图中的第9位X表示Don’t Care(不必关心位),该位是由从机(此处指OV5645)发出应答信号来响应主机表示当前ID Address、Sub-address和Write Data是否传输完成,但是从机有可能不发出应答信号,因此主机可不用判断此处是否有应答,直接默认当前传输完成即可。
我们可以发现,SCCB和IIC写传输协议是极为相似的,只是在SCCB写传输协议中,第9位为不必关心位,而IIC写传输协议为应答位。SCCB的读传输协议和IIC有些差异,在IIC读传输协议中,写完寄存器地址后会有restart即重复开始的操作;而SCCB读传输协议中没有重复开始的概念,在写完寄存器地址后,发起总线停止信号,下图为SCCB的读传输协议。


第三十七章 MIPICAMERA实验2168.png
图 错误!文档中没有指定样式的文字。.1.4 SCCB 读传输协议

SCCB读传输协议分为两个部分。第一部分是写器件地址和寄存器地址,即先进行一次虚写操作,通过这种虚写操作使地址指针指向虚写操作中寄存器地址的位置,当然虚写操作也可以通过前面介绍的写传输协议来完成。第二部分是读器件地址和读数据,此时读取到的数据才是寄存器地址对应的数据,注意ID Address(R) = 0x43(器件地址左移1位,低位补1)。上图中的NA位由主机产生,由于SCCB总线不支持连续读写,因此NA位必须为高电平。
在OV5645正常工作之前,必须先对传感器进行初始化,即通过配置寄存器使其工作在预期的工作模式,以及得到较好画质的图像。因为SCCB的写传输协议和IIC几乎相同,因此我们可以直接使用IIC的驱动程序来配置摄像头。当然这么多寄存器也并非都需要配置,很多寄存器可以采用默认的值。下表是本程序用到的关键寄存器的配置说明。
由于OV5645摄像头的寄存器非常多,并且大多数的寄存器都可以使用默认设置,因此上表只列出了一些关键寄存器,更多寄存器设置大家可以参考官方数据手册。


1.png
2.png
表37.1.1.1 OV5645寄存器表

由于OV5645摄像头的寄存器非常多,并且大多数的寄存器都可以使用默认设置,因此上表只列出了一些关键寄存器,更多寄存器设置大家可以参考官方数据手册。
时钟配置
在上表中列出了0x3035、0x3036和0x3037三个时钟配置寄存器,这三个寄存器在整个时钟链路当中的作用如下图所示:


第三十七章 MIPICAMERA实验4549.png
图 错误!文档中没有指定样式的文字。.5 时钟路径

从图中可以看到OV5645外部输入时钟范围是6~27Mhz(板载24Mhz),外部输入时钟进入摄像头内部PLL,先经过pre-divider,根据0x3037寄存器bit0~bit3的值将时钟降为4~27Mhz(本节实验降低到8Mhz);之后再经过multiplier,判断寄存器0x3036的bit7位是否为1,如果是1则将时钟倍频到0x3036[7:1]*2倍的值,如果不为1则倍频到0x3036[6:0]倍的值(本节实验倍频到0x3036[6:0]倍的值,即112倍,最终输出值为896Mhz),最终输出时钟范围是500~1000Mhz;再之后时钟会经过SYS divider0和MIPI divider,前者是判断0x3035寄存器bit4~bit7的值是否为0,如果为0则时钟分频16倍,否则分频0x3035[7:4]倍,后者是判断0x3035寄存器bit0~bit3的值是否为0,如果为0则时钟分频16倍,否则分频0x3035[3:0]倍(本节实验先分频2倍,再分频1倍,最终输出448Mhz);最后时钟到达MIPI PHY,在这个模块之中直接对时钟进行2分频,最终输出MIPI CLK(本节实验最终的MIPI CLK为224Mhz)。
图像输出开窗设置
OV5645摄像头输出的图像大小是可以调整的,整体的开窗设置如下图所示:


第三十七章 MIPICAMERA实验5166.png
图 错误!文档中没有指定样式的文字。.6 开窗示意图

从上图中可以看出OV5645的开窗大小是用(“开窗水平结束位置”减去“开窗水平起始位置”)*(“开窗垂直结束位置”减去“开窗垂直起始位置”),通过寄存器0x3800~0x3807来控制。
开完窗后还可以通过水平方向和垂直方向的偏移寄存器(0x3810~0x3813)来进一步调整大小,最终输出的图像就上图中白色区域部分。这里大家需要注意一下,在整体图像分辨率水平:垂直=4:3的情况下按上图去配置是可以的,如果是其他尺寸比例那么按上图配置不一定能成功,具体的配置需要原厂协助,我们也没有详细的配置方法或者尺寸计算公式。
OV5645输出图像大小不仅可以通过开窗来调整,还可以通过压缩功能来调整,如下图所示:


第三十七章 MIPICAMERA实验5517.png
图 错误!文档中没有指定样式的文字。.7 图像压缩

当寄存器0x5001的bit5压缩使能打开(0关闭,1开启),就可以在开窗的基础上输出压缩后的图像,压缩图像等于开窗图像减去2倍水平垂直偏移量。
输出像素格式
从寄存器的配置表中就可以看到OV5645摄像头支持的像素格式非常多,可以通过配置寄存器0x4300,让摄像头输出不同格式的像素数据。本节实验使用的是RAW格式数据,所以接下来只介绍RAW格式的像素数据输出,如下图所示:


第三十七章 MIPICAMERA实验5753.png
图 错误!文档中没有指定样式的文字。.8原始像素格式

从上图中可以看到整个传感器拥有水平2624垂直1964共计5153536个感光像素点,其中有效感光像素点是水平2592垂直1944共计5038848个像素,也就是图中active区域。在有效显示区域中像素数据是按照Bayer阵列排布的。所谓的Bayer阵列其实就是在上图active区域中像素按照2*2的方格排列,如下图所示:

第三十七章 MIPICAMERA实验5964.png
图 错误!文档中没有指定样式的文字。.9 Byer阵列

上图中红色方框框出来的就是一个2*2的Byer阵列,可以看到它的组合方式是BGGR,这是摄像头的原始配置,当寄存器0x4300配置为0x00时输出的就是这种组合,第一行数据全部是BGBG,第二行数据全部是GRGR,组成Byer阵列就是BGGR。当然也可以通过改变寄存器0x4300的配置值来改变输出的像素格式,具体的格式配置在错误!未找到引用源。中已经列出。这种Byer阵列数据可以通过插值法来进一步转换成RGB数据,最终在显示屏幕上显示出来。
彩条测试模式
图像传感器配置成彩条测试模式后,会输出彩色的条纹,方便测试图像传感器是否正常工作,通过配置寄存器 0x503d 的Bit[7]位打开和关闭彩条模式。当需要打开彩条模式时,寄存器 0x503d 配置成 0x80,关闭时配置成0x00,下图为打开彩条模式后图像输出的条纹(8个颜色条)。


第三十七章 MIPICAMERA实验6409.png
图 错误!文档中没有指定样式的文字。.1.10 测试彩条

注意彩条最左边是一个白色长条,颜色与背景一致,所以看不清楚。
关于OV5645的基础介绍,我们就介绍到这。OV5645的详细资料请参考光盘:7,硬件资料→2,芯片资料→ OV5645_CSP3_DS_2.01.pdf这个文档,供大家参考学习。

37.1.2 ESP32-P4的CAMERA模块介绍
ESP32-P4的LCD_CAM控制器包括独立的LCD控制模块和Camera(摄像头)控制模块。在前面已经介绍过了LCD模块,本章节主要介绍Camera模块。
首先通过分析LCD_CAM控制器的结构框图,了解一下其工作过程。结构框图如下图所示。

第三十七章 MIPICAMERA实验6730.png
图37.1.2.1 LCD_CAM控制器结构框图

Camera控制模块(红色虚线以上)包括独立的接收控制单元(Camera_Ctrl)、接收异步FIFO(Async RX FIFO)、时钟生成模块(CAM_Clock Generator)和格式转换模块(RGB/YUV Converter)。
接收控制单元:用于控制CAM数据的接收。CAM数据从上图可知,可由GDMA取自内部或外部存储器。
接收异步FIFO:用于与外部设备进行交互。
CAM时钟生成模块:用于生成CAM_CLK时钟。时钟源经过时钟生成模块处理后,生成Camera模块所需要的时钟CAM_CLK,这一过程如下图所示。


第三十七章 MIPICAMERA实验7024.png
图37.1.2.2 CAM模块时钟生成过程

Camera模块时钟由三个时钟源提供,分别是XTAL_CLK、PLL_F160M_CLK和APLL_CLK。可由HP_SYS_CLKRST_PERI_CLK_CTRL19_REG的HP_SYS_CLKRST_CAM_CLK_SRC_SEL位决定,0选择使用XTAL_CLK,1选择使用PLL_F160M_CLK,2选择使用APLL_CLK。        
选择好时钟源后(即得到CAM_CLK_SRC),经过降频得到CAM_CLK,后面再经过分频最终得到CAM_CLK,这三者的关系如下:


第三十七章 MIPICAMERA实验7288.png

Note:
① 第一条公式中的N、b和a都是通过HP_SYS_CLKRST_PERI_CLK_CTRL120_REG寄存器进行配置的。N的取值范围为2≤N≤256,通过HP_SYS_CLKRST_CAM_CLK_DIV_NUM位进行配置。b是通过HP_SYS_CLKRST_CAM_CLK_DIV_NUMERATOR位进行配置,而a是通过HP_SYS_CLKRST_CAM_CLK_DIV_DENONMINATOR位进行配置。
格式转换模块:用于各种格式的视频数据互相转换。在上图中,该部分是虚线框,也就是可不使用该模块。
Camera-LCD控制器的CAM和LCD接口都可通过GPIO交换矩阵灵活配置使用任意GPIO引脚。对于CAM模块的信号,即图37.1.2.1右上角部分,在这里也简单介绍一下,如下表所示。


3.png
表37.1.2.1 CAM模块信号描述

37.1.3 ESP32-P4 MIPI_CSI接口介绍
ESP32-P4带有一个MIPI CSI接口,用于连接MIPI接口的摄像头,具有如下特性:
        ● 符合MIPI CSI-2协议。
        ● 使用DPHY v1.1版本。
        ● 2-lane x 1.5 Gbps。
        ● 输入格式支持RGB888、RGB666、RGB565、YUV422、YUV420、RAW8、RAW10、RAW12。
        MIPI CSI接口使用专用数字管脚,管脚序号为42~48。

37.1.4 图像信号处理器ISP介绍
ESP32-P4带有一个图像信号处理器(ISP),具有如下特性:
最大分辨率 1920 x 1080
三个输入通道:MIPI CSI、DVP、DW-GDMA
输入格式:RAW8、RAW10、RAW12
输出格式:RAW8、RGB888、RGB565、YUV422、YUV420
pipeline结构:拜尔域降噪(BF)、去马赛克(Demosaic)、色彩矩阵(CCM)、伽马矫正(Gamma Correction)、锐化(Edge)、对比度色相饱和度亮度调节(Contrast/Hue/Saturation/Brightness)、自动曝光统计(AE)、自动对焦统计(AF)、白平衡统计(AWB)、直方图统计(HIST)。

37.1.5 像素处理加速器PPA外设介绍
ESP32-P4带有像素处理加速器(PPA),可实现SRM与BLEND两大功能,具有如下特性:
SRM可实现图像旋转、缩放、镜像
        - 输入格式支持ARGB8888、RGB888、RGB565、YUV420
        - 输出格式支持ARGB8888、RGB888、RGB565、YUV420
        - 支持逆时针90°、180°、270°旋转
        - 支持水平、垂直方向缩放,缩放系数的整数部分为8-bit,小数部分为4-bit
        - 支持水平、垂直方向镜像
BLEND可实现两个相同尺寸的图层叠加
        - 输入格式支持ARGB8888、RGB888、RGB565、L4、L8、A4、A8
        - 输出格式支持ARGB8888、RGB888、RGB565
        - 支持两个图层基于Alpha通道叠加。若图层不包含Alpha通道信息,通过寄存器配置补全
        - 通过设置前景以及背景color-key范围实现特殊颜色过滤

37.1.6 CAMERA驱动框架介绍
CAMERA驱动框架主要用到的是V4L2框架,即Video for Linux 2,是Linux系统中用于处理视频设备的内核驱动程序接口。
V4L2提供了一种标准化的方式,使用户空间程序能够与视频设备(如摄像头、视频采集卡等)进行通信和交互。它屏蔽了底层摄像头的不同驱动实现,为用户空间提供了统一的接口调用。

37.2 硬件设计

37.2.1 例程功能
开机的时候,初始化OV5645摄像头。初始化成功后直接在LCD上输出图像。 LED0用来指示程序正在运行。

37.2.2 硬件资源
1)LED灯
        LED        0        - IO51
2)RGBLCD / MIPILCD(引脚太多,不罗列出来)
3)MIPICAMERA
        CSI_D0_P(固定引脚)                CSI_D0_N(固定引脚)
        CSI_D1_P(固定引脚)                CSI_D1_N(固定引脚)
        CSI_CK_P(固定引脚)                CSI_CK_N(固定引脚)
        CSI_RST         - IO34                        CSI_PWDN        - IO54
        IIC_SCL                - IO32                        IIC_SDA                - IO33

37.2.3 原理图
CAMERA相关原理图,如下图所示。

第三十七章 MIPICAMERA实验9359.png
图37.2.3.1 摄像头实验原理图

这里说明一下,与OV5645通信的引脚都是1.8V的,所以通过CJ3134芯片进行转换。

37.3 程序设计

37.3.1 CAMERA的IDF驱动
esp_video、esp_sccb_intf、esp_cam_sensor组件驱动位于ESP-IDF在线组件注册表中。如果需要将该组件添加到项目工程中,可按照以下步骤操作:
1)打开ESP-IDF注册表。
2)搜索 “esp_video”、“esp_sccb_intf”、“esp_cam_sensor”组件。
3)将组件安装到项目中。
组件安装完成后,系统会自动更新main文件夹中的特殊组件清单文件idf_component.yml在项目编译时,系统会根据清单文件从注册表中下载并集成该组件到工程中。关于上述操作流程,可参考本书籍第八章的内容。
注意:在进行ESP32摄像头模块(如OV5645)开发时,必须对电源管理进行一些关键的调整。首先,在esp_cam_sensor组件下的ov5645.c文件中的ov5645_power_on函数中,我们需要确保pwdn(Power Down)管脚在初始化时先拉低再拉高。这一修改是根据OV5645摄像头的数据手册要求的,因为不按照这个时序操作,摄像头可能无法正常启动或工作不稳定。接着,在esp_video组件中的esp_video_csi_device.c文件里,我们将CSI_LDO_CFG_VOL_MV的电压值设置为1900mV。这是因为LDO3的电压在LCD初始化时已经被定义为1900mV,为了确保系统电源的一致性,必须将此电压值与LCD初始化时的配置保持一致。若电压不一致,可能会导致系统不稳定或出现运行错误。通过这些调整,我们可以确保摄像头模块和视频系统的电源管理符合硬件要求,并提高系统的稳定性,避免因电压不匹配或初始化顺序错误导致的潜在问题。这里的组件过多嵌套,暂时不做说明,请参考应用层代码即可。

37.3.2 程序流程图

第三十七章 MIPICAMERA实验10229.png
图37.3.2.1 摄像头实验程序流程图

37.3.3 程序解析
在28_mipicamera例程中,作者在28_mipicamera\main\APP路径下新建了1个文件夹MIPI_CAM,并且需要更改CMakeLists.txt内容,以便在其他文件上调用。
1. MIPI_CAM驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。MIPI_CAM驱动源码包括四个文件:mipi_cam.c、mipi_cam.h、app_video.c和app_video.h。
其中mipi_cam.c和mipi_cam.h主要是MIPI摄像头的驱动文件,而app_video.c和app_video.h主要是关于MIPI摄像头调用相对下层接口实现。
在mipi_cam.h中主要是函数声明,大家自行查看源码即可。
下面我们再解析mipi_cam.c的程序,首先先来看一下初始化函数mipi_cam_init,代码如下:
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief             mipi_cam初始化</font>
  3. <font size="3"> * [url=home.php?mod=space&uid=271674]@param[/url]             无</font>
  4. <font size="3"> * @retval            ESP_OK:开启成功; ESP_FAIL:开启失败</font>
  5. <font size="3"> */</font>
  6. <font size="3">esp_err_t mipi_cam_init(void)</font>
  7. <font size="3">{</font>
  8. <font size="3">    esp_err_t ret = ESP_OK;</font>

  9. <font size="3">    ret = app_video_main(bus_handle);                        /* 初始化摄像头硬件和软件接口 */</font>
  10. <font size="3">    if (ret != ESP_OK)</font>
  11. <font size="3">    {</font>
  12. <font size="3">        ESP_LOGE(mipi_cam_tag, "video main init failed with error 0x%x", ret);</font>
  13. <font size="3">        return ESP_FAIL;</font>
  14. <font size="3">    }</font>

  15. <font size="3">    int video_cam_fd0 = app_video_open(0);          /* 打开摄像头设备 */</font>
  16. <font size="3">    if (video_cam_fd0 < 0)</font>
  17. <font size="3">    {</font>
  18. <font size="3">        ESP_LOGE(mipi_cam_tag, "video cam open failed");</font>
  19. <font size="3">        return ESP_FAIL;</font>
  20. <font size="3">    }</font>

  21. <font size="3">        /* 初始化设备并设置捕获视频格式 */</font>
  22. <font size="3">    ret = app_video_init(video_cam_fd0, APP_VIDEO_FMT_RGB565);  </font>
  23. <font size="3">    if (ret != ESP_OK)</font>
  24. <font size="3">    {</font>
  25. <font size="3">        ESP_LOGE(mipi_cam_tag, "Video cam init failed with error 0x%x", ret);</font>
  26. <font size="3">        return ESP_FAIL;</font>
  27. <font size="3">    }</font>

  28. <font size="3">    xTaskCreatePinnedToCore(lcd_cam_task, "lcd cam display", 4096, &video_cam_fd0, 4, NULL, 0);        /* 新建摄像头显示任务 */</font>

  29. <font size="3">    return ESP_OK;</font>
  30. <font size="3">}</font>
复制代码
在MIPI摄像头初始化函数中,首先调用app_video_main函数初始化摄像头硬件和软件接口接口,然后调用app_video_open函数打开摄像头设备,随后调用app_video_init函数初始化设备并设置捕获视频格式。最后新建摄像头显示任务lcd_cam_task。
接下来,看一下前面提及到的摄像头显示任务函数lcd_cam_task,代码如下。
  1. <font size="3">/**</font>
  2. <font size="3"> * @brief            摄像头显示任务</font>
  3. <font size="3"> * @param             arg: 传入参数(未用到)</font>
  4. <font size="3"> * @retval            无</font>
  5. <font size="3"> */</font>
  6. <font size="3">static void lcd_cam_task(void *arg)</font>
  7. <font size="3">{</font>
  8. <font size="3">    int video_fd = *((int *)arg);       /* 任务参数 */</font>

  9. <font size="3">    int res = 0;</font>
  10. <font size="3">    struct v4l2_buffer buf;             /* 视频缓冲信息 */</font>

  11. <font size="3">    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;</font>
  12. <font size="3">    struct v4l2_format format = {0};    /* 数据流格式 */</font>
  13. <font size="3">    format.type = type;                 </font>

  14. <font size="3">    /* 获取LCD BUF(双buffer) */</font>
  15. <font size="3">    void *lcd_buffer[2];</font>
  16. <font size="3">    void *draw_buffer = NULL;</font>
  17. <font size="3">    size_t data_cache_line_size = 0;</font>

  18. <font size="3">    if (lcddev.id <= 0x7084)        /* RGB屏触摸屏 */</font>
  19. <font size="3">    {</font>
  20. <font size="3">        ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(</font>
  21. <font size="3">        lcddev.lcd_panel_handle, 2, &lcd_buffer[0], &lcd_buffer[1]));</font>
  22. <font size="3">    }</font>
  23. <font size="3">    else</font>
  24. <font size="3">    {</font>
  25. <font size="3">        ESP_ERROR_CHECK(esp_lcd_dpi_panel_get_frame_buffer(</font>
  26. <font size="3">        lcddev.lcd_panel_handle, 2, &lcd_buffer[0], &lcd_buffer[1]));</font>
  27. <font size="3">    }</font>
  28. <font size="3">    draw_buffer = lcd_buffer[1];</font>
  29. <font size="3">ESP_ERROR_CHECK(esp_cache_get_alignment(MALLOC_CAP_SPIRAM,</font>
  30. <font size="3">                                           &data_cache_line_size));</font>

  31. <font size="3">    /*  CAM BUF设置两个,使用MMAP */</font>
  32. <font size="3">    void *camera_outbuf[2];</font>
  33. <font size="3">    ESP_ERROR_CHECK(camera_set_bufs(video_fd, 2, NULL));                /* 设置帧缓存数量 */</font>
  34. <font size="3">    ESP_ERROR_CHECK(camera_get_bufs(2, &camera_outbuf[0]));           /* 获取帧缓冲 */</font>

  35. <font size="3">    /*---------------PPA的设置-SRM------------------*/</font>
  36. <font size="3">    ppa_client_handle_t ppa_srm_handle = NULL;</font>
  37. <font size="3">    ppa_client_config_t ppa_srm_config = {                 /* 设置客户端的属性 */</font>
  38. <font size="3">        .oper_type             = PPA_OPERATION_SRM,        /* PPA操作类型 */</font>
  39. <font size="3">        .max_pending_trans_num = 1,         /* 客户端可以持有的最大PPA事务挂起数量 */</font>
  40. <font size="3">};</font>
  41. <font size="3">    /* 注册PPA客户端 */</font>
  42. <font size="3">    ESP_ERROR_CHECK(ppa_register_client(&ppa_srm_config, &ppa_srm_handle)); </font>

  43. <font size="3">    ESP_ERROR_CHECK(camera_stream_start(video_fd));         /* 开启视频流 */</font>

  44. <font size="3">#if FPS_FUNC_ON == 1                 /* FPS_FUNC_ON该宏 打开帧率显示功能 */</font>
  45. <font size="3">    int fps_count = 0;</font>
  46. <font size="3">    int64_t start_time = esp_timer_get_time();</font>
  47. <font size="3">#endif</font>

  48. <font size="3">    if (ioctl(video_fd, VIDIOC_G_FMT, &format) != 0)        /* 获取设置支持的视频格式 */</font>
  49. <font size="3">    {</font>
  50. <font size="3">        ESP_LOGE(mipi_cam_tag, "get fmt failed");</font>
  51. <font size="3">    }</font>

  52. <font size="3">    while (1)</font>
  53. <font size="3">{   /* 双缓冲情况下,切换buffer */</font>
  54. <font size="3">        draw_buffer = lcd_buffer[0] == draw_buffer ?lcd_buffer[1]:lcd_buffer[0];</font>

  55. <font size="3">#if FPS_FUNC_ON == 1        /* FPS_FUNC_ON该宏 打开帧率显示功能 */</font>
  56. <font size="3">        fps_count++;</font>
  57. <font size="3">        if (fps_count == 50) {</font>
  58. <font size="3">            int64_t end_time = esp_timer_get_time();</font>
  59. <font size="3">            ESP_LOGI(mipi_cam_tag, "fps: %f", 1000000.0 / </font>
  60. <font size="3">((end_time - start_time) / 50.0));</font>
  61. <font size="3">            start_time = end_time;</font>
  62. <font size="3">            fps_count = 0;</font>
  63. <font size="3">        }</font>
  64. <font size="3">#endif</font>

  65. <font size="3">        memset(&buf, 0, sizeof(buf));</font>
  66. <font size="3">        buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;         /* 与前面format.type要一致 */</font>
  67. <font size="3">        buf.memory = V4L2_MEMORY_MMAP;                    /* 内存使用mmap方式 */</font>
  68. <font size="3">        </font>
  69. <font size="3">/* 将已经捕获好视频的内存拉出已捕获视频的队列 */</font>
  70. <font size="3">        res = ioctl(video_fd, VIDIOC_DQBUF, &buf);        </font>
  71. <font size="3">        if (res != 0)</font>
  72. <font size="3">        {</font>
  73. <font size="3">            ESP_LOGE(mipi_cam_tag, "failed to receive video frame");</font>
  74. <font size="3">        }</font>

  75. <font size="3">        if (mipidev.id == 0x8399)   /* 1080p */</font>
  76. <font size="3">        {</font>
  77. <font size="3">            ppa_srm_oper_config_t oper_config = {</font>
  78. <font size="3">                .in.buffer          = camera_outbuf[buf.index],        </font>
  79. <font size="3">                .in.pic_w           = format.fmt.pix.width,        </font>
  80. <font size="3">                .in.pic_h           = format.fmt.pix.height,        </font>
  81. <font size="3">                .in.block_w         = format.fmt.pix.width,         </font>
  82. <font size="3">                .in.block_h         = format.fmt.pix.height,        </font>
  83. <font size="3">                .in.block_offset_x  = 0,                            </font>
  84. <font size="3">                .in.block_offset_y  = 0,                            </font>
  85. <font size="3">                .in.srm_cm          = PPA_SRM_COLOR_MODE_RGB565,    </font>

  86. <font size="3">                .out.buffer         = draw_buffer,                  </font>
  87. <font size="3">                .out.buffer_size    = ALIGN_UP_BY(lcddev.height * lcddev.width *</font>
  88. <font size="3">                                      16 / 8, data_cache_line_size), </font>
  89. <font size="3">                .out.pic_w          = lcddev.width,                 </font>
  90. <font size="3">                .out.pic_h          = lcddev.height,                </font>
  91. <font size="3">                .out.block_offset_x = 60,                           </font>
  92. <font size="3">                .out.block_offset_y = 0,                            </font>
  93. <font size="3">                .out.srm_cm         = PPA_SRM_COLOR_MODE_RGB565,    </font>
  94. <font size="3">                .rotation_angle     = PPA_SRM_ROTATION_ANGLE_90,    </font>
  95. <font size="3">                .scale_x            = 1.5,                          </font>
  96. <font size="3">                .scale_y            = 1,                            </font>
  97. <font size="3">                .mirror_x           = true,                        </font>
  98. <font size="3">                .mode               = PPA_TRANS_MODE_BLOCKING,      </font>
  99. <font size="3">            };</font>
  100. <font size="3">            ppa_do_scale_rotate_mirror(ppa_srm_handle, &oper_config);   </font>
  101. <font size="3">        }</font>
  102. <font size="3">        else if (mipidev.id == 0x8394)  /* 720p */</font>
  103. <font size="3">        {</font>
  104. <font size="3">            ppa_srm_oper_config_t oper_config = {</font>
  105. <font size="3">                .in.buffer          = camera_outbuf[buf.index],</font>
  106. <font size="3">                .in.pic_w           = format.fmt.pix.width,   </font>
  107. <font size="3">                .in.pic_h           = format.fmt.pix.height,</font>
  108. <font size="3">                .in.block_w         = format.fmt.pix.width / 2,</font>
  109. <font size="3">                .in.block_h         = format.fmt.pix.height / 2,</font>
  110. <font size="3">                .in.block_offset_x  = 0,</font>
  111. <font size="3">                .in.block_offset_y  = 0,</font>
  112. <font size="3">                .in.srm_cm          = PPA_SRM_COLOR_MODE_RGB565,</font>

  113. <font size="3">                .out.buffer         = draw_buffer,</font>
  114. <font size="3">                .out.buffer_size    = ALIGN_UP_BY(lcddev.height * lcddev.width * </font>
  115. <font size="3">16 / 8, data_cache_line_size),</font>
  116. <font size="3">                .out.pic_w          = lcddev.width,</font>
  117. <font size="3">                .out.pic_h          = lcddev.height,</font>
  118. <font size="3">                .out.block_offset_x = 0,</font>
  119. <font size="3">                .out.block_offset_y = 0,</font>
  120. <font size="3">                .out.srm_cm         = PPA_SRM_COLOR_MODE_RGB565,</font>
  121. <font size="3">                .rotation_angle     = PPA_SRM_ROTATION_ANGLE_90,</font>
  122. <font size="3">                .scale_x            = 2,</font>
  123. <font size="3">                .scale_y            = 1.5,</font>
  124. <font size="3">                .mirror_x           = true,</font>
  125. <font size="3">                .mode               = PPA_TRANS_MODE_BLOCKING,</font>
  126. <font size="3">            };</font>
  127. <font size="3">            ppa_do_scale_rotate_mirror(ppa_srm_handle, &oper_config);   </font>
  128. <font size="3">        }</font>
  129. <font size="3">        else if (mipidev.id == 0x9881)  /* 800p */</font>
  130. <font size="3">        {</font>
  131. <font size="3">            ppa_srm_oper_config_t oper_config = {</font>
  132. <font size="3">                .in.buffer          = camera_outbuf[buf.index],</font>
  133. <font size="3">                .in.pic_w           = format.fmt.pix.width,   </font>
  134. <font size="3">                .in.pic_h           = format.fmt.pix.height,</font>
  135. <font size="3">                .in.block_w         = format.fmt.pix.width,</font>
  136. <font size="3">                .in.block_h         = format.fmt.pix.height / 2,</font>
  137. <font size="3">                .in.block_offset_x  = 0,</font>
  138. <font size="3">                .in.block_offset_y  = 0,</font>
  139. <font size="3">                .in.srm_cm          = PPA_SRM_COLOR_MODE_RGB565,</font>

  140. <font size="3">                .out.buffer         = draw_buffer,</font>
  141. <font size="3">                .out.buffer_size    = ALIGN_UP_BY(lcddev.height * lcddev.width *</font>
  142. <font size="3">    16 / 8, data_cache_line_size),</font>
  143. <font size="3">                .out.pic_w          = lcddev.width,</font>
  144. <font size="3">                .out.pic_h          = lcddev.height,</font>
  145. <font size="3">                .out.block_offset_x = 40,</font>
  146. <font size="3">                .out.block_offset_y = 0,</font>
  147. <font size="3">                .out.srm_cm         = PPA_SRM_COLOR_MODE_RGB565,</font>
  148. <font size="3">                .rotation_angle     = PPA_SRM_ROTATION_ANGLE_90,</font>
  149. <font size="3">                .scale_x            = 1,</font>
  150. <font size="3">                .scale_y            = 1.5,</font>
  151. <font size="3">                .mirror_x           = true,</font>
  152. <font size="3">                .mode               = PPA_TRANS_MODE_BLOCKING,</font>
  153. <font size="3">            };</font>
  154. <font size="3">            ppa_do_scale_rotate_mirror(ppa_srm_handle, &oper_config);   </font>
  155. <font size="3">        }</font>
  156. <font size="3">        else if (lcddev.id == 0X4342)</font>
  157. <font size="3">        {</font>
  158. <font size="3">            ppa_srm_oper_config_t oper_config = {</font>
  159. <font size="3">                .in.buffer          = camera_outbuf[buf.index],</font>
  160. <font size="3">                .in.pic_w           = format.fmt.pix.width,   </font>
  161. <font size="3">                .in.pic_h           = format.fmt.pix.height,</font>
  162. <font size="3">                .in.block_w         = format.fmt.pix.width / 4,</font>
  163. <font size="3">                .in.block_h         = format.fmt.pix.height / 4, </font>
  164. <font size="3">                .in.block_offset_x  = 0,</font>
  165. <font size="3">                .in.block_offset_y  = 0,</font>
  166. <font size="3">                .in.srm_cm          = PPA_SRM_COLOR_MODE_RGB565,</font>

  167. <font size="3">                .out.buffer         = draw_buffer,</font>
  168. <font size="3">                .out.buffer_size    = ALIGN_UP_BY(lcddev.height * lcddev.width *</font>
  169. <font size="3">                                      16 / 8, data_cache_line_size),</font>
  170. <font size="3">                .out.pic_w          = lcddev.width,</font>
  171. <font size="3">                .out.pic_h          = lcddev.height,</font>
  172. <font size="3">                .out.block_offset_x = 0,</font>
  173. <font size="3">                .out.block_offset_y = 16,</font>
  174. <font size="3">                .out.srm_cm         = PPA_SRM_COLOR_MODE_RGB565,</font>
  175. <font size="3">                .rotation_angle     = PPA_SRM_ROTATION_ANGLE_0,</font>
  176. <font size="3">                .scale_x            = 1.5,</font>
  177. <font size="3">                .scale_y            = 1,</font>
  178. <font size="3">                .mirror_x           = true,</font>
  179. <font size="3">                .mirror_y           = true,</font>
  180. <font size="3">                .mode               = PPA_TRANS_MODE_BLOCKING,</font>
  181. <font size="3">            };</font>
  182. <font size="3">            ppa_do_scale_rotate_mirror(ppa_srm_handle, &oper_config);   </font>
  183. <font size="3">        }</font>
  184. <font size="3">        else if (lcddev.id == 0X4384)       /* PCLK时钟要调整为20MHz  */</font>
  185. <font size="3">        {</font>
  186. <font size="3">            ppa_srm_oper_config_t oper_config = {</font>
  187. <font size="3">                .in.buffer          = camera_outbuf[buf.index],</font>
  188. <font size="3">                .in.pic_w           = format.fmt.pix.width,   </font>
  189. <font size="3">                .in.pic_h           = format.fmt.pix.height,</font>
  190. <font size="3">                .in.block_w         = format.fmt.pix.width / 2,</font>
  191. <font size="3">                .in.block_h         = format.fmt.pix.height / 2,</font>
  192. <font size="3">                .in.block_offset_x  = 0,</font>
  193. <font size="3">                .in.block_offset_y  = 0,</font>
  194. <font size="3">                .in.srm_cm          = PPA_SRM_COLOR_MODE_RGB565,</font>

  195. <font size="3">                .out.buffer         = draw_buffer,</font>
  196. <font size="3">                .out.buffer_size    = ALIGN_UP_BY(lcddev.height * lcddev.width * </font>
  197. <font size="3">16 / 8, data_cache_line_size),</font>
  198. <font size="3">                .out.pic_w          = lcddev.width,</font>
  199. <font size="3">                .out.pic_h          = lcddev.height,</font>
  200. <font size="3">                .out.block_offset_x = 0,</font>
  201. <font size="3">                .out.block_offset_y = 0,</font>
  202. <font size="3">                .out.srm_cm         = PPA_SRM_COLOR_MODE_RGB565,</font>
  203. <font size="3">                .rotation_angle     = PPA_SRM_ROTATION_ANGLE_0,</font>
  204. <font size="3">                .scale_x            = 1.25,</font>
  205. <font size="3">                .scale_y            = 1,</font>
  206. <font size="3">                .mirror_x           = true,</font>
  207. <font size="3">                .mirror_y           = true,</font>
  208. <font size="3">                .mode               = PPA_TRANS_MODE_BLOCKING,</font>
  209. <font size="3">            };</font>
  210. <font size="3">            ppa_do_scale_rotate_mirror(ppa_srm_handle, &oper_config);   </font>
  211. <font size="3">        }</font>
  212. <font size="3">        else if (lcddev.id == 0X7084)       /* PCLK时钟要调整为20MHz  */</font>
  213. <font size="3">        {</font>
  214. <font size="3">            ppa_srm_oper_config_t oper_config = {</font>
  215. <font size="3">                .in.buffer          = camera_outbuf[buf.index],</font>
  216. <font size="3">                .in.pic_w           = format.fmt.pix.width,   </font>
  217. <font size="3">                .in.pic_h           = format.fmt.pix.height,</font>
  218. <font size="3">                .in.block_w         = format.fmt.pix.width / 2,</font>
  219. <font size="3">                .in.block_h         = format.fmt.pix.height / 2,</font>
  220. <font size="3">                .in.block_offset_x  = 0,</font>
  221. <font size="3">                .in.block_offset_y  = 0,</font>
  222. <font size="3">                .in.srm_cm          = PPA_SRM_COLOR_MODE_RGB565,</font>

  223. <font size="3">                .out.buffer         = draw_buffer,</font>
  224. <font size="3">                .out.buffer_size    = ALIGN_UP_BY(lcddev.height * lcddev.width * </font>
  225. <font size="3">16 / 8, data_cache_line_size),</font>
  226. <font size="3">                .out.pic_w          = lcddev.width,</font>
  227. <font size="3">                .out.pic_h          = lcddev.height,</font>
  228. <font size="3">                .out.block_offset_x = 0,</font>
  229. <font size="3">                .out.block_offset_y = 0,</font>
  230. <font size="3">                .out.srm_cm         = PPA_SRM_COLOR_MODE_RGB565,</font>
  231. <font size="3">                .rotation_angle     = PPA_SRM_ROTATION_ANGLE_0,</font>
  232. <font size="3">                .scale_x            = 1.25,</font>
  233. <font size="3">                .scale_y            = 1,</font>
  234. <font size="3">                .mirror_x           = true,</font>
  235. <font size="3">                .mirror_y           = true,</font>
  236. <font size="3">                .mode               = PPA_TRANS_MODE_BLOCKING,</font>
  237. <font size="3">            };</font>
  238. <font size="3">            ppa_do_scale_rotate_mirror(ppa_srm_handle, &oper_config);   </font>
  239. <font size="3">        }</font>
  240. <font size="3">        else if (lcddev.id == 0X7016)       /* PCLK时钟要调整为18MHz  */</font>
  241. <font size="3">        {</font>
  242. <font size="3">            ppa_srm_oper_config_t oper_config = {</font>
  243. <font size="3">                .in.buffer          = camera_outbuf[buf.index],</font>
  244. <font size="3">                .in.pic_w           = format.fmt.pix.width,   </font>
  245. <font size="3">                .in.pic_h           = format.fmt.pix.height,</font>
  246. <font size="3">                .in.block_w         = format.fmt.pix.width / 2,</font>
  247. <font size="3">                .in.block_h         = format.fmt.pix.height / 2,</font>
  248. <font size="3">                .in.block_offset_x  = 0,</font>
  249. <font size="3">                .in.block_offset_y  = 0,</font>
  250. <font size="3">                .in.srm_cm          = PPA_SRM_COLOR_MODE_RGB565,</font>

  251. <font size="3">                .out.buffer         = draw_buffer,</font>
  252. <font size="3">                .out.buffer_size    = ALIGN_UP_BY(lcddev.height * lcddev.width * </font>
  253. <font size="3">16 / 8, data_cache_line_size),</font>
  254. <font size="3">                .out.pic_w          = lcddev.width,</font>
  255. <font size="3">                .out.pic_h          = lcddev.height,</font>
  256. <font size="3">                .out.block_offset_x = 32,</font>
  257. <font size="3">                .out.block_offset_y = 12,</font>
  258. <font size="3">                .out.srm_cm         = PPA_SRM_COLOR_MODE_RGB565,</font>
  259. <font size="3">                .rotation_angle     = PPA_SRM_ROTATION_ANGLE_0,</font>
  260. <font size="3">                .scale_x            = 1.5,</font>
  261. <font size="3">                .scale_y            = 1.2,</font>
  262. <font size="3">                .mirror_x           = true,</font>
  263. <font size="3">                .mirror_y           = true,</font>
  264. <font size="3">                .mode               = PPA_TRANS_MODE_BLOCKING,</font>
  265. <font size="3">            };</font>
  266. <font size="3">            ppa_do_scale_rotate_mirror(ppa_srm_handle, &oper_config);   </font>
  267. <font size="3">        }</font>
  268. <font size="3">/* 将空闲的内存加入可捕获视频的队列 */</font>
  269. <font size="3">        if (ioctl(video_fd, VIDIOC_QBUF, &buf) != 0)    </font>
  270. <font size="3">        {</font>
  271. <font size="3">            ESP_LOGE(mipi_cam_tag, "failed to free video frame");</font>
  272. <font size="3">        }</font>
  273. <font size="3">    }</font>
  274. <font size="3">}</font>
复制代码
该函数的实现,主要先把LCD的缓冲区获取到,然后设置CAM的缓冲区,图像数据直接传输到LCD的缓冲区中。由于摄像头的分辨率跟屏幕分辨率不一致问题,所以得使用PPA外设进行裁剪,具体操作如上代码所示。在while循环中,便可通过调用ppa_do_scale_rotate_mirror函数去把图像直接显示出来。特别注意:若使用的是RGB屏,其PCLK需要调整,才能正常运行起来。
2. CMakeLists.txt文件
本例程的功能实现主要依靠MIPI驱动。要在main函数中,成功调用MIPI_CAM文件中的内容,就得需要修改main文件夹下的CMakeLists.txt文件,修改如下:
  1. <font size="3">idf_component_register(</font>
  2. <font size="3">                        SRC_DIRS </font>
  3. <font size="3">                            "."</font>
  4. <font size="3">                            "APP/MIPI_CAM" </font>
  5. <font size="3">                        INCLUDE_DIRS </font>
  6. <font size="3">                            "."</font>
  7. <font size="3">                            "APP/MIPI_CAM" </font>
  8. <font size="3">                        )</font>
复制代码
3. main.c驱动代码
在main.c里面编写如下代码。
  1. <font size="3">void app_main(void)</font>
  2. <font size="3">{</font>
  3. <font size="3">    esp_err_t ret;</font>

  4. <font size="3">    ret = nvs_flash_init();                /* 初始化NVS */</font>
  5. <font size="3">    if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)</font>
  6. <font size="3">    {</font>
  7. <font size="3">        ESP_ERROR_CHECK(nvs_flash_erase());</font>
  8. <font size="3">        ESP_ERROR_CHECK(nvs_flash_init());</font>
  9. <font size="3">    }</font>

  10. <font size="3">    led_init();                         /* LED初始化 */</font>
  11. <font size="3">    myiic_init();                       /* MYIIC初始化 */</font>
  12. <font size="3">    lcd_init();                         /* LCD屏初始化 */</font>

  13. <font size="3">    lcd_show_string(30, 50,  200, 16, 16, "ESP32-P4", RED);</font>
  14. <font size="3">    lcd_show_string(30, 70,  200, 16, 16, "CAMERA TEST", RED);</font>
  15. <font size="3">    lcd_show_string(30, 90,  200, 16, 16, "ATOM@ALIENTEK", RED);</font>
  16. <font size="3">    vTaskDelay(pdMS_TO_TICKS(500));</font>
  17. <font size="3">    lcd_clear(WHITE);</font>
  18. <font size="3">    mipi_cam_init();                    /* 摄像头初始化 */</font>

  19. <font size="3">    while (1)</font>
  20. <font size="3">    {</font>
  21. <font size="3">        LED0_TOGGLE();</font>
  22. <font size="3">        vTaskDelay(pdMS_TO_TICKS(500));</font>
  23. <font size="3">    }</font>
  24. <font size="3">}</font>
复制代码
app_main函数比较简单了,主要就是初始化所需外设后,直接调用mipi_cam_init函数,该函数内部会新建一个摄像头显示任务,直接就可以把摄像头数据显示在LCD上了。

37.4 下载验证
将程序下载到开发板后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示的内容如下图所示:

第三十七章 MIPICAMERA实验26177.png
图37.4.1 摄像头实验程序运行效果图

屏幕显示了当前摄像头的图像数据。
摄像头实验我们就讲解到这里。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-1-20 03:35

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表