OpenEdv-开源电子网

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

《STM32H7R7开发指南 V1.1 》第五十章 摄像头实验

[复制链接]

1337

主题

1353

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5711
金钱
5711
注册时间
2019-5-8
在线时间
1543 小时
发表于 昨天 09:56 | 显示全部楼层 |阅读模式
第五十章 摄像头实验

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

2)章节摘自【正点原子】STM32H7R7开发指南 V1.1

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

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

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

6)正点原子STM32开发板技术交流群:756580169


2.jpg

3.png

STM32H7R7具有DCMIPP接口,并板载了一个摄像头接口(P7),该接口可以用来连接正点原子OV5640/OV2640/OV7725等摄像头模块。本章,我们将使用STM32驱动正点原子 OV5640摄像头模块,实现摄像头功能。
本章分为如下几个小节:
50.1 OV5640和DCMIPP简介
50.2 硬件设计
50.3 程序设计
50.4 下载验证


50.1 OV5640和DCMIPP简介
本节将分为两个部分,分别介绍OV5640和STM32H7R7的DCMIPP接口简介。另外,所有OV5640的相关资料,都在光盘:A盘7,硬件资料OV5640资料 文件夹里面。

50.1.1 OV5640简介
OV5640是OV(OmniVision)公司生产的一颗1/4寸的CMOS QSXGA(2592*1944)图像传感器,提供了一个完整的500W像素摄像头解决方案,并且集成了自动对焦(AF)功能,具有非常高的性价比。
该传感器体积小、工作电压低,提供单片QSXGA摄像头和影像处理器的所有功能。通过SCCB 总线控制,可以输出整帧、子采样、缩放和取窗口等方式的各种分辨率8/10位影像数据。该产品QSXGA图像最高达到15帧/秒(1080P图像可达30帧,720P图像可达60帧,QVGA分辨率时可达120帧)。用户可以完全控制图像质量、数据格式和传输方式。所有图像处理功能过程包括伽玛曲线、白平衡、对比度、色度等都可以通过SCCB接口编程。OmmiVision 图像传感器应用独有的传感器技术,通过减少或消除光学或电子缺陷如固定图案噪声、拖尾、浮散等,提高图像质量,得到清晰稳定的彩色图像。
OV5640的特点有:
采用1.4μm*1.4μm像素大小,并且使用OmniBSI技术以达到更高性能(高灵敏度、低串扰和低噪声)
自动图像控制功能:自动曝光(AEC)、自动白平衡(AWB)、自动消除灯光条纹、自动黑电平校准(ABLC)和自动带通滤波器(ABF)等。
支持图像质量控制:色饱和度调节、色调调节、gamma校准、锐度和镜头校准等
标准的SCCB接口,兼容IIC接口
支持RawRGB、RGB(RGB565/RGB555/RGB444)、CCIR656、YUV(422/420)、YCbCr(422)和压缩图像(JPEG)输出格式
支持QSXGA(500W)图像尺寸输出,以及按比例缩小到其他任何尺寸
支持闪光灯
支持图像缩放、平移和窗口设置
支持图像压缩,即可输出JPEG图像数据
支持数字视频接口(DVP)和MIPI接口
支持自动对焦
自带嵌入式微处理器
OV5640的功能框图图如图50.1.1.1所示:


第五十章 摄像头实验1132.png
图50.1.1.1 OV5640功能框图

其中image array部分的尺寸,OV5640的官方数据并没有给出具体的数字,其最大的有效输出尺寸为:2592*1944,即500W像素,我们根据官方提供的一些应用文档,发现其设置的image array最大为:2632*1951,所以,在接下来的介绍,我们设定其image array最大为2632*1951。

1、DVP接口说明
OV5640支持数字视频接口(DVP)和MIPI接口,因为我们的STM32H7R7使用的DCMIPP接口,仅支持DVP接口,所以,OV5640必须使用DVP输出接口,才可以连接我们的STM32开发板。
OV5640提供一个10位DVP接口(支持8位接法),其MSB和LSB可以程序设置先后顺序,正点原子OV5640模块采用默认的8位连接方式,如图50.1.1.2所示:


第五十章 摄像头实验1509.png
图50.1.1.2 OV5640默认8位连接方式

OV5640的寄存器通过SCCB时序访问并设置,SCCB时序和IIC时序十分类似。在本章我们不做介绍,请大家参考光盘《OmniVision Technologies Seril Camera Control Bus(SCCB) Specification》这个文档。

2、窗口设置说明
接下来,我们介绍一下OV5640的:ISP(Image Signal Processor)输入窗口设置、预缩放窗口设置和输出大小窗口设置,这几个设置与我们的正常使用密切相关,有必要了解一下。他们的设置关系,如图50.1.1.3所示:


第五十章 摄像头实验1796.png
图50.1.1.3 OV5640各窗口设置关系

ISP输入窗口设置(ISP input size)
该设置允许用户设置整个传感器区域(physical pixel size ,2632*1951)的感兴趣部分,也就是在传感器里面开窗(X_ADDR_ST、Y_ADDR_ST、X_ADDR_END和Y_ADDR_END),开窗范围从0*0~2632*1951都可以设置,该窗口所设置的范围,将输入ISP进行处理。
ISP输入窗口,通过:0X3800~0X3807等8个寄存器进行设置,这些寄存器的定义请看:OV5640_CSP3_DS_2.01_Ruisipusheng.pdf 这个文档(下同)。
预缩放窗口设置(pre-scaling size)
该设置允许用户在ISP输入窗口的基础上,再次设置将要用于缩放的窗口大小。该设置仅在ISP输入窗口内进行x/y方向的偏移(X_OFFSET/Y_OFFSET)。通过:0X3810~0X3813等4个寄存器进行设置。
输出大小窗口设置(data output size)
该窗口是以预缩放窗口为原始大小,经过内部DSP进行缩放处理后,输出给外部的图像窗口大小。它控制最终的图像输出尺寸(X_OUTPUT_SIZE/Y_OUTPUT_SIZE)。通过:0X3808~0X380B等4个寄存器进行设置。注意:当输出大小窗口与预缩放窗口比例不一致时,图像将进行缩放处理(会变形),仅当两者比例一致时,输出比例才是1:1(正常)。  
图50.1.1.3中,右侧data output size区域,才是OV5640输出给外部的图像尺寸,也就是显示在LCD上面的图像大小。输出大小窗口与预缩放窗口比例不一致时,会进行缩放处理,在LCD上面看到的图像将会变形。

3、输出时序说明
接下来,我们介绍一下OV5640的图像数据输出时序。首先我们简单介绍一些定义:
QSXGA,这里指:分辨率为2592*1944的输出格式,类似的还有:QXGA(2048*1536)、UXGA(1600*1200)、SXGA(1280*1024)、WXGA+(1440*900)、WXGA(1280*800)、XGA(1024*768)、SVGA(800*600)、VGA(640*480)、QVGA(320*240)和QQVGA(160*120)等。
PCLK,即像素时钟,一个PCLK时钟,输出一个像素(或半个像素)。
VSYNC,即帧同步信号。
HREF /HSYNC,即行同步信号。
OV5640的图像数据输出(通过Y[9:0])就是在PCLK,VSYNC和HREF/ HSYNC的控制下进行的。首先看看行输出时序,如图50.1.1.4所示:


第五十章 摄像头实验2934.png
图50.1.1.4 OV5640行输出时序

从上图可以看出,图像数据在HREF为高的时候输出,当HREF变高后,每一个PCLK时钟,输出一个8位/10位数据。我们采用8位接口,所以每个PCLK输出1个字节,且在RGB/YUV输出格式下,每个tp=2个Tpclk,如果是Raw格式,则一个tp=1个Tpclk。比如我们采用QSXGA时序,RGB565格式输出,每2个字节组成一个像素的颜色(低字节在前,高字节在后),这样每行输出总共有2592*2个PCLK周期,输出2592*2个字节。
再来看看帧时序(QSXGA模式),如图50.1.1.5所示:


第五十章 摄像头实验3210.png
图50.1.1.5 OV5640帧时序

上图清楚的表示了OV5640在QSXGA模式下的数据输出。我们按照这个时序去读取OV5640的数据,就可以得到图像数据。

4、自动对焦(Auto Focus)说明
OV5640由内置微型控制器完成自动对焦,并且VCM(Voice Coil Motor,即音圈马达)驱动器也已集成在传感器内部。微型控制器的控制固件(firmware)从主机下载。当固件运行后,内置微型控制器从OV5640传感器读得自动对焦所需的信息,计算并驱动VCM马达带动镜头到达正确的对焦位置。主机可以通过IIC命令控制微型控制器的各种功能。
OV5640的自动对焦命令(通过SCCB总线发送),如表50.1.1.1所示:


1.png
表50.1.1.1 OV5640自动对焦命令

OV5640内部的微控制器收到自动对焦命令后会自动将CMD_MAIN(0X3022)寄存器数据清零,当命令完成后会将CMD_ACK(0X3023)寄存器数据清零。
自动对焦(AF)过程
①在第一次进入图像预览的时候(图像可以正常输出时),下载固件(firmware)。
②拍照前,自动对焦,对焦完成后,拍照。
③拍照完毕,释放马达到初始状态。
接下来,我们分别说明。
①下载固件
OV5640初始化完成后,就可以下载AF自动对焦固件了,其操作和下载初始化参数类似,AF固件下载地址为:0X8000,初始化数组由厂家提供(本例程该数组保存在ov5640af.h里面),下载固件完成后,通过检查0X3029寄存器的值,来判断固件状态(等于0X70,说明正常)。
②自动对焦
OV5640支持单次自动对焦和持续自动对焦,通过0X3022寄存器控制。单次自动对焦过程如下:
1,将0X3022寄存器写为0X03,开始单点对焦过程。
2,读取寄存器0X3029,如果返回值为0X10,代表对焦已完成。
3,写寄存器0X3022为0X06,暂停对焦过程,使镜头将保持在此对焦位置。
其中,前两步是必须的,第三步,可以不要,因为单次自动对焦完成以后,就不会继续自动对焦了,镜头也就不会动了。
持续自动对焦过程如下:
1,将0X22寄存器写为0X08,释放马达到初始位置(对焦无穷远)。
2,将0X3022寄存器写为0X04,启动持续自动对焦过程。
3,读取寄存器0X3023,等待命令完成。
4,当OV5640每次检测到失焦时,就会自动进行对焦(一直检测)。
③释放马达,结束自动对焦
最后,在拍照完成,或者需要结束自动对焦的时候,我们对在寄存器0X3022写入0X08,即可释放马达,结束自动对焦。
最后说一下OV5640的图像数据格式,我们一般用2种输出方式:RGB565和JPEG。当输出RGB565格式数据的时候,时序完全就是上面两幅图介绍的关系。以满足不同需要。而当输出数据是JPEG数据的时候,同样也是这种方式输出(所以数据读取方法一模一样),不过PCLK数目大大减少了,且不连续,输出的数据是压缩后的JPEG数据,输出的JPEG数据以:0XFF,0XD8开头,以0XFF,0XD9结尾,且在0XFF,0XD8之前,或者0XFF,0XD9之后,会有不定数量的其他数据存在(一般是0),这些数据我们直接忽略即可,将得到的0XFF,0XD8~0XFF,0XD9之间的数据,保存为.jpg/.jpeg文件,就可以直接在电脑上打开看到图像了。
OV5640自带的JPEG输出功能,大大减少了图像的数据量,使得其在网络摄像头、无线视频传输等方面具有很大的优势。OV5640我们就介绍到这,关于OV5640更详细的介绍,请大家参考:A盘→7,硬件资料→OV5640资料→OV5640_CSP3_DS_2.01_Ruisipusheng.pdf。
正点原子OV5640摄像头模块
本实验,我们将使用STM32H7R7开发板的DCMIPP接口连接正点原子OV5640摄像头模块,该模块采用8位数据输出接口,自带24M有源晶振,无需外部提供时钟,模组支持自动对焦功能,且支持闪光灯,整个模块只需提供3.3V 供电即可正常使用。
正点原子OV5640摄像头模块外观如图50.1.1.6所示:


第五十章 摄像头实验5204.png
图50.1.1.6 正点原子OV5640摄像头模块外观图

模块原理图如图50.1.1.7所示:

第五十章 摄像头实验5254.png
图50.1.1.7 正点原子OV5640摄像头模块原理图

从上图可以看出,正点原子OV5640摄像头模块自带了有源晶振,用于产生24M时钟作为OV5640的XCLK输入,模块的闪光灯(LED1&LED2)由OV5640的STROBE脚控制(可编程控制)。同时自带了稳压芯片,用于提供OV5640稳定的2.8V和1.5V工作电压,模块通过一个2*9的双排排针(P7)与外部通信,与外部的通信信号如表50.1.1.2所示:

2.png
表50.1.1.2 OV5640模块信号及其作用描述

50.1.2 STM32H7R7 DCMIPP接口简介
STM32H7R7自带了一个数字摄像头(DCMIPP)接口,该接口是一个同步并行接口,能够将获取的像素经过处理后(如抽取、裁剪)存入到内存中。DCMIPP主要特性如下:
集成输入并行接口(支持16bit、120MHz,)
像素格式:RGB565、RGB888、YUV422、原始拜尔(Bayer)格式/Mono 8/10/12/14 和比特流(JPEG)
Pipe转存最大支持分辨率为4096*4096
典型转存帧率为30fps,通过降低图像分辨率可达到120fps
通过pipe管进行传输和管理
DCMIPP接口包括如下一些信号:
1,数据输入(D[0:13]),用于接摄像头的数据输出,接OV5640我们只用了8位数据。
2,水平同步(行同步)输入(HSYNC),用于接摄像头的HSYNC/HREF信号。
3,垂直同步(场同步)输入(VSYNC),用于接摄像头的VSYNC信号。
4,像素时钟输入(dcmipp_pxclk),用于接摄像头的PCLK信号。
DCMIPP接口是一个同步并行接口,可接收高速(可达120MB/s)数据流。该接口包含多达14条数据线(D13-D0)和一条像素时钟线(dcmipp_pxclk)。像素时钟的极性可以编程,因此可以在像素时钟的上升沿或下降沿捕获数据。
从摄像头接收的数据可以按行/帧来组织(原始YUV/RGB/拜尔模式),也可以是一系列 JPEG图像。
数据流可由可选的 HSYNC(水平同步)信号和 VSYNC(垂直同步)信号硬件同步,或者通 过数据流中嵌入的同步码同步。

50.1.2.1 DCMIPP接口功能概述
STM32H7R7 DCMIPP接口的框图如图50.1.2.1.1所示:


第五十章 摄像头实验6424.png
图50.1.2.1.1 DCMIPP接口框图

上图左边可以看到的是DCMIPP的内部信号,如下表所示:

3.png
表50.1.2.1.1 DCMIPP内部信号

dcmipp_it_global,DCMIPP 中断信号。DCMIPP每输出一帧完整的图像数据时,就会产生一个DCMIPP中断,我们接收到中断后,就可以处理pipe0传输到指定内存的图像数据了。
dcmipp_pclk和dcmipp_pclk,DCMIPP 接口时钟,时钟源自来于APB时钟或者AXI时钟。
上图左边的DCMIPP物理接口,具体如下表所示:


4.png
表50.1.2.1.3 DCMIPP物理接口

该接口由11/13/15/17个输入信号组成。仅支持从模式。根据 DCMIPP_PRCR 寄存器中 EDM[1:0] 位的设置,摄像头接口可以捕获16位数据。如果使用的位数少于16,则必须将未使用的输入引脚接地。
DCMIPP数据与dcmipp_pxclk(即PCLK)保持同步,并根据像素时钟的极性在像素时钟上升沿/下降沿发生变化。
DCMIPP_HSYNC(HREF)信号指示行的开始/结束。
DCMIPP_VSYNC 信号指示帧的开始/结束。
本实验我们用到DCMIPP的8位数据宽度,通过设置DCMIPP_PRCR中的EDM[2:0]=00设置。此时DCMIPP_D0~D7有效,DCMIPP_D8~D15上的数据则忽略,这个时候,每次需要1个像素时钟来捕获一个8位数据。
DCMIPP接口支持两种同步方式:内嵌码同步和硬件(HSYNC和VSYNC)同步。我们使用硬件同步,硬件同步模式下将使用两个同步信号 (HSYNC/VSYNC)。根据摄像头模块/模式的不同,可能在水平/垂直同步期间内发送数据。由于系统会忽略HSYNC/VSYNC信号有效电平期间内接收的所有数据,HSYNC/VSYNC 信号相当于消隐信号。
为了正确地将图像传输到指定内存缓冲区,数据传输将与VSYNC信号同步。选择硬件同步模式并启用捕获(DCMIPP_PRCR中的ESS位置0)时,数据传输将与VSYNC信号的 无效电平同步(开始下一帧时)。之后传输便可以连续执行,由Pipe0将连续帧传输到多个连续的缓冲区或一个具有循环特性的缓冲区。为了Pipe管理连续帧,每一帧结束时都由事件管理器输出事件,并产生DCMIPP中断,我们可以利用这个帧中断来判断是否有一帧数据采集完成,方便处理数据。
DCMIPP接口的捕获模式支持:快照模式和连续采集模式。一般我们使用连续采集模式,通过DCMIPP_P0FCTCR中的CPTMODE位设置。
关于DCMIPP接口的其他特性,我们这里就不再介绍了,请大家参考《STM32H7Rx参考手册_V6(英文版).pdf》第31章相关内容。

50.1.2.2 DCMIPP寄存器
本实验,我们将OV5640默认配置为QVGA输出,也就是320*240的分辨率,输出信号设置为:VSYNC高电平有效,HREF高电平有效,输出数据在PCLK的下降沿输出(即上升沿的时候,MCU才可以采集)。这样,STM32H7R7的DCMIPP接口就必须设置为:VSYNC低电平有效、HSYNC低电平有效和PIXCLK上升沿有效,这些设置都是通过DCMIPP_PRCR寄存器控制。
DCMIPP  并行接口控制寄存器(DCMIPP_PRCR)
DCMIPP并行接口控制寄存器描述如图50.1.2.2.1所示:


第五十章 摄像头实验8264.png
图50.1.2.2.1 DCMIPP_PRCR寄存器

ENABLE,该位用于设置是否使能DCMIPP,不过,在使能之前,必须将其他配置设置好。
FORMAT[7:0],该位用于设置格式,我们用设置为RGB565格式。
EDM[2:0],设置扩展数据模式,选择000:接口每个像素时钟捕获8位数据。
VSPOL,该位用于设置垂直同步极性,也就是VSYNC引脚上面,数据无效时的电平状态,根据前面说所,我们应该设置为0。
HSPOL,该位用于设置水平同步极性,也就是HSYNC引脚上面,数据无效时的电平状态,同样应该设置为0。
PCKPOL,该位用于设置像素时钟极性,我们用上升沿捕获,所以设置为1。
ESS,内嵌码同步选择,我们选择硬件同步,默认置0。
DCMIPP_PRCR寄存器的其他位,我们就不介绍了,另外DCMIPP的其他寄存器这里也不再介绍,请大家参考《STM32H7Rx参考手册_V6(英文版).pdf》第31.10.8小节。

50.2 硬件设计

1. 例程功能
1、本实验开机后,初始化摄像头模块(OV5640),如果初始化成功,则提示选择模式:RGB565模式或者JPEG模式。KEY0用于选择RGB565模式,KEY1用于选择JPEG模式。
2、当使用RGB565时,输出图像(固定为:WXGA)将经过缩放处理(完全由OV5640的DSP控制),显示在LCD上面(默认开启连续自动对焦)。我们可以通过KEY_UP按键选择:1:1显示,即不缩放,图片不变形,但是显示区域小(液晶分辨率大小),或者缩放显示,即将1280*800的图像压缩到液晶分辨率尺寸显示,图片变形,但是显示了整个图片内容。通过KEY0按键,可以执行一次自动对焦;KEY1按键,可以设置特效;KEY_UP按键,可以设置尺寸缩放。
3、当使用JPEG模式时,图像可以设置默认是QVGA 320*240尺寸,采集到的JPEG数据将先存放到STM32的RAM内存里面,每当采集到一帧数据,就会关闭DMA传输,然后将采集到的数据发送到串口2(此时可以通过上位机软件(ATK-CAM.exe)接收,并显示图片),之后再重新启动DMA传输。通过按键KEY_UP设置输出图片的尺寸、按键KEY0设置对比度、按键KEY1启动单次自动对焦、按键KEY2设置特效。
4、LED0闪烁,提示程序运行。LED1用于指示帧中断。

2. 硬件资源
1)LED灯
       LED0 :LED0 – PD14
       LED1: LED1 – PC0
2)串口1(PB14/PB15连接在板载USB转串口芯片CH340上面)
3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(包括MCU屏和RGB屏,都支持)
4)独立按键 :KEY0 – PE9、KEY1 – PE8、KEY2  - PE7、WK_UP - PC13
5)串口2 (USART2_TX – PD5,外接串口线/用杜邦线连接到CH340上面)
6)DCMIPP接口(用于驱动OV5640摄像头模块)
7)定时器6(用于打印摄像头帧率等信息)
8)正点原子OV5640摄像头模块,连接关系为:
       OV5640模块 -----------   STM32开发板
       OV_D0~D7  ------------   PC6/PC7/PE0/PE1/PE4/PB6/PB8/PB9
       OV_SCL       ------------   PM3
       OV_SDA       ------------   PM2
       OV_VSYNC ------------   PB7
       OV_HSYNC ------------   PG3
       OV_RST       ------------   PE2
       OV_PCLK   ------------    PA6
       OV_PWDN  ------------   PF5

3. 原理图
开发板板载的摄像头模块接口与MCU的连接关系,如下图所示:


第五十章 摄像头实验9961.png
图50.2.1 摄像头接口与开发板连接示意图

这些GPIO口的线都在开发板上连接到P7端口,所以我们只需要将OV5640摄像头模块插上开发板的连接座子就好了(摄像头模块正面往外插)。

第五十章 摄像头实验10055.png
图50.2.2 OV5640摄像头模块与开发板连接实物图

50.3 程序设计

50.3.1 DCMIPP的HAL库驱动
DCMIPP在HAL库中的驱动代码在stm32h7rsxx_hal_dcmipp.c文件(及其头文件)中。

1. HAL_DCMIPP_Init函数
DCMIPP初始化函数,其声明如下:
  1. HAL_StatusTypeDef HAL_DCMIPP_Init(DCMIPP_HandleTypeDef *hdcmipp);
复制代码
函数描述:
用于初始化DCMIPP。
函数形参:
形参1是DCMIPP_HandleTypeDef结构体类型指针变量,其定义如下:

  1. typedef struct
  2. {
  3.   DCMIPP_TypeDef                    *Instance;    /* DCMIPP 外设寄存器基地址 */
  4.   __IO HAL_DCMIPP_StateTypeDef      State;        /* DCMIPP 工作状态 */
  5.   __IO HAL_DCMIPP_PipeStateTypeDef  PipeState[DCMIPP_NUM_OF_PIPES];
  6.                                                   /* DCMIPP 管道状态 */
  7.   __IO uint32_t                     ErrorCode;    /* DCMIPP 错误代码 */
  8. } DCMIPP_HandleTypeDef;
复制代码
下面重点介绍DCMIPP_ParallelConfTypeDef结构体,其定义如下:
  1. typedef struct
  2. {
  3.   uint32_t  Format;                    /* 格式 */
  4.   uint32_t  VSPolarity;                /* 设置垂直同步的有效电平 */
  5.   uint32_t  HSPolarity;                /* 设置水平同步的有效边沿 */
  6.   uint32_t  PCKPolarity;               /* 设置像素时钟的有效边沿 */  
  7. uint32_t  ExtendedDataMode ;         /* 设置数据线的宽度(扩展数据模式) */
  8.   uint32_t  SynchroMode;               /* 同步方式选择硬件同步模式还是内嵌码模式 */
  9.   DCMIPP_EmbeddedSyncCodesTypeDef SynchroCodes; /* 分隔符设置 */
  10.   uint32_t  SwapBits;                  /* 使能/失能Swap位*/
  11.   uint32_t  SwapCycles;                /* 使能/失能Swap循环 */
  12. } DCMIPP_ParallelConfTypeDef;
复制代码
1)Format::用于设置格式,我们选择的是RGB565模式。
2)VSPolarity:用于设置VSYNC的有效电平,当VSYNC信号线表示为有效电平时,表示新的一帧数据传输完成,它可以被设置为高电平有效或低电平有效。
3)HSPolarity:用于设置HSYNC的有效电平,当 HSYNC 信号线表示为有效电平时,表示新的一行数据传输完成,它可以被设置为高电平有效或低电平有效。
4)PCKPolarity:用来设置像素时钟极性为上升沿有效还是下降沿有效。
5)ExtendedDataMode:用于设置扩展数据模式,可设置为8/10/12或者14位数据宽度。
6)SynchroMode:用于设置 DCMI数据的同步模式,可以选择为硬件同步方式或内嵌码方式。
如果选择硬件同步值DCMI_SYNCHRO_HARDWARE,那么数据捕获由HSYNC/VSYNC信号同步,如果选择内嵌码同步方式值DCMI_SYNCHRO_EMBEDDED,那么数据捕获由数据流中嵌入的同步码同步。
7)SyncroCodes:用于设置分隔码,包括:帧结束分隔码,行结束分隔码,行开始分隔码以及帧开始分隔码。
8)SwapBits:用于使能或失能Swap位。
9)SwapCycles:用于使能或失能Swap循环。
函数返回值:
HAL_StatusTypeDef枚举类型的值。
DCMIPP数据配置步骤
1)配置OV5640控制引脚,并配置OV5640工作模式。
在启动DCMIPP之前,我们先设置好OV5640。OV5640通过OV_SCL和OV_SDA进行寄存器配置,同时还有OV_PWDN/OV_RESET等信号,我们也需要配置对应IO状态,先设置OV_PWDN为0,退出掉电模式,然后拉低OV_RESET复位OV5640,之后再设置OV_RESET为1,结束复位,然后就是对OV5640的寄存器进行配置了。然后,可以根据我们的需要,设置成RGB565输出模式,还是JPEG输出模式。
2)配置相关引脚的模式和复用功能(AF13),使能时钟。
OV5640配置好之后,再设置DCMIPP接口与摄像头模块连接的IO口,使能IO和DCMIPP时钟,然后设置相关IO口为复用功能模式,复用功能选择AF13(DCMIPP复用)。
DCMIPP时钟使能方法:

  1. __HAL_RCC_DCMIPP_CLK_ENABLE();
复制代码
引脚模式配置就是通过HAL_GPIO_Init函数来配置。
3)配置DCMIPP相关设置,初始化DCMIPP接口。
这一步,主要通过DCMIPP_PRCR寄存器设置,包括VSPOL/HSPOL/PCKPOL/数据宽度等重要参数,都在这一步设置。HAL库提供了DCMIPP初始化函数HAL_DCMIPP_Init用于注册DCMIPP,用HAL_DCMIPP_PIPE_SetConfig函数配置DCMIPP管道,初始化函数声明如下:
HAL_StatusTypeDef HAL_DCMIPP_Init(DCMIPP_HandleTypeDef *hdcmipp);
该结构体第一个成员变量Instance用来指向寄存器基地址,设置为DCMIPP即可。
其他三个成员变量主要是DCMIPP的一些状态信息,重点需要配置的是DCMIPP管通道配置,函数声明如下:

  1. HAL_StatusTypeDef HAL_DCMIPP_PARALLEL_SetConfig(DCMIPP_HandleTypeDef *hdcmipp,
  2.                            const DCMIPP_ParallelConfTypeDef *pParallelConfig);
复制代码
第一个结构体参数是使用对应的DCMIPP,第二个结构体参数主要是DCMIPP的配置,我们重点看第二个DCMIPP_ParallelConfTypeDef就好,上面各个成员变量的作用都有介绍到,如Format是配置图像格式,我们这里就不一一介绍了。
同样,HAL库也提供了DCMIPP接口的MSP初始化回调函数:

  1. void HAL_DCMI_MspInit(DCMI_HandleTypeDef* hdcmi);
复制代码
一般情况下,该函数内部编写时钟使能,IO初始化以及NVIC相关程序。
4)配置DCMIPP管道,开启中断。
本实验我们采用连续模式采集,并将采集到的数据输出到LCD(RGB565模式)或内存(JPEG模式),我们通过PIPE通道进行转存,我们配置好PIPE通道后,通过DCMIPP_P0PPM0AR1寄存器设置图像数据的存放地址,也就是目的地址,目的地址是我们开辟的一片转存地址,大小为1024 * 1024 * 2 / 4。DCMIPP的PIPE管通过有专用的事件管理和DCMIPP中断,我们通过这些标志就可以获取摄像头图像采集的信息了。
5)设置OV5640的图像输出大小,使能DCMIPP捕获。
图像输出大小设置,分两种情况:在RGB565模式下,我们根据LCD的尺寸,设置输出图像大小,以实现全屏显示(图像可能因缩放而变形);在JPEG模式下,我们可以自由设置输出图像大小(可不缩放);最后,开启DCMIPP捕获,即可正常工作了。


50.3.2 程序解析

1. DCMIPP驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。DCMIPP驱动源码包括两个文件:dcmipp.c和dcmipp.h。
dcmipp.h头文件只是一些声明,下面直接开始介绍dcmipp.c文件,首先是DCMIPP初始化函数,其定义如下:

  1. /**
  2. * @brief       DCMIPP 初始化
  3. * [url=home.php?mod=space&uid=60778]@note[/url]        IO对应关系如下:
  4. *              摄像头模块 ------------  STM32开发板
  5. *               OV_D0~D7  ------------  PC6/PC7/PE0/PE1/PE4/PB6/PB8/PB9
  6. *               OV_SCL    ------------  PM3
  7. *               OV_SDA    ------------  PM2
  8. *               OV_VSYNC  ------------  PB7
  9. *               OV_HREF   ------------  PG3
  10. *               OV_RESET  ------------  PE2
  11. *               OV_PCLK   ------------  PA6
  12. *               OV_PWDN   ------------  PF5
  13. *              本函数仅初始化OV_D0~D7/OV_VSYNC/OV_HREF/OV_PCLK等信号(11个).
  14. * [url=home.php?mod=space&uid=271674]@param[/url]       无
  15. * @retval      无
  16. */
  17. void dcmipp_init(void)
  18. {
  19.     /* 初始化DCMIPP */
  20.     g_dcmipp_handle.Instance = DCMIPP;
  21.     HAL_DCMIPP_Init(&g_dcmipp_handle);
  22.    
  23. g_dcmipp_parallelcfg.Format           = DCMIPP_FORMAT_RGB565;      
  24. /* RGB565帧格式 */
  25.     g_dcmipp_parallelcfg.SwapCycles       = DCMIPP_SWAPCYCLES_ENABLE;
  26.     g_dcmipp_parallelcfg.SwapBits         = DCMIPP_SWAPBITS_DISABLE;
  27. g_dcmipp_parallelcfg.VSPolarity       = DCMIPP_VSPOLARITY_LOW;      
  28. /* VSYNC 低电平有效 */
  29. g_dcmipp_parallelcfg.HSPolarity       = DCMIPP_HSPOLARITY_LOW;      
  30. /* HSYNC 低电平有效 */
  31. g_dcmipp_parallelcfg.PCKPolarity      = DCMIPP_PCKPOLARITY_RISING;  
  32. /* PCLK上升沿有效 */
  33. g_dcmipp_parallelcfg.ExtendedDataMode = DCMIPP_INTERFACE_8BITS;     
  34. /* 8位数据格式 */
  35. g_dcmipp_parallelcfg.SynchroMode      = DCMIPP_SYNCHRO_HARDWARE;   
  36. /* 硬件同步HSYNC,VSYNC */
  37.     g_dcmipp_parallelcfg.SynchroCodes.FrameEndCode = 0;
  38.     g_dcmipp_parallelcfg.SynchroCodes.FrameStartCode = 0;
  39.     g_dcmipp_parallelcfg.SynchroCodes.LineEndCode = 0;
  40.     g_dcmipp_parallelcfg.SynchroCodes.LineStartCode = 0;
  41.     HAL_DCMIPP_PARALLEL_SetConfig(&g_dcmipp_handle, &g_dcmipp_parallelcfg);
  42.    
  43.     g_dcmipp_pipecfg.FrameRate = DCMIPP_FRAME_RATE_ALL;     /* 全帧捕获 */
  44. HAL_DCMIPP_PIPE_SetConfig(&g_dcmipp_handle, DCMIPP_PIPE0,
  45. &g_dcmipp_pipecfg);
  46.    
  47.     /* 使能DCMIPP及其帧捕获完成中断 */
  48. __HAL_DCMIPP_DISABLE_IT(&g_dcmipp_handle, DCMIPP_IT_PIPE0_FRAME |
  49. DCMIPP_IT_PIPE0_OVR | DCMIPP_IT_AXI_TRANSFER_ERROR |
  50. DCMIPP_IT_PARALLEL_SYNC_ERROR | DCMIPP_IT_PIPE0_VSYNC |
  51. DCMIPP_IT_PIPE0_LINE);
  52.     __HAL_DCMIPP_ENABLE_IT(&g_dcmipp_handle, DCMIPP_IT_PIPE0_FRAME);
  53. }
复制代码
该函数主要对DCMIPP_HandleTypeDef结构体成员赋值并初始化,最后关闭所有中断,只开启帧中断,使能DCMIPP。而DCMIPP接口的GPIO口的初始化是在HAL_DCMIPP_MspInit回调函数中完成,其定义如下:
  1. /**
  2. * @brief   HAL库DCMIPP初始化MSP回调函数
  3. * @param   phdcmipp: DCMIPP句柄指针
  4. * @retval  无
  5. */
  6. void HAL_DCMIPP_MspInit(DCMIPP_HandleTypeDef *phdcmipp)
  7. {
  8.     GPIO_InitTypeDef gpio_init_struct = {0};
  9.    
  10.     if (phdcmipp->Instance == DCMIPP)
  11.     {
  12.         /* 使能时钟 */
  13.         __HAL_RCC_DCMIPP_CLK_ENABLE();
  14.         __HAL_RCC_DCMIPP_FORCE_RESET();
  15.         __HAL_RCC_DCMIPP_RELEASE_RESET();
  16.         
  17.         __HAL_RCC_GPIOA_CLK_ENABLE();
  18.         __HAL_RCC_GPIOB_CLK_ENABLE();
  19.         __HAL_RCC_GPIOC_CLK_ENABLE();
  20.         __HAL_RCC_GPIOE_CLK_ENABLE();
  21.         __HAL_RCC_GPIOG_CLK_ENABLE();
  22.         
  23.         /* 初始化引脚 */
  24.         gpio_init_struct.Pin = GPIO_PIN_6;
  25.         gpio_init_struct.Mode = GPIO_MODE_AF_PP;
  26.         gpio_init_struct.Pull = GPIO_PULLUP;
  27.         gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  28.         gpio_init_struct.Alternate = GPIO_AF13_DCMIPP;
  29.         HAL_GPIO_Init(GPIOA, &gpio_init_struct);
  30.         
  31.         gpio_init_struct.Pin = GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8 |
  32.         GPIO_PIN_9;
  33.         HAL_GPIO_Init(GPIOB, &gpio_init_struct);
  34.         
  35.         gpio_init_struct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
  36.         HAL_GPIO_Init(GPIOC, &gpio_init_struct);
  37.         
  38.         gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4;
  39.         HAL_GPIO_Init(GPIOE, &gpio_init_struct);
  40.         
  41.         gpio_init_struct.Pin = GPIO_PIN_3;
  42.         gpio_init_struct.Alternate = GPIO_AF5_DCMIPP;
  43.         HAL_GPIO_Init(GPIOG, &gpio_init_struct);
  44.         
  45.         /* 配置中断 */
  46.         HAL_NVIC_SetPriority(DCMIPP_IRQn, 0, 0);
  47.         HAL_NVIC_EnableIRQ(DCMIPP_IRQn);
  48.     }
  49. }
复制代码
下面介绍的是DCMIPP启动传输和关闭传输函数,它们的定义分别如下:
  1. /**
  2. * @brief   启动DCMIPP传输
  3. * @param   无
  4. * @retval  无
  5. */
  6. void dcmipp_start(void)
  7. {
  8.     if (lcddev.wramcmd != 0)
  9.     {
  10.         lcd_set_cursor(0, 0);               /* 设置坐标到原点 */
  11.         lcd_write_ram_prepare();            /* 开始写入GRAM */
  12.     }
  13.     SCB_CleanInvalidateDCache();
  14.     HAL_DCMIPP_PIPE_Start(&g_dcmipp_handle, DCMIPP_PIPE0, (uint32_t)camera_date_buf, DCMIPP_MODE_CONTINUOUS);
  15. }

  16. /**
  17. * @brief   停止DCMIPP传输
  18. * @param   无
  19. * @retval  无
  20. */
  21. void dcmipp_stop(void)
  22. {
  23.     HAL_DCMIPP_PIPE_Stop(&g_dcmipp_handle, DCMIPP_PIPE0);
  24. }
复制代码
下面介绍的是DCMIPP中断服务函数(及其回调函数)、DCMIPP帧捕获完成回调函数,它们的定义分别如下:
  1. /**
  2. * @brief   DCMIPP中断服务函数
  3. * @param   无
  4. * @retval  无
  5. */
  6. void DCMIPP_IRQHandler(void)
  7. {
  8.     HAL_DCMIPP_IRQHandler(&g_dcmipp_handle);
  9. }

  10. /**
  11. * @brief   HAL库DCMIPP帧捕获完成回调函数
  12. * @param   pHdcmipp: DCMIPP句柄指针
  13. * @param   Pipe    : 配置的管道
  14. * @retval  无
  15. */
  16. void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *pHdcmipp,          uint32_t Pipe)
  17. {
  18.     if (pHdcmipp->Instance == DCMIPP)
  19.     {
  20.         jpeg_data_process();                /* jpeg数据处理 */
  21.         LED1_TOGGLE();                      /* LED1闪烁 */
  22.         g_ov_frame++;
  23.    }
  24. }
复制代码
其中:HAL_DCMIPP_PIPE_FrameEventCallback函数,用于处理帧中断,可以实现帧率统计(需要定时器支持)和JPEG数据处理等
DCMIPP驱动代码就介绍到这里。


2. OV5640驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。OV5640驱动源码包括六个文件:ov5640.c、ov5640.h、ov5640af.h、ov5640cfg.h、sccb.c和sccb.h。
其中sccb.c和sccb.h是SCCB通信接口的驱动代码。OV5640的寄存器通过SCCB时序访问并设置,SCCB时序和IIC时序十分类似,这里我们也是用软件模拟SCCB时序。
下面,首先介绍sccb.h头文件中SCCB的IO口宏定义,其定义情况如下:

  1. /* 引脚 定义 */
  2. #define SCCB_SCL_GPIO_PORT               GPIOM
  3. #define SCCB_SCL_GPIO_PIN                GPIO_PIN_3
  4. #define SCCB_SCL_GPIO_CLK_ENABLE()      
  5. do{ __HAL_RCC_GPIOM_CLK_ENABLE(); }while(0)   /* PM口时钟使能 */

  6. #define SCCB_SDA_GPIO_PORT               GPIOM
  7. #define SCCB_SDA_GPIO_PIN                GPIO_PIN_2
  8. #define SCCB_SDA_GPIO_CLK_ENABLE()      
  9. do{ __HAL_RCC_GPIOM_CLK_ENABLE(); }while(0)   /* PM口时钟使能 */

  10. /* IO操作函数 */
  11. #define SCCB_SCL(x)    do{ x ? \
  12.   HAL_GPIO_WritePin(SCCB_SCL_GPIO_PORT, SCCB_SCL_GPIO_PIN, GPIO_PIN_SET) : \
  13.   HAL_GPIO_WritePin(SCCB_SCL_GPIO_PORT, SCCB_SCL_GPIO_PIN, GPIO_PIN_RESET); \
  14.                           }while(0)       /* SCL */

  15. #define SCCB_SDA(x)   do{ x ? \
  16.    HAL_GPIO_WritePin(SCCB_SDA_GPIO_PORT, SCCB_SDA_GPIO_PIN, GPIO_PIN_SET) : \
  17.    HAL_GPIO_WritePin(SCCB_SDA_GPIO_PORT, SCCB_SDA_GPIO_PIN, GPIO_PIN_RESET); \
  18.                           }while(0)       /* SDA */

  19. #define SCCB_SDA_READ    HAL_GPIO_ReadPin(SCCB_SDA_GPIO_PORT,
  20. SCCB_SDA_GPIO_PIN) /* 读取SDA */
复制代码
SCCB时序有两根信号线(SCCB_SCL和SCCB_SDA),所以这里定义了两个IO口(PM3和PM2)来控制。IO操作函数有三个,包括SCCB_SCL用于控制时钟,SCCB_SDA是写IO口输出的值为逻辑1或者逻辑0,SCCB_SDA_READ是读取IO口的值,逻辑1或者逻辑0。
接下来介绍sccb.c文件的代码,首先是SCCB接口初始化函数,该函数主要就是初始化PM3和PM2两个IO口,其定义如下:

  1. /**
  2. * @brief   初始化SCCB
  3. * @param   无
  4. * @retval  无
  5. */
  6. void sccb_init(void)
  7. {
  8.     GPIO_InitTypeDef gpio_init_struct = {0};
  9.    
  10.     /* 时钟使能 */
  11.     SCCB_SCL_GPIO_CLK_ENABLE();
  12.     SCCB_SDA_GPIO_CLK_ENABLE();
  13.     HAL_PWREx_EnableUSBVoltageDetector();   /* 使用GPIOM需要开启该电源 */
  14.    
  15.     /* 配置SCL引脚 */
  16.     gpio_init_struct.Pin = SCCB_SCL_GPIO_PIN;
  17.     gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
  18.     gpio_init_struct.Pull = GPIO_PULLUP;
  19.     gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  20.     HAL_GPIO_Init(SCCB_SCL_GPIO_PORT, &gpio_init_struct);
  21.    
  22.     /* 配置SDA引脚 */
  23.     gpio_init_struct.Pin = SCCB_SDA_GPIO_PIN;
  24.     gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;
  25.     gpio_init_struct.Pull = GPIO_PULLUP;
  26.     gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  27.     HAL_GPIO_Init(SCCB_SDA_GPIO_PORT, &gpio_init_struct);
  28.    
  29.     sccb_stop();
  30. }
复制代码
PM3和PM2都设置为带上拉的开漏输出,这样设置的好处是:SDA引脚不用再设置IO口方向了,因为开漏输出模式下,STM32的IO口可以读取外部信号的高低电平,这个在前面介绍GPIO外设的时候已经讲解过。初始化IO口后,调用sccb_stop函数停止总线上的所有设备,防止误操作。
sccb.c文件的其他函数,这里就不再介绍了,主要都是IO口模拟SCCB时序的相关函数,详细请看源码。
ov5640.c、ov5640.h、ov5640af.h和ov5640cfg.h这四个文件主要就是用于OV5640摄像头的初始化,当然也要使用到SCCB的驱动代码。
ov5640cfg.h文件存放的是OV5640初始化数组,总共有3个数组。我们大概了解下数组结构,每个数组条目的第一个字节为寄存器号(也就是寄存器地址),第二个字节为要设置的值,比如{0x4300, 0x30},就表示在0x4300地址,写入0X30这个值。
三个数组中,ov5640_init_reg_tbl数组,用于初始化OV5640,该数组必须最先进行配置;ov5640_rgb565_reg_tbl数组,用于设置OV5640的输出格式为RGB565,分辨率为1280*800,帧率为15帧,在RGB模式下使用;OV5640_jpeg_reg_tbl数组用于设置OV5640的输出格式为JPEG,分辨率为2592*1944,帧率为7.5帧,在JPEG模式下使用。由于数组的篇幅过长,就不列出来了,请对照源码理解。
ov5640af.h文件存放的是OV5640自动对焦配置数组,前面介绍过。
下面开始介绍ov5640.h文件,主要是OV5640的PWDN和RESET引脚定义和控制函数,以及OV5640的ID、访问地址和相关寄存器定义,其定义如下:

  1. /* 引脚定义 */
  2. #define OV_RESET_GPIO_PORT          GPIOE
  3. #define OV_RESET_GPIO_PIN           GPIO_PIN_2
  4. #define OV_RESET_GPIO_CLK_ENABLE()  do { __HAL_RCC_GPIOE_CLK_ENABLE(); } while(0)
  5. #define OV_PWDN_GPIO_PORT           GPIOF
  6. #define OV_PWDN_GPIO_PIN            GPIO_PIN_5
  7. #define OV_PWDN_GPIO_CLK_ENABLE()   do { __HAL_RCC_GPIOF_CLK_ENABLE(); } while(0)

  8. /* IO操作 */
  9. #define OV5640_PWDN(x)              do { (x) ?                             \
  10.    HAL_GPIO_WritePin(OV_PWDN_GPIO_PORT, OV_PWDN_GPIO_PIN, GPIO_PIN_SET):   \
  11.    HAL_GPIO_WritePin(OV_PWDN_GPIO_PORT, OV_PWDN_GPIO_PIN, GPIO_PIN_RESET); \
  12.                                     } while (0)
  13. #define OV5640_RST(x)               do { (x) ?                             \
  14.    HAL_GPIO_WritePin(OV_RESET_GPIO_PORT, OV_RESET_GPIO_PIN, GPIO_PIN_SET): \
  15.    HAL_GPIO_WritePin(OV_RESET_GPIO_PORT, OV_RESET_GPIO_PIN, GPIO_PIN_RESET);\
  16.                                     } while (0)

  17. /* OV5640 ID和SCCB访问地址定义 */
  18. #define OV5640_ID                   0x5640
  19. #define OV5640_ADDR                 0x3C
复制代码
下面开始介绍ov5640.c文件,首先是OV5640初始化函数,其定义如下:
  1. /**
  2. * @brief   初始化OV5640
  3. * @param   无
  4. * @retval  初始化结果
  5. * @arg     0: 成功
  6. * @arg     1: 失败
  7. */
  8. uint8_t ov5640_init(void)
  9. {
  10.     GPIO_InitTypeDef gpio_init_struct = {0};
  11.     uint16_t id;
  12.     uint16_t i;
  13.    
  14.     /* 使能时钟 */
  15.     OV_RESET_GPIO_CLK_ENABLE();
  16.     OV_PWDN_GPIO_CLK_ENABLE();
  17.     /* 初始化RESET引脚 */
  18.     gpio_init_struct.Pin = OV_RESET_GPIO_PIN;
  19.     gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
  20.     gpio_init_struct.Pull = GPIO_PULLUP;
  21.     gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  22.     HAL_GPIO_Init(OV_RESET_GPIO_PORT, &gpio_init_struct);
  23.    
  24.     gpio_init_struct.Pin = OV_PWDN_GPIO_PIN;
  25.     HAL_GPIO_Init(OV_PWDN_GPIO_PORT, &gpio_init_struct);
  26.    
  27.     /* 硬件复位 */
  28.     OV5640_RST(0);
  29.     delay_ms(20);
  30.     OV5640_PWDN(0);
  31.     delay_ms(5);
  32.     OV5640_RST(1);
  33.     delay_ms(20);
  34.    
  35.     /* 初始化SCCB */
  36.     sccb_init();
  37.    
  38.     /* 校验ID */
  39.     id = ov5640_read_reg(0x300A) << 8;
  40.     id |= ov5640_read_reg(0x300B);
  41.     if (id != OV5640_ID)
  42.     {
  43.         return 1;
  44.     }
  45.    
  46.     /* 软件复位 */
  47.     ov5640_write_reg(0x3103, 0x11);
  48.     ov5640_write_reg(0x3008, 0x82);
  49.     delay_ms(10);
  50.    
  51.     /* 初始化OV5640寄存器 */
  52. for (i=0; i<(sizeof(ov5640_init_reg_tbl)/sizeof(ov5640_init_reg_tbl[0]));
  53. i++)
  54.     {
  55.         ov5640_write_reg(ov5640_init_reg_tbl[i][0], ov5640_init_reg_tbl[i][1]);
  56.     }
  57.    
  58.     /* 检查闪光灯 */
  59.     ov5640_flash_ctrl(1);
  60.     delay_ms(50);
  61.     ov5640_flash_ctrl(0);
  62.    
  63.     return 0;
  64. }
复制代码
该函数除了初始化OV5640相关的IO口,最主要的是完成OV5640的寄存器序列初始化。OV5640的寄存器特多(百几十个),配置特麻烦,幸好厂家有提供参考配置序列,就存放在上面介绍的ov5640_ init_reg_tbl这个数组里面。
另外,在ov5640.c里面,还有几个函数比较重要,这里不贴代码了,只介绍功能:

ov5640_outsize_set函数,用于设置图像输出大小;
ov5640_image_window_set函数,用于设置图像开窗大小(ISP大小);
ov5640_focus_init函数,用于初始化自动对焦功能;
ov5640_focus_single函数,用于实现一次自动对焦;
ov5640_focus_constant函数,用于开启持续自动对焦功能;
其中,ov5640_outsize_set和ov5640_image_window_set函数就是前面在50.1.1节所介绍的3个窗口的设置,他们共同决定了图像的输出。
其他的函数就不列出来了,请大家查看源码。

3. TIMER驱动代码
TIMER驱动代码我们主要就是用到定时器6,在定时器6超时中断回调函数中打印帧率,超时中断回调函数定义如下:

  1. /**
  2. * @brief       定时器超时中断回调函数
  3. * @param       htim:定时器句柄指针
  4. * @retval      无
  5. */
  6. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  7. {
  8.     if (htim ->Instance == BTIM_TIMX_INT)
  9.     {
  10.         printf("Frame:%d\r\n", g_ov_frame);
  11.         g_ov_frame = 0;
  12.     }
  13. }
复制代码
g_ov_frame变量在main.c中被定义,用于存放帧率。TIMER的其他驱动代码在介绍定时器的时候已经介绍过,请看源码。

4. USART2驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。USART2驱动源码包括两个文件:usart2.c和usart2.h。
usart2.h头文件对usart2的IO口做了宏定义,具体如下:

  1. /* USART2相关定义 */
  2. #define USART2_UX                       USART2
  3. #define USART2_UX_CLK_ENABLE()         
  4.                do { __HAL_RCC_USART2_CLK_ENABLE(); } while (0)
  5. #define USART2_UX_TX_GPIO_PORT          GPIOD
  6. #define USART2_UX_TX_GPIO_PIN           GPIO_PIN_5
  7. #define USART2_UX_TX_GPIO_AF            GPIO_AF7_USART2
  8. #define USART2_UX_TX_GPIO_CLK_ENABLE()  
  9.                do { __HAL_RCC_GPIOD_CLK_ENABLE(); } while (0)
复制代码
PD5复用为串口2的发送引脚。
下面介绍usart2.c文件的串口2初始化函数,其定义如下:

  1. /**
  2. * @brief   初始化USART2
  3. * @param   baudrate: 通信波特率
  4. * @retval  无
  5. */
  6. void usart2_init(uint32_t baudrate)
  7. {
  8.     GPIO_InitTypeDef gpio_init_struct = {0};
  9.    
  10.     /* 使能时钟 */
  11.     USART2_UX_CLK_ENABLE();
  12.     USART2_UX_TX_GPIO_CLK_ENABLE();
  13.    
  14.     /* 初始化TX引脚 */
  15.     gpio_init_struct.Pin = USART2_UX_TX_GPIO_PIN;
  16.     gpio_init_struct.Mode = GPIO_MODE_AF_PP;
  17.     gpio_init_struct.Pull = GPIO_PULLUP;
  18.     gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  19.     gpio_init_struct.Alternate = USART2_UX_TX_GPIO_AF;
  20.     HAL_GPIO_Init(USART2_UX_TX_GPIO_PORT, &gpio_init_struct);

  21.     /* 初始化USART2 */
  22.     g_usart2_handle.Instance = USART2_UX;
  23.     g_usart2_handle.Init.BaudRate = baudrate;
  24.     g_usart2_handle.Init.WordLength = UART_WORDLENGTH_8B;
  25.     g_usart2_handle.Init.StopBits = UART_STOPBITS_1;
  26.     g_usart2_handle.Init.Parity = UART_PARITY_NONE;
  27.     g_usart2_handle.Init.Mode = UART_MODE_TX;
  28.     g_usart2_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  29.     g_usart2_handle.Init.OverSampling = UART_OVERSAMPLING_16;
  30.     g_usart2_handle.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  31.     g_usart2_handle.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  32.     HAL_UART_Init(&g_usart2_handle);
  33. }
复制代码
这里需要注意的是,串口2的时钟源频率在sys_stm32_clock_init函数中已经设置了。其他的代码基本usart_init函数是一样的。

5. main.c代码
main.c前面定义了一些变量和数组,具体如下:

  1. static uint8_t g_ov_mode;   /* OV5640输出数据格式
  2.                              * 0: RGB565格式
  3.                              * 1: JPEG格式
  4.                              */
  5. uint8_t g_ov_frame = 0;     /* 帧数 */
  6. uint16_t g_curline = 0;     /* 摄像头输出数据,当前行编号 */
  7. uint16_t g_yoffset = 0;     /* y方向的偏移量 */

  8. /**
  9. * 0,数据没有采集完;
  10. * 1,数据采集完了,但是还没处理;
  11. * 2,数据已经处理完成了,可以开始下一帧接收
  12. */
  13. volatile uint8_t g_jpeg_data_ok = 0;        /* JPEG数据采集完成标志 */
  14. volatile uint32_t g_jpeg_data_len = 0;      /* buf中的JPEG有效数据长度 */

  15. /* OV5640特效名称定义 */
  16. static const char *effects_tbl[] = {"Normal", "Cool", "Warm", "B&W", "Yellowish ", "Inverse", "Greenish"};

  17. /* JPEG图片尺寸名称定义 */
  18. static const char *jpeg_size_tbl[] = {"QQVGA", "QVGA", "VGA", "SVGA", "XGA", "WXGA", "WXGA+", "SXGA", "UXGA", "1080P", "QXGA", "500W"};

  19. /* JPEG图片尺寸参数定义 */
  20. static const uint16_t jpeg_size_param_tbl[][2] =
  21. {
  22.     {160, 120},     /* QQVGA */
  23.     {320, 240},     /* QVGA */
  24.     {640, 480},     /* VGA */
  25.     {800, 600},     /* SVGA */
  26.     {1024, 768},    /* XGA */
  27.     {1280, 800},    /* WXGA */
  28.     {1440, 900},    /* WXGA+ */
  29.     {1280, 1024},   /* SXGA */
  30.     {1600, 1200},   /* UXGA */
  31.     {1920, 1080},   /* 1080P */
  32.     {2048, 1536},   /* QXGA */
  33.     {2592, 1944}    /* 500W */
  34. };
复制代码
在main.c里面,总共有4个函数,我们接下来分别介绍。首先是处理JPEG数据函数,其定义如下:
  1. /**
  2. * @brief       处理JPEG数据
  3. * @ntoe        在DCMIPP_IRQHandler中断服务函数里面被调用
  4. *              当采集完一帧JPEG数据后,调用此函数,切换JPEG BUF.开始下一帧采集.
  5. *
  6. * @param       无
  7. * @retval      无
  8. */
  9. void jpeg_data_process(void)
  10. {
  11.     if (g_ov_mode & 0X01)              /* 只有在JPEG格式下,才需要做处理. */
  12.     {
  13.         if (g_jpeg_data_ok == 0)       /* jpeg数据还未采集完? */
  14.         {
  15.             dcmipp_stop();             /* 关闭数据传输 */
  16.             HAL_DCMIPP_PIPE_GetDataCounter(&g_dcmipp_handle, DCMIPP_PIPE0,
  17. (uint32_t*)&g_jpeg_data_len);
  18.             g_jpeg_data_ok = 1;        /* 标记JPEG数据采集完成,等待其他函数处理 */                                                               
  19.         }
  20.         /* 采集的JPEG数据已被处理 */
  21.         if (g_jpeg_data_ok == 2)
  22.         {
  23.             /* 重新开始采集JPEG数据 */
  24.             g_jpeg_data_ok = 0;
  25.             g_jpeg_data_len = 0;
  26.             dcmipp_start();
  27.         }
  28.     }
  29.     else
  30.     {
  31. //        if (lcdltdc.pwidth == 0)
  32. //        {
  33.             lcd_set_cursor(0, 0);
  34.             lcd_write_ram_prepare();    /* 开始写入GRAM */
  35. //        }
  36.     }
  37. }
复制代码
该函数用于处理JPEG数据的接收,在HAL_DCMIPP_PIPE_FrameEventCallback函数(在dcmipp.c里面)里面被调用。
接下来介绍的是JPEG测试函数,其定义如下:

  1. /**
  2. * @brief   JPEG测试
  3. * @param   无
  4. * @retval  无
  5. */
  6. void jpeg_test(void)
  7. {
  8.     uint8_t key;
  9.     uint8_t *p, headok;
  10.     uint32_t i, jpgstart, jpglen;
  11.     uint8_t effect = 0, contrast = 2;
  12.     uint8_t msgbuf[15];                          /* 消息缓存区 */
  13.     uint8_t size = 1;                            /* 默认是QVGA 320*240尺寸 */
  14.    
  15.     lcd_clear(WHITE);
  16.     lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
  17.     lcd_show_string(30, 70, 200, 16, 16, "OV5640 JPEG Mode", RED);
  18.     lcd_show_string(30, 100, 200, 16, 16, "KEY0:Contrast", RED);   /* 对比度 */
  19.     lcd_show_string(30, 120, 200, 16, 16, "KEY1:AUTO Focus", RED); /* 自动对焦 */
  20.     lcd_show_string(30, 140, 200, 16, 16, "KEY2:Effect", RED);     /* 特效 */
  21.     lcd_show_string(30, 160, 200, 16, 16, "KEY_UP:Size", RED);  /* 分辨率设置 */
  22.     sprintf((char *)msgbuf, "JPEG Size:%s", jpeg_size_tbl[size]);
  23. lcd_show_string(30, 180, 200, 16, 16, (char *)msgbuf, RED);         
  24. /* 显示当前JPEG分辨率 */
  25.    
  26.     ov5640_rgb565_mode();                        /* RGB565模式 */
  27.     ov5640_focus_init();

  28.     ov5640_jpeg_mode();                          /* JPEG模式 */
  29.     ov5640_light_mode(0);                        /* 自动模式 */
  30.     ov5640_color_saturation(3);                  /* 色彩饱和度0 */
  31.     ov5640_brightness(4);                        /* 亮度0 */
  32.     ov5640_contrast(3);                          /* 对比度0 */
  33.     ov5640_sharpness(33);                        /* 自动锐度 */
  34.     ov5640_focus_constant();                     /* 启动持续对焦 */
  35.     dcmipp_init();                               /* DCMIPP配置 */

  36. ov5640_outsize_set(4, 0, jpeg_size_param_tbl[size][0],
  37. jpeg_size_param_tbl[size][1]);               /* 设置输出尺寸 */
  38.     dcmipp_start();                              /* 启动传输 */

  39.     while (1)
  40.     {
  41.         if (g_jpeg_data_ok == 1)                /* 已经采集完一帧图像了 */
  42.         {  
  43.             p = (uint8_t *)camera_date_buf;
  44.             printf("g_jpeg_data_len:%d\r\n", g_jpeg_data_len * 4);
  45.             /* 打印jpeg图片大小 */
  46.             lcd_show_string(30, 210, 210, 16, 16, "Sending JPEG data...", RED);
  47.             /* 提示正在传输数据 */

  48.             jpglen = 0;        /* 设置jpg文件大小为0 */
  49.             headok = 0;        /* 清除jpg头标记 */
  50.             for (i = 0; i < g_jpeg_data_len * 4; i++)
  51.             /* 查找0XFF,0XD8和0XFF,0XD9,获取jpg文件大小 */
  52.             {
  53.                 if ((p[i] == 0XFF) && (p[i + 1] == 0XD8))
  54.                 /* 找到FF D8  */
  55.                 {
  56.                     jpgstart = i;
  57.                     headok = 1;       /* 标记找到jpg头(FF D8) */
  58.                 }
  59.                 if ((p[i] == 0XFF) && (p[i + 1] == 0XD9) && headok)
  60.                 /* 找到头以后,再找FF D9 */
  61.                 {
  62.                     jpglen = i - jpgstart + 2;
  63.                     break;
  64.                 }
  65.             }
  66.             if (jpglen)                       /* 正常的jpeg数据 */
  67.             {
  68.                 p += jpgstart;                /* 偏移到0XFF,0XD8处 */
  69.                 for (i = 0; i < jpglen; i++)  /* 发送整个jpg文件 */
  70.                 {
  71.                     USART2->TDR = p[i];  
  72.                     while ((USART2->ISR & 0X40) == 0);
  73.                     /* 循环发送,直到发送完毕 */
  74.                     
  75.                     key = key_scan(0);
  76.                     if (key)break;
  77.                 }           
  78.             }
  79.             if (key)                          /* 有按键按下,需要处理 */
  80.             {  
  81.                 lcd_show_string(30, 210, 210, 16, 16, "Quit Sending data   ",
  82. RED);                        /* 提示退出数据传输 */
  83.                 switch (key)
  84.                 {
  85.                     case KEY0_PRES:          /* 对比度设置 */
  86.                         contrast++;
  87.                         if (contrast > 6)contrast = 0;
  88.                         ov5640_contrast(contrast);
  89.                         sprintf((char *)msgbuf, "Contrast:%d", (signed
  90.                  char)contrast - 3);
  91.                         break;
  92.                     
  93.                     case KEY1_PRES:                     /* 执行一次自动对焦 */
  94.                         ov5640_focus_single();
  95.                         break;
  96.                     
  97.                     case KEY2_PRES:                     /* 特效设置 */
  98.                         effect++;
  99.                         if (effect > 6)effect = 0;
  100.                         ov5640_special_effects(effect); /* 设置特效 */
  101.                         sprintf((char *)msgbuf, "%s", effects_tbl[effect]);
  102.                         break;
  103.                     
  104.                     case WKUP_PRES:                     /* JPEG输出尺寸设置 */
  105.                         size++;  
  106.                         if (size > 11)size = 0;   
  107.                         ov5640_outsize_set(16, 4, jpeg_size_param_tbl[size][0],
  108.                      jpeg_size_param_tbl[size][1]);  /* 设置输出尺寸 */
  109.                         sprintf((char *)msgbuf, "JPEG Size:%s",
  110. jpeg_size_tbl[size]);
  111.                         break;
  112.                 }

  113.                 key = 0;                            /* 避免重复进入 */
  114.                 lcd_fill(30, 180, 239, 190 + 16, WHITE);
  115.                 lcd_show_string(30, 180, 210, 16, 16, (char *)msgbuf, RED);
  116.                /* 显示提示内容 */
  117.                 delay_ms(800);
  118.             }
  119.             else
  120.             {
  121.                 lcd_show_string(30, 210, 210, 16, 16, "Send data complete!!",
  122. RED);          /* 提示传输结束设置 */
  123.             }
  124.             g_jpeg_data_ok = 2; /* 标记jpeg数据处理完了,可以让DMA去采集下一帧了. */
  125.             dcmipp_start();   
  126.         }
  127.     }
  128. }
复制代码
该函数将OV5640设置为JPEG模式,并开启持续自动对焦、实现OV5640的JPEG数据接收,通过串口2发送给上位机软件。
接下来介绍的是RGB565测试函数,其定义如下:

  1. /**
  2. * @brief   RGB565测试
  3. * @param   无
  4. * @retval  无
  5. */
  6. void rgb565_test(void)
  7. {
  8.     uint8_t key;
  9.     float fac = 0;
  10.     uint8_t effect = 0, contrast = 2;
  11.     uint8_t scale = 1;
  12.     uint8_t msgbuf[15];
  13.     uint16_t outputheight = 0;
  14.     uint32_t memaddr;
  15.     uint8_t i;
  16.    
  17.     lcd_clear(WHITE);
  18.     lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
  19.     lcd_show_string(30, 70, 200, 16, 16, "OV5640 RGB565 Mode", RED);
  20.     lcd_show_string(30, 90, 200, 16, 16, "KEY0: Auto Focus", RED);
  21.     lcd_show_string(30, 110, 200, 16, 16, "KEY1: Effects", RED);
  22.     lcd_show_string(30, 130, 200, 16, 16, "KEY_UP: Fullsize/Scale", RED);
  23.    
  24.     /* 配置OV5640 */
  25.     ov5640_rgb565_mode();
  26.     ov5640_focus_init();
  27.     ov5640_light_mode(0);
  28.     ov5640_color_saturation(3);
  29.     ov5640_brightness(4);
  30.     ov5640_contrast(3);
  31.     ov5640_sharpness(33);
  32.     ov5640_focus_constant();   
  33.     /* 配置DCMIPP */
  34.     dcmipp_init();
  35.    
  36.     if (lcddev.height == 1024)
  37.     {
  38.         g_yoffset = (lcddev.height - 800) / 2;
  39.         outputheight = 800;
  40.         ov5640_write_reg(0x3035, 0X51);         /* 降低输出帧率,否则可能抖动 */
  41.     }
  42.     else if (lcddev.height == 1280)
  43.     {
  44.         g_yoffset = (lcddev.height - 600) / 2;
  45.         outputheight = 600;
  46.         ov5640_write_reg(0x3035, 0X51);         /* 降低输出帧率,否则可能抖动 */
  47.     }
  48.     else
  49.     {
  50.         g_yoffset = 0;
  51.         outputheight = lcddev.height;
  52.     }

  53.     g_curline = g_yoffset;                                /* 行数复位 */
  54.     ov5640_outsize_set(4, 0, lcddev.width, outputheight); /* 满屏缩放显示 */
  55.     dcmipp_start();                                       /* 启动传输 */
  56.     lcd_clear(0xFFFF);
  57.    
  58.     while (1)
  59.     {
  60.         lcd_color_fill(0, 0, lcddev.width - 1, lcddev.height - 1,
  61. (uint16_t*)camera_date_buf);
  62.         key = key_scan(0);
  63.         if (key)
  64.         {
  65.             if (key != KEY0_PRES)
  66.             {
  67.                 dcmipp_stop();    /* 非KEY1按下,停止显示 */
  68.             }
  69.             
  70.             switch (key)
  71.             {
  72.                 case KEY0_PRES:   /* 执行一次自动对焦 */
  73.                     ov5640_focus_single();
  74.                     break;

  75.                 case KEY1_PRES:   /* 特效设置 */
  76.                     effect++;

  77.                     if (effect > 6)effect = 0;

  78.                     ov5640_special_effects(effect);         /* 设置特效 */
  79.                     sprintf((char *)msgbuf, "%s", effects_tbl[effect]);
  80.                     break;

  81.                 case WKUP_PRES: /* 1:1尺寸(显示真实尺寸)/缩放 */
  82.                     scale = !scale;

  83.                     if (scale == 0)
  84.                     {
  85.                         fac = (float)800 / outputheight;    /* 得到比例因子 */
  86.                         ov5640_outsize_set((1280 - fac * lcddev.width) / 2,
  87. (800 - fac * outputheight) / 2, lcddev.width,
  88. outputheight);
  89.                         sprintf((char *)msgbuf, "Full Size 1:1");
  90.                     }
  91.                     else
  92.                     {
  93.                         ov5640_outsize_set(4, 0, lcddev.width, outputheight);
  94.                         sprintf((char *)msgbuf, "Scale");
  95.                     }

  96.                     break;
  97.             }

  98.             if (key != KEY0_PRES)                        /* 非KEY0按下 */
  99.             {
  100.                 lcd_show_string(30, 50, 210, 16, 16, (char*)msgbuf, RED);  
  101.        /* 显示提示内容 */
  102.                 delay_ms(800);
  103.                 lcd_set_cursor(0, 0);
  104.                 lcd_write_ram_prepare();
  105.                 dcmipp_start();                          /* 重新开始传输 */
  106.             }
  107.         }
  108.         delay_ms(10);
  109.     }
  110. }
复制代码
该函数将OV5640设置为RGB565模式,并将接收到的数据,传送给LCD。
接下来介绍的是main函数,其定义如下:

  1. int main(void)
  2. {
  3.     uint8_t t = 0;
  4.     uint8_t key;
  5.    
  6.     sys_mpu_config();                   /* 配置MPU */
  7.     sys_cache_enable();                 /* 使能Cache */
  8.     HAL_Init();                         /* 初始化HAL库 */
  9.     sys_stm32_clock_init(300, 6, 2);    /* 配置时钟,600MHz */
  10.     delay_init(600);                    /* 初始化延时 */
  11.     usart_init(115200);                 /* 初始化串口 */
  12.     usart2_init(921600);                /* 初始化USART2 */
  13.     led_init();                         /* 初始化LED */
  14.     key_init();                         /* 按键初始化 */
  15.     hyperram_init();                    /* 初始化HyperRAM */
  16.     lcd_init();                         /* 初始化LCD */
  17. btim_timx_int_init(10000 - 1, 30000 - 1);   
  18. /* 初始化基本定时器中断,溢出频率为1Hz */
  19.    
  20.     lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
  21.     lcd_show_string(30, 70, 200, 16, 16, "OV5640 TEST", RED);
  22.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
  23.    
  24.     while (ov5640_init() != 0)                  /* 初始化OV5640 */
  25.     {
  26.         lcd_show_string(30, 110, 200, 16, 16, "OV5640 Error!", RED);
  27.         delay_ms(500);
  28.         lcd_show_string(30, 110, 200, 16, 16, "Please Check! ", RED);
  29.         delay_ms(500);
  30.         LED0_TOGGLE();
  31.     }
  32.     lcd_show_string(30, 110, 200, 16, 16, "OV5640 OK!   ", RED);
  33.    
  34.     /* 选择测试模式 */
  35.     lcd_show_string(30, 130, 200, 16, 16, "KEY0: RGB565 Mode", BLUE);
  36.     lcd_show_string(30, 150, 200, 16, 16, "KEY1: JPEG Mode", BLUE);
  37.    
  38.     while (1)
  39.     {
  40.         key = key_scan(0);
  41.         if (key == KEY0_PRES)
  42.         {
  43.             g_ov_mode = 0;
  44.             break;
  45.         }
  46.         else if (key == KEY1_PRES)
  47.         {
  48.             g_ov_mode = 1;
  49.             break;
  50.         }
  51.         
  52.         if (++t == 20)
  53.         {
  54.             t = 0;
  55.             LED0_TOGGLE();
  56.         }
  57.         
  58.         delay_ms(10);
  59.     }
  60.    
  61.     /* 进入测试 */
  62.     if (g_ov_mode == 0)
  63.     {
  64.         
  65.         rgb565_test();
  66.     }
  67.     else
  68.     {
  69.         jpeg_test();
  70.     }
  71. }
复制代码
该函数完成对各相关硬件的初始化,然后检测OV5640,最后通过按键选择来调用jpeg_test还是rgb565_test,实现JPEG测试和RGB565测试。

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

第五十章 摄像头实验40408.png
图50.4.1 摄像头实验程序运行效果图

上图是摄像头已经正确的插上开发板上的运行结果,如果没有插上摄像头,会有错误的报错信息。在OV5640初始化成功后,屏幕提示选择测试模式,此时我们可以按KEY0,进入RGB565模式测试,也可以按KEY1,进入JPEG模式测试。
当按KEY0后,选择RGB565模式,LCD满屏显示压缩放后的图像(有变形),如图50.4.2所示:


第五十章 摄像头实验40597.png
图50.4.2 RGB565模式测试图片

此时,可以通过KEY0按键,执行一次自动对焦;KEY1按键,设置特效; KEY_UP按键,显示1:1尺寸或者缩放。
当按KEY1后,选择JPEG模式,此时屏幕显示JPEG数据传输进程,如图50.4.3所示:


第五十章 摄像头实验40724.png
图50.4.3 JPEG模式测试图

默认条件下,图像分辨率是QVGA (320*240)的,硬件上:我们需要用一根杜邦线连接开发板的PD5排针到RXD排针端。
打开上位机软件:ATK-XCAM.exe(路径:光盘→6,软件资料→软件→串口网络摄像头软件→ATK-XCAM .exe),选择正确的串口,然后波特率设置为921600,打开即可收到下位机传过来的图片了,如图50.4.4所示:


第五十章 摄像头实验40920.png
图50.4.4 ATK-XCAM.exe软件接收并显示JPEG图片

我们可以通过KEY_UP设置输出图像的尺寸(QQVGA~ WXGA)。KEY0按键,设置对比度;KEY1按键,执行一次自动对焦;KEY2按键,设置特效。
回复

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

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

GMT+8, 2026-5-27 00:09

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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