1)实验平台:正点原子领航者ZYNQ
开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/
FPGA/zdyz_linhanz.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:876744900
5)关注正点原子公众号,获取最新资料
第二十三章OV5640摄像头LCD显示
OV5640同OV7725一样,都是OmniVision(豪威科技)公司生产的CMOS图像传感器。不同的是,OV5640支持更高的分辨率、采集速率,具有更高的图像处理性能,主要应用在
手机、数码相机、电脑多媒体等领域。本章我们将使用Zynq开发板实现对OV5640的数字图像采集并通过LCD实时显示。
本章包括以下几个部分:
22323.1OV5640简介
23.2实验任务
23.3硬件设计
23.4软件设计
23.5下载验证
23.1OV5640简介
OV5640是一款1/4英寸单芯片图像传感器,其感光阵列达到2592*1944(即500W像素),能实现最快15fps QSXVGA(2592*1944)或者90fps VGA(640*480)分辨率的图像采集。传感器采用OmniVision推出的OmniBSI(背面照度)技术,使传感器达到更高的性能,如高灵敏度、低串扰和低噪声。传感器内部集成了图像处理的功能,包括自动曝光控制(AEC)、自动白平衡(AWB)等。同时该传感器支持LED补光、MIPI(移动产业处理器接口)输出接口和DVP(数字视频并行)输出接口选择、ISP(图像信号处理)以及AFC(自动聚焦控制)等功能。
OV5640的功能框图如下图所示:
图 23.1.1 OV5640功能框图
由上图可知,时序发生器(
timing generator)控制着感光阵列(image array)、放大器(AMP)、AD转换以及输出外部时序信号(VSYNC、HREF和PCLK),外部时钟XVCLK经过PLL锁相环后输出的时钟作为系统的控制时钟;感光阵列将光信号转化成模拟信号,经过增益放大器之后进入10位AD转换器;AD转换器将模拟信号转化成数字信号,并且经过ISP进行相关图像处理,最终输出所配置格式的10位视频数据流。增益放大器控制以及ISP等都可以通过寄存器(registers)来配置,配置寄存器的接口就是SCCB接口,该接口协议兼容IIC协议。
OV5640使用的是两线式SCCB接口总线,有关SCCB总线的详细介绍可以参考“OV7725摄像头LCD显示实验”中OV7725简介部分。虽然OV5640和OV7725都是采用SCCB接口总线来配置寄存器,但不同的是,OV7725是用8位(1个字节)来表示寄存器地址,而OV5640是用16位(两个字节)表示寄存器地址。
OV5640 SCCB的写传输协议如下图所示:
图 23.1.2 OV5640 SCCB写传输协议
上图中的ID ADDRESS是由7位器件地址和1位读写控制位构成(0:写 1:读),OV5640的器件地址为7’h3c,所以在写传输协议中,ID Address(W)= 8’h78(器件地址左移1位,低位补0);Sub-address(H)为高8位寄存器地址,Sub-address(L)为低8位寄存器地址,在OV5640众多寄存器中,有些寄存器是可改写的,有些是只读的,只有可改写的寄存器才能正确写入;Write Data为8位写数据,每一个寄存器地址对应8位的配置数据。
在OV5640正常工作之前,必须先对传感器进行初始化,即通过配置寄存器使其工作在预期的工作模式,以及得到较好画质的图像。因为SCCB的写传输协议和IIC几乎相同,因此我们可以直接使用IIC的驱动程序来配置摄像头。当然这么多寄存器也并非都需要配置,很多寄存器可以采用默认的值。OV公司提供了OV5640的软件应用手册(OV5640 Software Application Note,位于开发板所随附的资料“7_硬件资料/4_OV5640资料/OV5640_camera_module_software_application_notes.pdf”),如果某些寄存器不知道如何配置可以参考此手册,下表是本程序用到的关键寄存器的配置说明。
表 23.1.1 OV5640关键寄存器配置说明
OV5640的寄存器较多,对于其它寄存器的描述可以参考OV5640的数据手册。需要注意的是,OV5640的数据手册并没有提供全部的寄存器描述,而大多数必要的寄存器配置在ov5640的软件应用手册中可以找到,可以结合这两个手册学习如何对OV5640进行配置。
输出图像参数设置
接下来,我们介绍一下OV5640的ISP输入窗口设置、预缩放窗口设置和输出大小窗口设置,这几个设置与我们的正常使用密切相关,有必要了解一下,它们的设置关系如下图所示:
图 23.1.3 图像窗口设置
ISP输入窗口设置(ISP Input Size)允许用户设置整个传感器显示区域(physical pixel size,2632*1951,其中2592*1944像素是有效的),开窗范围从0*0~2632*1951都可以任意设置。也就是上图中的X_ADDR_ST(寄存器地址0x3800、0x3801)、Y_ADDR_ST(寄存器地址0x3802、0x3803)、X_ADDR_END(寄存器地址0x3804、0x3805)和Y_ADDR_END(寄存器地址0x3806、0x3807)寄存器。该窗口设置范围中的像素数据将进入ISP进行图像处理。
预缩放窗口设置(pre-scaling size)允许用户在ISP输入窗口的基础上进行裁剪,用于设置将进行缩放的窗口大小,该设置仅在ISP输入窗口内进行X/Y方向的偏移。可以通过X_OFFSET(寄存器地址0x3810、0x3811)和Y_OFFSET(寄存器地址0x3812、0x3813)进行配置。
输出大小窗口设置(data output size)是在预缩放窗口的基础上,经过内部DSP进行缩放处理,并将处理后的数据输出给外部的图像窗口,图像窗口控制着最终的图像输出尺寸。可以通过X_OUTPUT_SIZE(寄存器地址0x3808、0x3809)和Y_OUTPUT_SIZE(寄存器地址0x380A、0x380B)进行配置。注意:当输出大小窗口与预缩放窗口比例不一致时,图像将进行缩放处理(图像变形),仅当两者比例一致时,输出比例才是1:1(正常图像)。
图 23.1.3中,右侧data output size区域,才是OV5640输出给外部的图像尺寸,也就是显示在显示器或者液晶屏上面的图像大小。输出大小窗口与预缩放窗口比例不一致时,会进行缩放处理,在显示器上面看到的图像将会变形。
输出像素格式
OV5640支持多种不同的数据像素格式,包括YUV(亮度参量和色度参量分开表示的像素格式)、RGB(其中RGB格式包含RGB565、RGB555等)以及RAW(原始图像数据),通过寄存器地址0x4300配置成不同的数据像素格式。
由于数据像素格式常用RGB565,我们这里也将ov5640配置为RGB565格式。由上表(表 23.1.1)可知,将寄存器0x4300寄存器的Bit[7:4]设置成0x6即可。OV5640支持调节RGB565输出格式中各颜色变量的顺序,对于我们常见的应用来说,一般是使用RGB或BGR序列。我们在“OV7725摄像头LCD显示实验”的章节中介绍过,OV7725摄像头按照RGB的顺序输出,本章我们将OV5640输出的RGB565的颜色顺序和OV7725保持一致,将寄存器0x4300寄存器的Bit[3:0]设置成0x1。因此,“OV7725摄像头LCD显示实验”章节中的图像采集模块可以直接用来采集OV5640输出的图像。
彩条测试模式
图像传感器配置成彩条测试模式后,会输出彩色的条纹,方便测试图像传感器是否正常工作,通过配置寄存器0x503d的Bit[7]位打开和关闭彩条模式。当需要打开彩条模式时,寄存器0x503d配置成0x80,关闭时配置成0x00,下图为打开彩条模式后图像输出的条纹。
图 23.1.4 彩条模式下的图像条纹
LED闪光灯
当外界环境光较暗时,传感器采集图像会受到较大影响,此时可以通过打开LED补光灯来弥补光照不足所带来的影响,就像手机在夜晚拍照时也会打开闪光灯来提高图像质量。通过配置寄存器0x3016=0x02,0x301c=0x02来使能LED补光灯功能;配置寄存器0x3019=0x02打开闪光灯,0x3019=0x00关闭闪光灯。
图像输出时序
接下来,我们介绍一下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时钟输出一个像素或者半个像素(像素数据的高8位或者低8位)。
VSYNC:帧同步信号。
HREF/HSYNC:行同步信号。
D[9:0]:像素数据,在RGB565格式中,只有高8位是有效的。
tPclk:一个时钟周期 。
tp:一个像素点的周期,在RGB565和YUV422输出格式下,tp=2*tPclk;Raw输出格式下,tp=tPclk。
下图为OV5640输出图像数据的行时序图。
图 23.1.5 OV5640行时序图
从上图可以看出,传感器在HREF为高电平的时候输出图像数据,当HREF变高后,每一个 PCLK时钟,输出一个8位或者10位像素数据。比如我们采用QSXGA时序,RGB565格式输出,tp=2*tPclk,每2个字节组成一个像素的颜色,这样每行总共输出2592*2个PCLK,也就是2592*2个字节。
再来看看帧时序(QSXGA模式,分辨率2592*1944),如下图所示:
图 23.1.6 OV5640 QSXGA帧时序
由上图可知,VSYNC的上升沿作为一帧的开始,高电平同步脉冲的时间为5688tp,紧接着等待48276tp时间后,HREF开始拉高,此时输出有效数据;HREF由2592tp个高电平和252tp个低电平构成;最后一行图像数据输出完成之后等待14544tp时间,一帧数据传输结束。所以输出一帧图像的时间实际上是tFrame = 5596992tp。
从OV5640的行时序图和帧时序图可以发现,其输出时序和OV7725是非常相似的,只是时间参数不同而已,大家可以参考“OV7725摄像头LCD显示实验”中帧时序的介绍来学习OV5640的输出时序。
23.2实验任务
本节实验任务是使用领航者ZYNQ开发板及OV5640摄像头实现图像采集,并通过LCD实时显示。
23.3硬件设计
本次实验的硬件
电路和“OV7725摄像头LCD显示实验”中的硬件电路是基本相同的,都使用了领航者Zynq开发板上的摄像头扩展接口,以及LCD接口,IO管脚位置的配置也是一样的。
唯一的不同点在于,原先在“OV7725摄像头LCD显示实验”中的Zynq输出给摄像头扩展接口的“cam_sgm_ctrl”信号,在本实验中变为了Zynq输出给OV5640的“
电源休眠模式选择”信号cam_pwdn;且原来的cam_sgm_ctrl是我们需要一直赋值为高电平,但对于OV5640,cam_pwdn我们需要将其一直赋值为低电平,表示不休眠即正常工作模式。
OV5640摄像头的管脚分配如下表所示:
表 23.3.1 OV5640摄像头管脚分配
在系统架构上,本次实验与“OV7725摄像头LCD显示实验”也是类似的,只是用于采集摄像头数据的摄像头采集IP核稍微不同,即上文提到的一个管脚功能和赋值的不同。本次实验的系统架构如下图所示:
图 23.3.1 系统架构框图
有关整个系统架构的搭建过程,请读者参考OV7725摄像头LCD显示实验。连线后的设计画布如下图所示:
图 23.3.3 IP Integrator设计画布
其中,OV5640图像采集模块如下图所示:
图 23.3.2 OV5640图像采集模块
ov5640_capture_data IP核的源代码与“OV7725摄像头LCD显示实验”中的ov7725_capture_data IP核的代码基本是一样的,唯一的不同点在于cam_pwdn信号的赋值,如下所示:
- 1 module ov5640_capture_data(
- 2 input rst_n , //复位信号
- 3
- 4 //摄像头接口
- 与OV7725摄像头LCD显示实验相同的代码部分被省略。。。。。。
- 9 output cam_rst_n , //cmos 复位信号,低电平有效
- 10 output cam_pwdn , //电源休眠模式选择
- 与OV7725摄像头LCD显示实验相同的代码部分被省略。。。。。。
- 50
- 51 //不对摄像头硬件复位,固定高电平
- 52 assign cam_rst_n = 1'b1;
- 53
- 54 //电源休眠模式选择 0:正常模式 1:电源休眠模式
- 55 assign cam_pwdn = 1'b0;
- 56
- 与OV7725摄像头LCD显示实验相同的代码部分被省略。。。。。。
- 141
- 142 endmodule
为工程添加约束文件,约束文件如下:
- #----------------------摄像头接口的时钟---------------------------
- #72M
- create_clock -period 13.888 -name cam_pclk [get_ports cam_pclk]
- set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets cam_pclk_IBUF]
- #----------------------LCD接口---------------------------
- set_property -dict {PACKAGE_PIN Y18 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[0]}]
- set_property -dict {PACKAGE_PIN Y19 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[1]}]
- set_property -dict {PACKAGE_PIN W20 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[2]}]
- set_property -dict {PACKAGE_PIN V20 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[3]}]
- set_property -dict {PACKAGE_PIN U14 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[4]}]
- set_property -dict {PACKAGE_PIN U15 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[5]}]
- set_property -dict {PACKAGE_PIN T20 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[6]}]
- set_property -dict {PACKAGE_PIN U20 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[7]}]
- set_property -dict {PACKAGE_PIN W14 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[8]}]
- set_property -dict {PACKAGE_PIN Y14 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[9]}]
- set_property -dict {PACKAGE_PIN N15 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[10]}]
- set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[11]}]
- set_property -dict {PACKAGE_PIN V16 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[12]}]
- set_property -dict {PACKAGE_PIN W16 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[13]}]
- set_property -dict {PACKAGE_PIN W18 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[14]}]
- set_property -dict {PACKAGE_PIN W19 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[15]}]
- set_property -dict {PACKAGE_PIN T10 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[16]}]
- set_property -dict {PACKAGE_PIN T11 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[17]}]
- set_property -dict {PACKAGE_PIN P14 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[18]}]
- set_property -dict {PACKAGE_PIN R14 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[19]}]
- set_property -dict {PACKAGE_PIN V13 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[20]}]
- set_property -dict {PACKAGE_PIN U13 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[21]}]
- set_property -dict {PACKAGE_PIN G15 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[22]}]
- set_property -dict {PACKAGE_PIN H15 IOSTANDARD LVCMOS33} [get_ports {lcd_rgb_tri_io[23]}]
- set_property -dict {PACKAGE_PIN U17 IOSTANDARD LVCMOS33} [get_ports lcd_hs]
- set_property -dict {PACKAGE_PIN P20 IOSTANDARD LVCMOS33} [get_ports lcd_vs]
- set_property -dict {PACKAGE_PIN N20 IOSTANDARD LVCMOS33} [get_ports lcd_de]
- set_property -dict {PACKAGE_PIN Y16 IOSTANDARD LVCMOS33} [get_ports lcd_bl]
- set_property -dict {PACKAGE_PIN T16 IOSTANDARD LVCMOS33} [get_ports lcd_clk]
- #----------------------摄像头接口---------------------------
- set_property -dict {PACKAGE_PIN P15 IOSTANDARD LVCMOS33} [get_ports cam_rst_n]
- set_property -dict {PACKAGE_PIN R17 IOSTANDARD LVCMOS33} [get_ports cam_pwdn]
- set_property -dict {PACKAGE_PIN P16 IOSTANDARD LVCMOS33} [get_ports {cam_data[0]}]
- set_property -dict {PACKAGE_PIN V15 IOSTANDARD LVCMOS33} [get_ports {cam_data[1]}]
- set_property -dict {PACKAGE_PIN W15 IOSTANDARD LVCMOS33} [get_ports {cam_data[2]}]
- set_property -dict {PACKAGE_PIN T12 IOSTANDARD LVCMOS33} [get_ports {cam_data[3]}]
- set_property -dict {PACKAGE_PIN U12 IOSTANDARD LVCMOS33} [get_ports {cam_data[4]}]
- set_property -dict {PACKAGE_PIN V12 IOSTANDARD LVCMOS33} [get_ports {cam_data[5]}]
- set_property -dict {PACKAGE_PIN W13 IOSTANDARD LVCMOS33} [get_ports {cam_data[6]}]
- set_property -dict {PACKAGE_PIN T14 IOSTANDARD LVCMOS33} [get_ports {cam_data[7]}]
- set_property -dict {PACKAGE_PIN T17 IOSTANDARD LVCMOS33} [get_ports cam_href]
- set_property -dict {PACKAGE_PIN T15 IOSTANDARD LVCMOS33} [get_ports cam_pclk]
- set_property -dict {PACKAGE_PIN R18 IOSTANDARD LVCMOS33} [get_ports cam_vsync]
- #cam_scl:
- set_property -dict {PACKAGE_PIN P18 IOSTANDARD LVCMOS33} [get_ports {emio_sccb_tri_io[0]}]
- #cam_sda:
- set_property -dict {PACKAGE_PIN N17 IOSTANDARD LVCMOS33} [get_ports {emio_sccb_tri_io[1]}]
- set_property PULLUP true [get_ports {emio_sccb_tri_io[1]}]
最后在左侧Flow Navigator导航栏中找到PROGRAM AND DEBUG,点击该选项中的“ Generate Bitstream”,对设计进行综合、实现、并生成Bitstream文件。
在生成Bitstream之后,在菜单栏中选择 File > Export > Export hardware导出硬件,并在弹出的对话框中,勾选“Include bitstream”。然后在菜单栏选择File > Launch SDK,启动SDK软件。
23.4软件设计
打开SDK后,我们创建一个Application Project,添加到工程中的源文件与“OV7725摄像头LCD显示实验”基本类似,不同之处在于,ov5640文件夹下存放用于初始化ov5640的源代码。如下图所示:
图 23.4.1 SDK工程的源文件
main函数的代码如下所示:
- 1 #include
- 2 #include
- 3 #include
- 4 #include "xil_types.h"
- 5 #include "xil_cache.h"
- 6 #include "xparameters.h"
- 7 #include "xgpio.h"
- 8 #include "xaxivdma.h"
- 9 #include "xaxivdma_i.h"
- 10 #include "display_ctrl/display_ctrl.h"
- 11 #include "vdma_api/vdma_api.h"
- 12 #include "emio_sccb_cfg/emio_sccb_cfg.h"
- 13 #include "ov5640/ov5640_init.h"
- 14
- 15 //宏定义
- 16 #define BYTES_PIXEL 3 //像素字节数,RGB888占3个字节
- 17 #define FRAME_BUFFER_NUM 3 //帧缓存个数3
- 18 #define DYNCLK_BASEADDR XPAR_AXI_DYNCLK_0_BASEADDR //动态时钟基地址
- 19 #define VDMA_ID XPAR_AXIVDMA_0_DEVICE_ID //VDMA器件ID
- 20 #define DISP_VTC_ID XPAR_VTC_0_DEVICE_ID //VTC器件ID
- 21 //PL端 AXI GPIO 0(lcd_id)器件 ID
- 22 #define AXI_GPIO_0_ID XPAR_AXI_GPIO_0_DEVICE_ID
- 23 //使用AXI GPIO(lcd_id)通道1
- 24 #define AXI_GPIO_0_CHANEL 1
- 25
- 26 //全局变量
- 27 //frame buffer的起始地址
- 28 unsigned int const frame_buffer_addr = (XPAR_PS7_DDR_0_S_AXI_BASEADDR
- 29 + 0x1000000);
- 30 XAxiVdma vdma;
- 31 DisplayCtrl dispCtrl;
- 32 XGpio axi_gpio_inst; //PL端 AXI GPIO 驱动实例
- 33 VideoMode vd_mode;
- 34 unsigned int lcd_id;
- 35
- 36 int main(void)
- 37 {
- 38 u32 status;
- 39 u16 cmos_h_pixel; //ov5640 DVP 输出水平像素点数
- 40 u16 cmos_v_pixel; //ov5640 DVP 输出垂直像素点数
- 41 u16 total_h_pixel; //ov5640 水平总像素大小
- 42 u16 total_v_pixel; //ov5640 垂直总像素大小
- 43
- 44 //获取LCD的ID
- 45 XGpio_Initialize(&axi_gpio_inst, AXI_GPIO_0_ID);
- 46 lcd_id = LTDC_PanelID_Read(&axi_gpio_inst,AXI_GPIO_0_CHANEL);
- 47 xil_printf("lcd_id = %xnr",lcd_id);
- 48
- 49 //根据获取的LCD的ID号来进行ov5640显示分辨率参数的选择
- 50 switch(lcd_id){
- 51 case 0x4342 :
- 52 cmos_h_pixel = 480;
- 53 cmos_v_pixel = 272;
- 54 total_h_pixel = 1800;
- 55 total_v_pixel = 1000;
- 56 break;
- 57 case 0x7084 :
- 58 cmos_h_pixel = 800;
- 59 cmos_v_pixel = 480;
- 60 total_h_pixel = 1800;
- 61 total_v_pixel = 1000;
- 62 break;
- 63 case 0x7016 :
- 64 cmos_h_pixel = 1024;
- 65 cmos_v_pixel = 600;
- 66 total_h_pixel = 2200;
- 67 total_v_pixel = 1000;
- 68 break;
- 69 case 0x1018 :
- 70 cmos_h_pixel = 1280;
- 71 cmos_v_pixel = 800;
- 72 total_h_pixel = 2570;
- 73 total_v_pixel = 980;
- 74 break;
- 75 default :
- 76 cmos_h_pixel = 480;
- 77 cmos_v_pixel = 272;
- 78 total_h_pixel = 1800;
- 79 total_v_pixel = 1000;
- 80 break;
- 81 }
- 82
- 83 emio_init(); //初始化EMIO
- 84 status = ov5640_init( cmos_h_pixel, //初始化ov5640
- 85 cmos_v_pixel,
- 86 total_h_pixel,
- 87 total_v_pixel);
- 88 if(status == 0)
- 89 xil_printf("OV5640 detected successful!rn");
- 90 else
- 91 xil_printf("OV5640 detected failed!rn");
- 92
- 93 //根据获取的LCD的ID号来进行video参数的选择
- 94 switch(lcd_id){
- 95 case 0x4342 : vd_mode = VMODE_480x272; break;
- 96 case 0x7084 : vd_mode = VMODE_800x480; break;
- 97 case 0x7016 : vd_mode = VMODE_1024x600; break;
- 98 case 0x1018 : vd_mode = VMODE_1280x800; break;
- 99 default : vd_mode = VMODE_800x480; break;
- 100 }
- 101
- 102 //配置VDMA
- 103 run_vdma_frame_buffer(&vdma, VDMA_ID, vd_mode.width, vd_mode.height,
- 104 frame_buffer_addr,0,0,BOTH);
- 105 //初始化Display controller
- 106 DisplayInitialize(&dispCtrl, DISP_VTC_ID, DYNCLK_BASEADDR);
- 107 //设置VideoMode
- 108 DisplaySetMode(&dispCtrl, &vd_mode);
- 109 DisplayStart(&dispCtrl);
- 110
- 111 return 0;
- 112 }
main()函数的整体流程架构与“OV7725摄像头LCD显示实验”基本类似。但是,由于ov5640不像ov7725那样只能输出固定分辨率的视频图像,ov5640能够对输出图像的分辨率大小进行调整,所以我们可以根据具有不同分辨率的LCD来使ov5640输出不同分辨率的视频图像。代码中的第39-42行定义了4个变量,用于存储ov5640的分辨率配置参数。代码中第50行的switch语句,根据读取到的LCD的ID号来对ov5640的分辨率配置参数进行赋值,然后这些变量值将作为实参来传递给ov5640_init()函数。
ov5640的初始化过程完成之后,就是对VDMA、VTC以及动态时钟配置的初始化,如代码中的第93-114 行所述。
ov5640_init()函数的代码如下所示:
- 1 u8 ov5640_init( u16 cmos_h_pixel, u16 cmos_v_pixel,
- 2 u16 total_h_pixel, u16 total_v_pixel )
- 3 {
- 4 u16 cam_id = 0;
- 5
- 6 usleep(20000); //OV5640上电到开始配置sccb至少等待20ms
- 7
- 8 //读OV5640摄像头ID
- 9 cam_id = sccb_read_reg16(0x300b); //LSB 0x40
- 10 cam_id |= sccb_read_reg16(0x300a) << 8; //MSB 0x56
- 11
- 12 if(cam_id != 0x5640) //获取到正确的OV5640 ID
- 13 return 1;
- 14 else{
- 15 //先对寄存器进行软件复位,使寄存器恢复初始值
- 16 //寄存器软件复位后,需要延时1ms才能配置其它寄存器
- 17 sccb_write_reg16(0x3008,0x82); //Bit[7]:复位 Bit[6]:电源休眠
- 18
- 19 //延时1ms
- 20 usleep(1000);
- 21
- 22 sccb_write_reg16(0x3008,0x02); //正常工作模式
- 配置代码较长,省略部分源代码……
- 250 //设置输出像素个数
- 251 sccb_write_reg16(0x3808, cmos_h_pixel >> 8 ); //DVP 输出水平像素点数高4位
- 252 sccb_write_reg16(0x3809, cmos_h_pixel & 0x00FF); //DVP输出水平像素点数低8位
- 253 sccb_write_reg16(0x380a, cmos_v_pixel >> 8 ); //DVP输出垂直像素点数高3位
- 254 sccb_write_reg16(0x380b, cmos_v_pixel & 0x00FF); //DVP输出垂直像素点数低8位
- 255 sccb_write_reg16(0x380c, total_h_pixel >> 8 ); //水平总像素大小高5位
- 256 sccb_write_reg16(0x380d, total_h_pixel & 0x00FF); //水平总像素大小低8位
- 257 sccb_write_reg16(0x380e, total_v_pixel >> 8 ); //垂直总像素大小高5位
- 258 sccb_write_reg16(0x380f, total_v_pixel & 0x00FF); //垂直总像素大小低8位
- 配置代码较长,省略部分源代码……
- 283 return 0;
- 284 }
- 285 }
代码中的第9-10行读取寄存器地址0x300B和0x300A来获取OV5640摄像头的ID,注意OV7725摄像头的ID为“0x5640”。如果获取的ID不等于0x5640,则返回1;否则开始对OV7725摄像头进行配置。第250-258行将在main函数中赋值的分辨率配置数据写入到0x5640中,配置完成后函数返回0。
23.5下载验证
编译完工程之后我们就可以开始下载程序了。将OV5640摄像头模块插在领航者Zynq开发板的“OLED/CAMERA”插座上,并将LCD的排线接头插入开发板上的LCD接线座。将下载器一端连电脑,另一端与开发板上的JTAG端口连接,连接电源线并打开电源开关。
在SDK软件下方的SDK Terminal窗口中点击右上角的加号设置并连接串口。然后下载本次实验硬件设计过程中所生成的BIT文件,来对PL进行配置。最后下载软件程序,下载完成后,在下方的SDK Terminal中可以看到应用程序打印的信息,如下图所示:
图 23.5.1 串口打印的信息
同时,RGB LCD液晶屏上显示出OV5640摄像头采集的图像,说明本次OV5640摄像头RGB LCD屏显示的实验在领航者ZYQN开发板上验证成功,如下图所示:
图 23.5.2 RGB LCD屏显示图像数据