53. DCMI—OV2640摄像头¶
本章参考资料:《STM32F4xx参考手册》、《STM32F4xx规格书》、库帮助文档《stm32f4xx_dsp_stdperiph_lib_um.chm》。
关于开发板配套的OV2640摄像头参数可查阅《ov2640datasheet》配套资料获知。
STM32F4芯片具有浮点运算单元,适合对图像信息使用DSP进行基本的图像处理,其处理速度比传统的8、16位机快得多, 而且它还具有与摄像头通讯的专用DCMI接口,所以使用它驱动摄像头采集图像信息并进行基本的加工处理非常适合。 本章讲解如何使用STM32驱动OV2640型号的摄像头。
53.1. 摄像头简介¶
在各类信息中,图像含有最丰富的信息,作为机器视觉领域的核心部件,摄像头被广泛地应用在安防、探险以及车牌检测等场合。 摄像头按输出信号的类型来看可以分为数字摄像头和模拟摄像头,按照摄像头图像传感器材料构成来看可以分为CCD和CMOS。 现在智能手机的摄像头绝大部分都是CMOS类型的数字摄像头。
53.1.1. 数字摄像头跟模拟摄像头区别¶
输出信号类型
数字摄像头输出信号为数字信号,模拟摄像头输出信号为标准的模拟信号。
接口类型
数字摄像头有USB接口(比如常见的PC端免驱摄像头)、IEE1394火线接口(由苹果公司领导的开发联盟开发的一种高速度传送接口, 数据传输率高达800Mbps)、千兆网接口(网络摄像头)。模拟摄像头多采用AV视频端子(信号线+地线)或S-VIDEO(即莲花头–SUPER VIDEO, 是一种五芯的接口,由两路视频亮度信号、两路视频色度信号和一路公共屏蔽地线共五条芯线组成)。
分辨率
模拟摄像头的感光器件,其像素指标一般维持在752(H)*582(V)左右的水平,像素数一般情况下维持在41万左右。 现在的数字摄像头分辨率一般从数十万到数千万。但这并不能说明数字摄像头的成像分辨率就比模拟摄像头的高, 原因在于模拟摄像头输出的是模拟视频信号,一般直接输入至电视或监视器,其感光器件的分辨率与电视信号的扫描数呈一定的换算关系, 图像的显示介质已经确定,因此模拟摄像头的感光器件分辨率不是不能做高,而是依据于实际情况没必要做这么高。
53.1.2. CCD与CMOS的区别¶
摄像头的图像传感器CCD与CMOS传感器主要区别如下:
成像材料
CCD与CMOS的名称跟它们成像使用的材料有关,CCD是“电荷耦合器件”(Charge Coupled Device)的简称, 而CMOS是“互补金属氧化物半导体”(Complementary Metal Oxide Semiconductor)的简称。
功耗
由于CCD的像素由MOS电容构成,读取电荷信号时需使用电压相当大(至少12V)的二相或三相或四相时序脉冲信号,才能有效地传输电荷。 因此CCD的取像系统除了要有多个电源外,其外设电路也会消耗相当大的功率。有的CCD取像系统需消耗2~5W的功率。 而CMOS光电传感器件只需使用一个单电源5V或3V,耗电量非常小,仅为CCD的1/8~1/10,有的CMOS取像系统只消耗20~50mW的功率。
成像质量
CCD传感器件制作技术起步早,技术成熟,采用PN结或二氧化硅(sio2)隔离层隔离噪声,所以噪声低,成像质量好。与CCD相比, CMOS的主要缺点是噪声高及灵敏度低,不过现在随着CMOS电路消噪技术的不断发展,为生产高密度优质的CMOS传感器件提供了良好的条件, 现在的CMOS传感器已经占领了大部分的市场,主流的单反相机、智能手机都已普遍采用CMOS传感器。
53.2. OV2640摄像头¶
本章主要讲解实验板配套的摄像头,它的实物见图 实验板配套的OV2640摄像头 ,该摄像头主要由镜头、图像传感器、板载电路及下方的信号引脚组成。
镜头部件包含一个镜头座和一个可旋转调节距离的凸透镜,通过旋转可以调节焦距,正常使用时,镜头座覆盖在电路板上遮光, 光线只能经过镜头传输到正中央的图像传感器,它采集光线信号,然后把采集得的数据通过下方的信号引脚输出数据到外部器件。
53.2.1. OV2640传感器简介¶
图像传感器是摄像头的核心部件,上述摄像头中的图像传感器是一款型号为OV2640的CMOS类型数字图像传感器。该传感器支持输出最大为200万像素的图像 (1600x1200分辨率), 支持使用VGA时序输出图像数据,输出图像的数据格式支持YUV(422/420)、YCbCr422、RGB565以及JPEG格式,若直接输出JPEG格式的图像时可大大减少数据量, 方便网络传输。它还可以对采集得的图像进行补偿,支持伽玛曲线、白平衡、饱和度、色度等基础处理。根据不同的分辨率配置, 传感器输出图像数据的帧率从15-60帧可调,工作时功率在125mW-140mW之间。
53.2.2. OV2640引脚及功能框图¶
OV2640传感器采用BGA封装,它的前端是采光窗口,引脚都在背面引出,引脚的分布见图 OV2640传感器引脚分布图 。
图中的非彩色部分是电源相关的引脚,彩色部分是主要的信号引脚,介绍如下表 OV2640管脚 。
下面我们配合图 OV2640功能框图 中的OV2640功能框图讲解这些信号引脚。
控制寄存器
标号处的是OV2640的控制寄存器,它根据这些寄存器配置的参数来运行,而这些参数是由外部控制器通过SIO_C和SIO_D引脚写入的, SIO_C与SIO_D使用的通讯协议SCCB跟I2C十分类似,在STM32中我们完全可以直接用I2C硬件外设来控制。
通信、控制信号及时钟
标号处包含了OV2640的通信、控制信号及外部时钟,其中PCLK、HREF及VSYNC分别是像素同步时钟、行同步信号以及帧同步信号, 这与液晶屏控制中的信号是很类似的。RESETB引脚为低电平时,用于复位整个传感器芯片,PWDN用于控制芯片进入低功耗模式。 注意最后的一个XCLK引脚,它跟PCLK是完全不同的,XCLK是用于驱动整个传感器芯片的时钟信号,是外部输入到OV2640的信号; 而PCLK是OV2640输出数据时的同步信号,它是由OV2640输出的信号。XCLK可以外接晶振或由外部控制器提供, 若要类比XCLK之于OV2640就相当于HSE时钟输入引脚与STM32芯片的关系,PCLK引脚可类比STM32的I2C外设的SCL引脚。
感光矩阵
标号处的是感光矩阵,光信号在这里转化成电信号,经过各种处理,这些信号存储成由一个个像素点表示的数字图像。
数据输出信号
标号处包含了DSP处理单元,它会根据控制寄存器的配置做一些基本的图像处理运算。这部分还包含了图像格式转换单元及压缩单元, 转换出的数据最终通过Y0-Y9引脚输出,一般来说我们使用8根据数据线来传输,这时仅使用Y2-Y9引脚, OV2640与外部器件的连接方式见图 8位数据线接法 。
53.2.3. SCCB时序¶
外部控制器对OV2640寄存器的配置参数是通过SCCB总线传输过去的,而SCCB总线跟I2C十分类似,所以在STM32驱动中我们直接使用片上I2C外设与它通讯。 SCCB与标准的I2C协议的区别是它每次传输只能写入或读取一个字节的数据,而I2C协议是支持突发读写的, 即在一次传输中可以写入多个字节的数据(EEPROM中的页写入时序即突发写)。关于SCCB协议的完整内容可查看配套资料里的《SCCB协议》文档,下面我们简单介绍下。
SCCB的起始、停止信号及数据有效性
SCCB的起始信号、停止信号及数据有效性与I2C完全一样,见图 SCCB停止信号 及图 SCCB的数据有效性 。
起始信号:在SIO_C为高电平时,SIO_D出现一个下降沿,则SCCB开始传输。
停止信号:在SIO_C为高电平时,SIO_D出现一个上升沿,则SCCB停止传输。
数据有效性:除了开始和停止状态,在数据传输过程中,当SIO_C为高电平时,必须保证SIO_D上的数据稳定, 也就是说,SIO_D上的电平变换只能发生在SIO_C为低电平的时候,SIO_D的信号在SIO_C为高电平时被采集。
SCCB数据读写过程
在SCCB协议中定义的读写操作与I2C也是一样的,只是换了一种说法。它定义了两种写操作,即三步写操作和两步写操作。 三步写操作可向从设备的一个目的寄存器中写入数据,见图 SCCB的三步写操作 。在三步写操作中, 第一阶段发送从设备的ID地址+W标志(等于I2C的设备地址:7位设备地址+读写方向标志),第二阶段发送从设备目标寄存器的8位地址, 第三阶段发送要写入寄存器的8位数据。图中的“X”数据位可写入1或0,对通讯无影响。
而两步写操作没有第三阶段,即只向从器件传输了设备ID+W标志和目的寄存器的地址,见图 SCCB的两步写操作 。 两步写操作是用来配合后面的读寄存器数据操作的,它与读操作一起使用,实现I2C的复合过程。
两步读操作,它用于读取从设备目的寄存器中的数据,见图 SCCB的两步读操作 。在第一阶段中发送从设备的设备ID+R标志(设备地址+读方向标志)和自由位, 在第二阶段中读取寄存器中的8位数据和写NA位(非应答信号)。由于两步读操作没有确定目的寄存器的地址, 所以在读操作前,必需有一个两步写操作,以提供读操作中的寄存器地址。
可以看到,以上介绍的SCCB特性都与I2C无区别,而I2C比SCCB还多出了突发读写的功能, 所以SCCB可以看作是I2C的子集,我们完全可以使用STM32的I2C外设来与OV2640进行SCCB通讯。
53.2.4. OV2640的寄存器¶
控制OV2640涉及到它很多的寄存器,可直接查询《ov2640datasheet》了解,通过这些寄存器的配置,可以控制它输出图像的分辨率大小、 图像格式及图像方向等。要注意的是OV2640有两组寄存器,这两组寄存器有部分地址重合,通过设置地址为0xFF的RA_DLMT寄存器可以切换寄存器组, 当RA_DLMT寄存器为0时,通过SCCB发送的寄存器地址在DSP相关的寄存器组寻址,见图 DSP相关寄存器说明 ; RA_DLMT寄存器为1时,在Sensor相关的寄存器组寻址,图 Sensor相关寄存器说明 。
官方还提供了一个《OV2640_Camera_app》的文档,它针对不同的配置需求,提供了配置范例,见图 调节帧率的寄存器配置范例 。 其中write_SCCB是一个利用SCCB向寄存器写入数据的函数,第一个参数为要写入的寄存器的地址,第二个参数为要写入的内容。
53.2.5. 像素数据输出时序¶
主控器控制OV2640时采用SCCB协议读写其寄存器,而它输出图像时则使用VGA时序(还可用SVGA、UXGA,这些时序都差不多), 这跟控制液晶屏输入图像时很类似。OV2640输出图像时,一帧帧地输出,在帧内的数据一般从左到右,从上到下, 一个像素一个像素地输出(也可通过寄存器修改方向),见图 摄像头数据输出 。
例如,见图 像素同步时序 和图 帧图像同步时序 ,若我们使用Y2-Y9数据线,图像格式设置为RGB565, 进行数据输出时,Y2-Y9数据线会在1个像素同步时钟PCLK的驱动下发送1字节的数据信号,所以2个PCLK时钟可发送1个RGB565格式的像素数据。 像素数据依次传输,每传输完一行数据时,行同步信号HREF会输出一个电平跳变信号,每传输完一帧图像时,VSYNC会输出一个电平跳变信号。
53.3. STM32的DCMI接口简介¶
STM32F4系列的控制器包含了DCMI数字摄像头接口(Digital cameraInterface), 它支持使用上述类似VGA的时序获取图像数据流,支持原始的按行、帧格式来组织的图像数据,如YUV、RGB, 也支持接收JPEG格式压缩的数据流。接收数据时,主要使用HSYNC及VSYNC信号来同步。
53.3.1. DCMI整体框图¶
STM32的DCMI接口整体框图见图 DCMI接口整体框图 。
外部接口及时序
上图标号处的是DCMI向外部引出的信号线。DCMI提供的外部接口的方向都是输入的,接口的各个信号线说明见表 DCMI的信号线说明 。
其中DCMI_D数据线的数量可选8、10、12或14位,各个同步信号的有效极性都可编程控制。 它使用的通讯时序与OV2640的图像数据输出接口时序一致,见图 DCMI时序图 。
内部信号及PIXCLK的时钟频率
图 DCMI接口整体框图 的标号处表示DCMI与内部的信号线。在STM32的内部,使用HCLK作为时钟源提供给DCMI外设。 从DCMI引出有DCMI_IT信号至中断控制器,并可通过DMA_REQ信号发送DMA请求。
DCMI从外部接收数据时,在HCLK的上升沿时对PIXCLK同步的信号进行采样, 它限制了PIXCLK的最小时钟周期要大于2.5个HCLK时钟周期,即最高频率为HCLK的1/4。
53.3.2. DCMI接口内部结构¶
DCMI接口的内部结构见图 DCMI接口内部结构 。
(1) 同步器
同步器主要用于管理DCMI接收数据的时序,它根据外部的信号提取输入的数据。
(2) FIFO/数据格式化器
为了对数据传输加以管理,STM32在DCMI接口上实现了 4 个字(32bit x4)深度的 FIFO,用以缓冲接收到的数据。
(3) AHB接口
DCMI接口挂载在AHB总线上,在AHB总线中有一个DCMI接口的数据寄存器,当我们读取该寄存器时,它会从FIFO中获取数据, 并且FIFO中的数据指针会自动进行偏移,使得我们每次读取该寄存器都可获得一个新的数据。
(4) 控制/状态寄存器
DCMI的控制寄存器协调图中的各个结构运行,程序中可通过检测状态寄存器来获DCMI的当前运行状态。
(5) DMA接口
由于DCMI采集的数据量很大,我们一般使用DMA来把采集得的数据搬运至内存。
53.3.3. 同步方式¶
DCMI接口支持硬件同步或内嵌码同步方式,硬件同步方式即使用HSYNC和VSYNC作为同步信号的方式,OV2640就是使用这种同步时序。
而内嵌码同步的方式是使用数据信号线传输中的特定编码来表示同步信息,由于需要用0x00和0xFF来表示编码, 所以表示图像的数据中不能包含有这两个值。利用这两个值,它扩展到4个字节,定义出了2种模式的同步码,每种模式包含4个编码, 编码格式为0xFF0000XY,其中XY的值可通过寄存器设置。当DCMI接收到这样的编码时,它不会把这些当成图像数据, 而是按照表 两种模式的内嵌码 中的编码来解释,作为同步信号。
53.3.4. 捕获模式及捕获率¶
DCMI还支持两种数据捕获模式,分别为快照模式和连续采集模式。快照模式时只采集一帧的图像数据,连续采集模式会一直采集多个帧的数据, 并且可以通过配置捕获率来控制采集多少数据,如可配置为采集所有数据或隔1帧采集一次数据或隔3帧采集一次数据。
53.4. DCMI初始化结构体¶
与其它外设一样,STM32的DCMI外设也可以使用库函数来控制,其中最主要的配置项都封装到了DCMI_InitTypeDef结构体, 来这些内容都定义在库文件“stm32f4xx_dcmi.h”及“stm32f4xx_ dcmi.c”中,编程时我们可以结合这两个文件内的注释使用或参考库帮助文档。
DCMI_InitTypeDef初始化结构体的内容见 代码清单:OV2640-1 。
1 2 3 4 5 6 7 8 9 10 11 12 13 | /**
* @brief DCMI 初始化结构体
*/
typedef struct
{
uint16_t DCMI_CaptureMode; /*选择连续模式或拍照模式 */
uint16_t DCMI_SynchroMode; /*选择硬件同步模式还是内嵌码模式 */
uint16_t DCMI_PCKPolarity; /*设置像素时钟的有效边沿*/
uint16_t DCMI_VSPolarity; /*设置VSYNC的有效电平*/
uint16_t DCMI_HSPolarity; /*设置HSYNC的有效边沿*/
uint16_t DCMI_CaptureRate; /*设置图像的采集间隔 */
uint16_t DCMI_ExtendedDataMode; /*设置数据线的宽度 */
} DCMI_InitTypeDef;
|
这些结构体成员说明如下,其中括号内的文字是对应参数在STM32标准库中定义的宏:
(1) DCMI_CaptureMode
本成员设置DCMI的捕获模式, 可以选择为连续摄像(DCMI_CaptureMode_Continuous)或单张拍照DCMI_CaptureMode_SnapShot。
(2) DCMI_SynchroMode
本成员设置DCMI数据的同步模式, 可以选择为硬件同步方式(DCMI_SynchroMode_Hardware)或内嵌码方式(DCMI_SynchroMode_Embedded)。
(3) DCMI_PCKPolarity
本成员用于配置DCMI接口像素时钟的有效边沿,即在该时钟边沿时,DCMI会对数据线上的信号进行采样, 它可以被设置为上升沿有效(DCMI_PCKPolarity_Rising)或下降沿有效(DCMI_PCKPolarity_Falling)。
(4) DCMI_VSPolarity
本成员用于设置VSYNC的有效电平,当VSYNC信号线表示为有效电平时,表示新的一帧数据传输完成, 它可以被设置为高电平有效(DCMI_VSPolarity_High)或低电平有效(DCMI_VSPolarity_Low)。
(5) DCMI_HSPolarity
类似地,本成员用于设置HSYNC的有效电平,当HSYNC信号线表示为有效电平时,表示新的一行数据传输完成, 它可以被设置为高电平有效(DCMI_HSPolarity_High)或低电平有效(DCMI_HSPolarity_Low)。
(6) DCMI_CaptureRate
本成员可以用于设置DCMI捕获数据的频率,可以设置为全采集、半采集或1/4采集(DCMI_CaptureRate_All_Frame/ 1of2_Frame/ 1of4_Frame), 在间隔采集的情况下,STM32的DCMI外设会直接按间隔丢弃数据。
(7) DCMI_ExtendedDataMode
本成员用于设置DCMI的数据线宽度,可配置为8/10/12及14位数据线宽(DCMI_ExtendedDataMode_8b/10b/12b/14b)。
配置完这些结构体成员后,我们调用库函数DCMI_Init即可把这些参数写入到DCMI的控制寄存器中,实现DCMI的初始化。
53.5. DCMI—OV2640摄像头实验¶
本小节讲解如何使用DCMI接口从OV2640摄像头输出的RGB565格式的图像数据,并把这些数据实时显示到液晶屏上。
学习本小节内容时,请打开配套的“DCMI—OV2640摄像头”工程配合阅读。
53.5.1. 硬件设计¶
摄像头原理图
本实验采用的OV2640摄像头实物见图 实验板配套的OV2640摄像头 ,其原理图见图 OV2640摄像头原理图 。
图 28‑18标号处的是OV2640芯片的主电路,在这部分中已对SCCB使用的信号线接了上拉电阻,外部电路可以省略上拉; 标号处的是一个24MHz的有源晶振,它为OV2640提供系统时钟,如果不想使用外部晶振提供时钟源,可以参考图中的R6处贴上0欧电阻, XCLK引脚引出至外部,由外部控制器提供时钟;标号处的是摄像头引脚集中引出的排针接口,使用它可以方便地与STM32实验板中的排母连接。
摄像头与实验板的连接
通过排母,OV2640与STM32引脚的连接关系见图 STM32实验板引出的DCMI接口 。
以上原理图可查阅《ov2640—黑白原理图》及《野火F407开发板底板原理图》文档获知,若您使用的摄像头或实验板不一样,请根据实际连接的引脚修改程序。
53.5.2. 软件设计¶
为了使工程更加有条理,我们把摄像头控制相关的代码独立分开存储,方便以后移植。在“液晶显示”工程的基础上新建“bsp_ov2640.c”及“bsp_ov2640.h”文件, 这些文件也可根据您的喜好命名,它们不属于STM32标准库的内容,是由我们自己根据应用需要编写的。
53.5.2.1. 编程要点¶
初始化DCMI时钟,I2C时钟;
使用I2C接口向OV2640写入寄存器配置;
初始化DCMI工作模式;
初始化DMA,用于搬运DCMI的数据到显存空间进行显示;
编写测试程序,控制采集图像数据并显示到液晶屏。
53.5.2.2. 代码分析¶
摄像头硬件相关宏定义
我们把摄像头控制硬件相关的配置都以宏的形式定义到 “bsp_ov2640.h”文件中,其中包括I2C及DCMI接口的,见 代码清单:OV2640-2 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | /*摄像头接口 */
//IIC SCCB
#define CAMERA_I2C I2C1
#define CAMERA_I2C_CLK RCC_APB1Periph_I2C1
#define CAMERA_I2C_SCL_PIN GPIO_Pin_8
#define CAMERA_I2C_SCL_GPIO_PORT GPIOB
#define CAMERA_I2C_SCL_GPIO_CLK RCC_AHB1Periph_GPIOB
#define CAMERA_I2C_SCL_SOURCE GPIO_PinSource8
#define CAMERA_I2C_SCL_AF GPIO_AF_I2C1
#define CAMERA_I2C_SDA_PIN GPIO_Pin_9
#define CAMERA_I2C_SDA_GPIO_PORT GPIOB
#define CAMERA_I2C_SDA_GPIO_CLK RCC_AHB1Periph_GPIOB
#define CAMERA_I2C_SDA_SOURCE GPIO_PinSource9
#define CAMERA_I2C_SDA_AF GPIO_AF_I2C1
//VSYNC
#define DCMI_VSYNC_GPIO_PORT GPIOB
#define DCMI_VSYNC_GPIO_CLK RCC_AHB1Periph_GPIOB
#define DCMI_VSYNC_GPIO_PIN GPIO_Pin_7
#define DCMI_VSYNC_PINSOURCE GPIO_PinSource7
#define DCMI_VSYNC_AF GPIO_AF_DCMI
// HSYNC
#define DCMI_HSYNC_GPIO_PORT GPIOA
#define DCMI_HSYNC_GPIO_CLK RCC_AHB1Periph_GPIOA
#define DCMI_HSYNC_GPIO_PIN GPIO_Pin_4
#define DCMI_HSYNC_PINSOURCE GPIO_PinSource4
#define DCMI_HSYNC_AF GPIO_AF_DCMI
//PIXCLK
#define DCMI_PIXCLK_GPIO_PORT GPIOA
#define DCMI_PIXCLK_GPIO_CLK RCC_AHB1Periph_GPIOA
#define DCMI_PIXCLK_GPIO_PIN GPIO_Pin_6
#define DCMI_PIXCLK_PINSOURCE GPIO_PinSource6
#define DCMI_PIXCLK_AF GPIO_AF_DCMI
//PWDN
#define DCMI_PWDN_GPIO_PORT GPIOC
#define DCMI_PWDN_GPIO_CLK RCC_AHB1Periph_GPIOC
#define DCMI_PWDN_GPIO_PIN GPIO_Pin_0
//数据信号线
#define DCMI_D0_GPIO_PORT GPIOC
#define DCMI_D0_GPIO_CLK RCC_AHB1Periph_GPIOC
#define DCMI_D0_GPIO_PIN GPIO_Pin_6
#define DCMI_D0_PINSOURCE GPIO_PinSource6
#define DCMI_D0_AF GPIO_AF_DCMI
/*....省略部分数据线*/
|
以上代码根据硬件的连接,把与DCMI、I2C接口与摄像头通讯使用的引脚号、引脚源以及复用功能映射都以宏封装起来。
初始化DCMI的 GPIO及I2C
利用上面的宏,初始化DCMI的GPIO引脚及I2C,见 代码清单:OV2640-3 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | /**
* @brief 初始化控制摄像头使用的GPIO(I2C/DCMI)
* @param None
* @retval None
*/
void OV2640_HW_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStruct;
/***DCMI引脚配置***/
/* 使能DCMI时钟 */
RCC_AHB1PeriphClockCmd(DCMI_PWDN_GPIO_CLK|DCMI_VSYNC_GPIO_CLK |
DCMI_HSYNC_GPIO_CLK | DCMI_PIXCLK_GPIO_CLK|
DCMI_D0_GPIO_CLK| DCMI_D1_GPIO_CLK|, ENABLE);
/*控制/同步信号线*/
GPIO_InitStructure.GPIO_Pin = DCMI_VSYNC_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(DCMI_VSYNC_GPIO_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(DCMI_VSYNC_GPIO_PORT, DCMI_VSYNC_PINSOURCE, DCMI_VSYNC_AF);
GPIO_InitStructure.GPIO_Pin = DCMI_HSYNC_GPIO_PIN ;
GPIO_Init(DCMI_HSYNC_GPIO_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(DCMI_HSYNC_GPIO_PORT, DCMI_HSYNC_PINSOURCE, DCMI_HSYNC_AF);
GPIO_InitStructure.GPIO_Pin = DCMI_PIXCLK_GPIO_PIN ;
GPIO_Init(DCMI_PIXCLK_GPIO_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(DCMI_PIXCLK_GPIO_PORT, DCMI_PIXCLK_PINSOURCE, DCMI_PIXCLK_AF);
GPIO_InitStructure.GPIO_Pin = DCMI_PWDN_GPIO_PIN ;
GPIO_Init(DCMI_PWDN_GPIO_PORT, &GPIO_InitStructure);
/*PWDN引脚,高电平关闭电源,低电平供电*/
GPIO_ResetBits(DCMI_PWDN_GPIO_PORT,DCMI_PWDN_GPIO_PIN);
/*数据信号*/
GPIO_InitStructure.GPIO_Pin = DCMI_D0_GPIO_PIN ;
GPIO_Init(DCMI_D0_GPIO_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(DCMI_D0_GPIO_PORT, DCMI_D0_PINSOURCE, DCMI_D0_AF);
/*...省略部分数据信号线*/
/****** 配置I2C,使用I2C与摄像头的SCCB接口通讯*****/
/* 使能I2C时钟 */
RCC_APB1PeriphClockCmd(CAMERA_I2C_CLK, ENABLE);
/* 使能I2C使用的GPIO时钟 */
RCC_AHB1PeriphClockCmd(CAMERA_I2C_SCL_GPIO_CLK|CAMERA_I2C_SDA_GPIO_CLK, ENABLE);
/* 配置引脚源 */
GPIO_PinAFConfig(CAMERA_I2C_SCL_GPIO_PORT, CAMERA_I2C_SCL_SOURCE, CAMERA_I2C_SCL_AF);
GPIO_PinAFConfig(CAMERA_I2C_SDA_GPIO_PORT, CAMERA_I2C_SDA_SOURCE, CAMERA_I2C_SDA_AF);
/* 初始化GPIO */
GPIO_InitStructure.GPIO_Pin = CAMERA_I2C_SCL_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(CAMERA_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(CAMERA_I2C_SCL_GPIO_PORT, CAMERA_I2C_SCL_SOURCE, CAMERA_I2C_SCL_AF);
GPIO_InitStructure.GPIO_Pin = CAMERA_I2C_SDA_PIN ;
GPIO_Init(CAMERA_I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
/*初始化I2C模式 */
I2C_DeInit(CAMERA_I2C);
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStruct.I2C_OwnAddress1 = 0xFE;
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStruct.I2C_ClockSpeed = 40000;
/* 写入配置 */
I2C_Init(CAMERA_I2C, &I2C_InitStruct);
/* 使能I2C */
I2C_Cmd(CAMERA_I2C, ENABLE);
}
|
与所有使用到GPIO的外设一样,都要先把使用到的GPIO引脚模式初始化,以上代码把DCMI接口的信号线全都初始化为DCMI复用功能, 而PWDN则被初始化成普通的推挽输出模式,并且在初始化完毕后直接控制它为低电平,使能给摄像头供电。
函数中还包含了I2C的初始化配置,使用I2C与OV2640的SCCB接口通讯,这里的I2C模式配置与标准的I2C无异。
配置DCMI的模式
接下来需要配置DCMI的工作模式,我们通过编写OV2640_Init函数完成该功能,见 代码清单:OV2640-4 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #define FSMC_LCD_ADDRESS FSMC_Addr_ILI9806G_DATA
/**
* @brief 配置 DCMI/DMA 以捕获摄像头数据
* @param None
* @retval None
*/
void OV2640_Init(void)
{
DCMI_InitTypeDef DCMI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/*** 配置DCMI接口 ***/
/* 使能DCMI时钟 */
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI, ENABLE);
/* DCMI 配置*/
DCMI_InitStructure.DCMI_CaptureMode = DCMI_CaptureMode_Continuous;
DCMI_InitStructure.DCMI_SynchroMode = DCMI_SynchroMode_Hardware;
DCMI_InitStructure.DCMI_PCKPolarity = DCMI_PCKPolarity_Rising;
DCMI_InitStructure.DCMI_VSPolarity = DCMI_VSPolarity_Low;
DCMI_InitStructure.DCMI_HSPolarity = DCMI_HSPolarity_Low;
DCMI_InitStructure.DCMI_CaptureRate = DCMI_CaptureRate_All_Frame;
DCMI_InitStructure.DCMI_ExtendedDataMode = DCMI_ExtendedDataMode_8b;
DCMI_Init(&DCMI_InitStructure);
//配置DMA传输,直接配置循环传输即可
OV2640_DMA_Config(FSMC_LCD_ADDRESS,1);
/* 配置中断 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* 配置帧中断,接收到帧同步信号就进入中断 */
NVIC_InitStructure.NVIC_IRQChannel = DCMI_IRQn ; //帧中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DCMI_ITConfig (DCMI_IT_FRAME,ENABLE);
}
|
该函数的执行流程如下:
(1) 使能DCMI外设的时钟, 它是挂载在AHB2总线上的;
(2) 根据摄像头的时序和硬件连接的要求,配置DCMI工作模式为:使用硬件同步, 连续采集所有帧数据,采集时使用8根数据线,PIXCLK被设置为上升沿有效,VSYNC和HSYNC都被设置成低电平有效;
(3) 调用OV2640_DMA_Config函数开始DMA数据传输, 它包含本次传输的目的首地址及传输的数据量,后面我们再详细解释 ;
(4) 配置DCMI的帧传输中断,可以用于统计帧率。另外, 为了防止有时DMA出现传输错误或传输速度跟不上导致数据错位、偏移等问题,每次DCMI接收到摄像头的一帧数据,得到新的帧同步信号后(VSYNC),就进入中断,重新设置液晶屏显示窗口,可使它重新开始一帧的数据传输(本工程没有实现这个功能)。
配置DMA数据传输
上面的DCMI配置函数中调用了OV2640_DMA_Config函数开始了DMA传输,该函数的定义见 代码清单:OV2640-5 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | /**
* @brief 配置 DCMI/DMA 以捕获摄像头数据
* @param DMA_Memory0BaseAddr:本次传输的目的首地址
* @param DMA_BufferSize:本次传输的数据量(单位为字,即4字节)
*/
void OV2640_DMA_Config(uint32_t DMA_Memory0BaseAddr,uint16_t DMA_BufferSize)
{
DMA_InitTypeDef DMA_InitStructure;
/* 配置DMA从DCMI中获取数据*/
/* 使能DMA*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_Cmd(DMA2_Stream1,DISABLE);
while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE) {}
DMA_InitStructure.DMA_Channel = DMA_Channel_1;
DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS; //DCMI数据寄存器地址
//DMA传输的目的地址(传入的参数)
DMA_InitStructure.DMA_Memory0BaseAddr = DMA_Memory0BaseAddr;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize =DMA_BufferSize; //传输的数据大小(传入的参数)
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
//液晶数据地址,不自增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_INC8;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
/*DMA初始化 */
DMA_Init(DMA2_Stream1, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream1,ENABLE);
while (DMA_GetCmdStatus(DMA2_Stream1) != ENABLE) {}
}
|
该函数跟普通的DMA配置无异,它把DCMI接收到的数据从它的数据寄存器搬运到液晶显示器上。 它包含2个输入参数:DMA_Memory0BaseAddr和DMA_BufferSize,其中DMA_Memory0BaseAddr用于设置本次DMA传输的目的首地址, 该参数会被赋值到结构体成员DMA_InitStructure.DMA_Memory0BaseAddr中。
根据前面的函数OV2640_Init对它的调用,DMA_Memory0BaseAddr的值为ILI9806G液晶屏接收显存数据的地址,当STM32往该地址写入数据时, FSMC会自动产生时序把该数据写入到液晶屏,从而可以使用液晶屏显示摄像头采集得的图像。
另一个参数DMA_BufferSize则用于指示本次DMA传输的数据量,它会被赋值到结构体成员DMA_InitStructure.DMA_BufferSize中, 要注意它的单位是一个字,即4字节,是DCMI数据寄存器的单位大小。
根据前面的函数OV2640_Init对它的调用,DMA_BufferSize的值被直接赋为1,而DMA又被配置为循环模式,所以当开始传输时, STM32会把摄像头采集到DCMI数据寄存器里的4字节数据使用DMA分2次写入到液晶屏数据地址,然后不断循环该过程。
若液晶屏提前调用ILI9806G_OpenWindow设置好了数据的显示窗口,那么这些数据就会一个一个(16位的数据)地显示在对应的位置,从而实时显示摄像头采集得到的数据。
DCMI帧中断
OV2640_Init函数初始化了DCMI,使能了帧中断,当摄像头采集到一帧图像的,会进入该中断,见 代码清单:OV2640-6 中的DCMI_IRQHandler。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | extern uint8_t fps;
//使用帧中断重置line_num,可防止有时掉数据的时候DMA传送行数出现偏移
void DCMI_IRQHandler(void)
{
if ( DCMI_GetITStatus (DCMI_IT_FRAME) == SET ) {
/*传输完一帧,计数复位*/
fps++; //帧率计数
DCMI_ClearITPendingBit(DCMI_IT_FRAME);
}
}
|
在本工程中的帧中断服务函数中,仅仅是使用了一个变量用于统计帧数,以便配合定时器计算帧率。实际上, 为了避免有时STM32内部DMA传输受到阻塞而跟不上外部摄像头信号导致的数据错误(即液晶显示摄像头的数据错位), 可以在本断服务函数中重新调用液晶屏的ILI9806G_OpenWindow复位液晶显示的起始位置,达到重新同步的目的, 由于本工程实测并没有错位的问题,所以这里省略了,当您了解了整个工程的原理后,可以自行添加防错位的处理代码。
使能DCMI采集
以上是我们使用DCMI的传输配置,但它还没有使能DCMI采集,在实际使用中还需要调用下面两个库函数开始采集数据。
//使能DCMI采集数据
DCMI_Cmd(ENABLE);
DCMI_CaptureCmd(ENABLE);
读取OV2640芯片ID
配置完了STM32的DCMI,还需要控制摄像头,它有很多寄存器用于配置工作模式。利用STM32的I2C接口,可向OV2640的寄存器写入控制参数, 我们先写个读取芯片ID的函数测试一下,见 代码清单:OV2640-7 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | //存储摄像头ID的结构体
typedef struct
{
uint8_t Manufacturer_ID1;
uint8_t Manufacturer_ID2;
uint8_t PIDH;
uint8_t PIDL;
} OV2640_IDTypeDef;
/*寄存器地址*/
#define OV2640_DSP_RA_DLMT 0xFF
#define OV2640_SENSOR_MIDH 0x1C
#define OV2640_SENSOR_MIDL 0x1D
#define OV2640_SENSOR_PIDH 0x0A
#define OV2640_SENSOR_PIDL 0x0B
/**
* @brief 读取摄像头的ID.
* @param OV2640ID: 存储ID的结构体
* @retval None
*/
void OV2640_ReadID(OV2640_IDTypeDef *OV2640ID)
{
/*OV2640有两组寄存器,设置0xFF寄存器的值为0或为1时可选择使用不同组的寄存器*/
OV2640_WriteReg(OV2640_DSP_RA_DLMT, 0x01);
/*读取寄存芯片ID*/
OV2640ID->Manufacturer_ID1 = OV2640_ReadReg(OV2640_SENSOR_MIDH);
OV2640ID->Manufacturer_ID2 = OV2640_ReadReg(OV2640_SENSOR_MIDL);
OV2640ID->PIDH = OV2640_ReadReg(OV2640_SENSOR_PIDH);
OV2640ID->PIDL = OV2640_ReadReg(OV2640_SENSOR_PIDL);
}
|
在OV2640的MIDH及MIDL寄存器中存储了它的厂商ID,默认值为0x7F和0xA2;而PIDH及PIDL寄存器存储了产品ID,PIDH的默认值为0x26, PIDL的默认值不定,可能的值为0x40、0x41及0x42。在代码中我们定义了一个结构体OV2640_IDTypeDef专门存储这些读取得的ID信息。
由于这些寄存器都是属于Sensor组的,所以在读取这些寄存器的内容前,需要先把OV2640中0xFF地址的DLMT寄存器值设置为1。
OV2640_ReadID函数中使用的OV2640_ReadReg及OV2640_WriteReg函数是使用STM32的I2C外设向某寄存器读写单个字节数据的底层函数, 它与我们前面章节中用到的I2C函数大同小异,就不展开分析了。
向OV2640写入寄存器配置
检测到OV2640的存在后,向它写入配置参数,见 代码清单:OV2640-8 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | uint16_t img_width=852, img_height=480;
/* 使用用 UXGA 模式,再根据所需的图像窗口裁剪 */
const unsigned char OV2640_UXGA[][2]=
{
0xff, 0x00,
0x2c, 0xff,
0x2e, 0xdf,
0xff, 0x01,
0x3c, 0x32,
0x11, 0x00,
0x09, 0x02,
0x04, 0xD8, //水平翻转
0x13, 0xe5,
/*....以下省略*/
}
/**
* @brief 配置OV2640为UXGA模式,并设置输出图像大小
* @param None
* @retval None
*/
void OV2640_UXGAConfig(void)
{
uint32_t i;
/* 写入寄存器配置 */
for (i=0; i<(sizeof(OV2640_UXGA)/2); i++)
{
OV2640_WriteReg(OV2640_UXGA[i][0], OV2640_UXGA[i][1]);
}
/*设置输出的图像大小*/
OV2640_OutSize_Set(img_width,img_height);
}
|
这个OV2640_UXGAConfig函数简单粗暴,它直接把一个二维数组OV2640_UXGA使用I2C传输到OV2640中,该二维数组的第一维存储的是寄存器地址, 第二维存储的是对应寄存器要写入的控制参数。
如果您对这些寄存器配置感兴趣,可以一个个对着OV2640的寄存器说明来阅读,阅读时要注意区分DLMT寄存器(地址0xFF)为1或为0时的寄存器组。 总的来部,这些配置主要是把OV2640配置成了UXGA时序模式,并使用8根数据线输出格式为RGB565的图像数据。
其中UXGA时序是指它最大可输出1600x1200分辨率的图像,OV2640还支持使用SVGA时序输出最大分辨率为800x600的图像,相对于UXGA,它可使用更高的帧率输出, 但由于它规定好了数据是800行、600列,而我们的液晶屏为(854行、480列),所以就把OV2640配置成UXGA模式了。通过修改COM7寄存器(地址0x12)可改变时序模式。
设置液晶屏显示窗口
初始化摄像头后,即可以使能DCMI采集,并且设置液晶显示窗口,然后DMA会把数据传输至液晶屏上,见 代码清单:OV2640-9 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | extern uint16_t img_width, img_height;
/**
* @brief 液晶屏开窗,使能摄像头数据采集
* @param 无
* @retval 无
*/
void ImagDisp(void)
{
//扫描模式,横屏
ILI9806G_GramScan(5);
LCD_SetFont(&Font16x32);
LCD_SetColors(RED,BLACK);
ILI9806G_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH); /* 清屏,显示全黑 */
ILI9806G_DispStringLine_EN(LINE(0),"BH 4.8 inch LCD + OV2640");
/*DMA会把数据传输到液晶屏,开窗后数据按窗口排列 */
ILI9806G_OpenWindow(0,0,img_width,img_height);
OV2640_Capture_Control(ENABLE);
}
|
在本函数中,先调用了ILI9806G_GramScan函数把液晶屏设置成横屏显示模式,然后使用ILI9806G_OpenWindow设置一个宽img_width高img_height的窗口, 根据液晶驱动FSMC的配置,开窗后只要我们往地址FSMC_LCD_ADDRESS写入的数据会一个一个地添加到该窗口中进行显示, 而前面的代码已经配置了DMA搬运摄像头数据的工作,只要执行完本函数最后的OV2640_Capture_Control使能DCMI采集, 一切就会开始正常运转:摄像头根据寄存器的配置输出RGB565的图像,STM32的DCMI外设把接收到的图像数据缓存到DCMI数据寄存器中, 当接收到2个像素数据后,DMA开始传输,把这两个数据搬运到内存地址FSMC_LCD_ADDRESS,往该地址写入数据时触发FSMC产生时序写入到液晶屏, 最终液晶屏把数据显示到窗口上,如此循环往复,完成一帧数据的采集和显示,不断循环,即可实现连续的实时采集与显示。
main函数
最后我们来编写main函数,利用前面讲解的函数,控制采集图像,见 代码清单:OV2640-10 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | /**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
/*图像大小,修改这两个值即可改变图像的大小*/
//要求为4的倍数
img_width=852;
img_height =480;
ILI9806G_Init (); //LCD 初始化
LCD_SetFont(&Font16x32);
LCD_SetColors(RED,BLACK);
ILI9806G_Clear(0,0,LCD_X_LENGTH,LCD_Y_LENGTH); /* 清屏,显示全黑 */
Debug_USART_Config();
/* 配置SysTick 为1ms中断一次,时间到后触发定时中断,
*进入stm32fxx_it.c文件的SysTick_Handler处理,通过数中断次数计时
*/
SysTick_Init();
//液晶扫描方向
ILI9806G_GramScan(5);
CAMERA_DEBUG("STM32F407 DCMI 驱动OV2640例程");
/* 初始化摄像头GPIO及IIC */
OV2640_HW_Init();
/* 读取摄像头芯片ID,确定摄像头正常连接 */
OV2640_ReadID(&OV2640_Camera_ID);
if (OV2640_Camera_ID.PIDH == 0x26) {
sprintf(dispBuf, "OV2640 camera,ID:0x%x", OV2640_Camera_ID.PIDH);
ILI9806G_DispStringLine_EN(LINE(0),dispBuf);
CAMERA_DEBUG("检测到摄像头 %x %x",OV2640_Camera_ID.Manufacturer_ID1 ,OV2640_Camera_ID.Manufacturer_ID2);
} else {
LCD_SetTextColor(RED);
ILI9806G_DispString_EN(10,10,"Can not detect OV2640 module,please check the connection!");
CAMERA_DEBUG("没有检测到OV2640摄像头,请重新检查连接。");
while (1);
}
/*使用寄存器表初始化摄像头*/
OV2640_Init();
OV2640_UXGAConfig();
/*DMA直接传输摄像头数据到LCD屏幕显示*/
ImagDisp();
while (1) {
//使用串口输出帧率
if (Task_Delay[0]==0) {
/*输出帧率*/
CAMERA_DEBUG("\r\n帧率:%.1f/s \r\n", (double)fps/5.0);
//重置
fps =0;
Task_Delay[0]=5000; //此值每1ms会减1,减到0才可以重新进来这里
}
}
}
|
在main函数中,首先初始化了液晶屏、串口和定时器等外设。
摄像头控制部分,首先调用了OV2640_HW_Init函数初始化DCMI的GPIO及I2C,然后调用OV2640_ReadID函数检测摄像头与实验板是否正常连接, 若连接正常则调用OV2640_Init函数初始化DCMI的工作模式及DMA,再调用OV2640_UXGAConfig函数向OV2640写入寄存器配置,最后, 一定要记得调用ImagDisp使能DCMI开始捕获数据,这样才能正常开始工作。
在main函数的while循环里,使用定时器每5秒统计一次帧率,并使用串口输出。
53.5.2.3. 下载验证¶
把OV2640接到实验板的摄像头接口中,用USB线连接开发板,编译程序下载到实验板,并上电复位,液晶屏会显示摄像头采集得的图像,通过旋转镜头可以调焦。