Bootstrap

【正点原子FPGA连载】 第三十章双目OV5640摄像头LCD显示实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

1)实验平台:正点原子MPSoC开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=692450874670
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html

第三十章双目OV5640摄像头LCD显示实验

双目摄像头是在一个模组上集成了两个摄像头,实现了双通道的图像采集。双目摄像头一般应用于安防监控、立体视觉测距、三维重建等领域。本章我们将使用ZYNQ MPSoC开发板实现对双目OV5640摄像头的图像采集并通过LCD屏幕实时显示。
本章包括以下几个部分:
3030.1简介
30.2实验任务
30.3硬件设计
30.4软件设计
30.5下载验证

30.1简介

摄像头在日常生活中非常常见,一般分为单目摄像头、双目摄像头和多目摄像头。单目摄像头凭借着成本低和易用的特点,目前使用最为广泛;双目摄像头主要应用于单目摄像头无法胜任的场合,如三维坐标定位等;当然针对一些特殊的应用,目前市场上也出现了多目摄像头,以应对更加复杂的场景。
我们在前面的例程中实现了单目OV5640摄像头LCD屏的实时显示,本章我们在此基础上实现双目OV5640摄像头的实时显示,即两路摄像头的图像视频显示在LCD屏上,并叠加两个标记层的显示。Xilinx官方提供了专门用于视频混合显示和图层叠加的视频处理模块,即OSD(On Screen Display)IP核和Video Mixer IP核,由于新版本的开发环境已经不支持OSD IP核,所以我们使用Video Mixer IP核。
Video Mixer IP核为alpha混合和合成多个视频或图形层提供了灵活的视频处理方法,支持多达17层(一个主层和16个覆盖层),带有可选的logo层,使用来自内存或AXI-Stream流的视频输入组合。Video Mixer IP核可通过综合寄存器接口进行编程,以控制帧大小、背景颜色、层位置和AXI4 Lite接口。该IP核也支持中断操作。
Video Mixer IP核的特性和优势如下所示:
(1):支持(每像素)8 个视频/图形和标识层的 α 混合;
(2):可选标识(块 RAM (BRAM))层支持色彩透明;
(3):各层既可以是内存映射的 AXI4 接口,也可以是 AXI4-Stream;
(4):提供可编程背景色彩;
(5):提供可编程层位置及尺寸;
(6):可按 1 倍、2 倍或 4 倍提供层缩放;
(7):可选内建色彩空间转换;
(8):支持 RGB、YUV 444、YUV 422、 YUV 420;
(9):支持流媒体 8 位、10 位、12 位和 16 位每个色彩组件的输入输出;
(10):接口,内存接口上 8 位和 10 位每个色彩组件;
(11):支持的空间分辨率: 64 × 64 至 4,096 × 2,160;
(12):在所有支持的器件系列中,支持 4K 60 fps;
Video Mixer IP核寄存器包括顶层寄存器、CSC系数寄存器、图层寄存器和Logo层寄存器,这里我们介绍实验里用到的顶层寄存器和图层寄存器。
顶层寄存器包括下面这些:
“Control”寄存器:bit[7]~bit[0]共8bit控制位,包括控制传输开始、传输完成标志、空闲标志、ready标志、刷新事件挂起、刷新状态、自动重启模式。
“Global Interrupt Enable”:IP核中断使能寄存器,用来使能IP核内所有中断。
“IP Interrupt Enable”:该寄存器允许选择性的启动中断。
“IP Interrupt Status Register”:双重用途寄存器。当中断发生时,相应的中断源位被设置在此寄存器中。在读回模式下,可以确定中断源。在写回模式下,请求的中断源位被清除。
“Width”:该寄存器用来设置每一行的有效像素个数。
“Height”:该寄存器用来设置每一帧有多少行。
“Layer enable”:图层使能寄存器,bit[0]用来使能Master层,也就是最底层,其它图层在这一层上叠加,bit[1]bit[16]用来使能Layer1Layer16,bit[23]用来使能Logo层,也就是最顶层。
图层寄存器包括下面这些寄存器:
“Alpha”:设置透明度寄存器。
“Start X”:图层左上角的X位置。
“Start Y”:图层左上角的Y位置。
“Width”:图层的有效宽度(单位:像素)。
“Stride”:当图层视频数据为Memory类型时,该寄存器有效,表示一行当中像素的总字节数。
“Height”:图层的有效高度(单位:行)。
“Scale Factor”:图层缩放因子。
“Plane 1 Buffer”:对于Memory类型图层,用于指定平面1的缓冲地址。
“Plane 2 Buffer”:在半平面模式下有效,用于指定平面2缓冲地址。

30.2实验任务

本章的实验任务是通过调用Video Mixer IP核,在ZYNQ MPSoC开发板上实现双目OV5640摄像头LCD屏幕实时显示,并在LCD屏幕上显示标记层。

30.3硬件设计

我们的ZYNQ MPSoC开发板上有J1、J19两个扩展接口,本次实验使用的是J19扩展口(纽扣电池座附近),该接口可以用来连接双目OV5640摄像头、高速ADDA等模块,扩展接口原理图如下图所示:
在这里插入图片描述

图 31.3.1扩展接口原理图
ATK-Dual-OV5640是正点原子推出的一款双目OV5640摄像头,该模块通过2*20排母(2.54mm间距)同外部连接,连接时将双目摄像头的排母直接插在开发板上的扩展口即可,模块外观如下图所示:
在这里插入图片描述

图 31.3.2双目OV5640模块
由于双目OV5640的引脚较多,这里不再给出引脚分配列表,在硬件设计的最后给出了本次实验管脚分配的约束代码,大家在搭建工程时可以直接拷贝。
我们在前面的例程中实现了单目OV5640摄像头LCD接口的实时显示,本章我们在此基础上,增加了一路摄像头的图像采集与显示,然后使用视频混合IP核将这两路视频拼接到一起显示。Xilinx官方提供的Video Mixer IP核最大支17层外部视频的输入,本实验使用4路视频输入,包括两路摄像头采集的视频和两路标记层输入,标记层用来区分OV5640 1摄像头和OV5640 2摄像头。打开第二十七章OV5640摄像头LCD显示实验的工程,点击File->Project->Save as,将工程另存为dual_ov5640_lcd工程,然后将原ov5640_lcd工程下ip_repo文件夹复制到当前工程路径下,并将这个文件夹下的ip核添加到工程中,添加之前先将原来添加的ip核删除,具体方法参考《自定义IP核-呼吸灯实验》中图 6.3.44所示的步骤,这样我们的双目OV5640 LCD显示的工程初步搭建起来了。
在导航栏中点击“Open Block Design”,打开设计框图,将“Processor System Reset”、“AXI Interconnect”、“AXI SmartConnect”三个IP核删除,如下图所示:
在这里插入图片描述

图 31.3.3删除IP核
删除“AXI4-Stream to Video Out” IP核中video_in端口和“axi_vdma_0” IP核的连接。
然后分别添加“ov5640_capture_data”、“video in to AXI4-Stream”、“vdma”IP核。添加完成后,先将“ov5640_capture_data_1”IP核相应端口引出,并将后缀改为“_2,”,修改完成后如下图所示:

在这里插入图片描述

图 31.3.4引出端口并重命名
重新配置vdma_1 IP核的数据位宽为24。
配置v_vid_in_axi4s_1 IP核的Clock Mode为“Independent”模式。
接下来添加Video Mixer IP核。这里需要注意的是,直接添加完该IP核后,在后面的“Generate Output Product”步骤中会报“找不到Video Mixer IP”的错误,这是软件Bug导致的时间溢出问题。解决方法是,读者到“正点原子ZYNQ售后群”(QQ群)中下载《HLS时间溢出》文档,按照里面的步骤打上补丁即可。
Video Mixer IP核添加后,双击该IP核进入配置页面,如下图所示:
在这里插入图片描述

图 31.3.5 Video Mixer IP核配置页面
这里介绍几个需要配置的选项:
“Number of Overlay Layers”:覆盖层,用来叠加混合的视频层,这里设置为3;
“Maximum Number of Columns”:最大显示的列数,由于我们支持的屏幕的分辨率,列数目最大1280,所以这里设置为“1280”。
“Maximum Number of Rows”:最大显示行数,这里设置为“800”。
“Enable Global Alpha”:表格中这个选项用来设置Alpha值,即透明度,这里Layer1~Layer3全部使能。
“Enable Scaling”:表示使能缩放,这里全部勾选。
“Interface Type”:表格中这个选项用来设置某一图层视频的接口类型,由于Layer 1层视频源为摄像头2输出的Stream流,所以接口类型要设置为“Stream”。Layer 2和Layer 3视频源是从DDR3输出的,所以接口类型保持默认的“Memory”就行。
“Video Format”设置视频数据的格式,这里我们将Layer2和Layer3层的视频数据格式设置为“RGB8”,设置完成后如下图所示:
在这里插入图片描述

然后我们将“Zynq UltraScale+ MPSoC”IP 核emio位宽设置为“4”。
接下来点击“Run Connection Automation”进行自动连线。完成后,发现还需要对“ov5640_capture_data_1”、“v_vid_in_axi4s_1”、“axi_vdma_1”、 “axi_vdma_0”、 “v_mix_0”、 “v_axis_vid_out_0” IP核进行手动连线。连线完成后的整体框图如下所示:
在这里插入图片描述

图 31.3.6整体框图
然后保存并运行“Generate Output Product”。
添加引脚约束文件。由于lcd引脚约束和前面的实验相同,这里只列出双目摄像头的引脚约束:

#----------------------摄像头接口的时钟---------------------------
#72M
create_clock -period 13.888 -name cam_pclk [get_ports cam_pclk]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets cam_pclk_IBUF]

create_clock -period 13.888 -name cam_pclk_2 [get_ports cam_pclk_2]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets cam_pclk_2_IBUF]

set_property -dict {PACKAGE_PIN C13 IOSTANDARD LVCMOS33} [get_ports cam_pclk]
set_property -dict {PACKAGE_PIN F13 IOSTANDARD LVCMOS33} [get_ports cam_rst_n]
set_property -dict {PACKAGE_PIN B15 IOSTANDARD LVCMOS33} [get_ports cam_pwdn]
set_property -dict {PACKAGE_PIN E15 IOSTANDARD LVCMOS33} [get_ports {cam_data[0]}]
set_property -dict {PACKAGE_PIN D15 IOSTANDARD LVCMOS33} [get_ports {cam_data[1]}]
set_property -dict {PACKAGE_PIN E14 IOSTANDARD LVCMOS33} [get_ports {cam_data[2]}]
set_property -dict {PACKAGE_PIN D14 IOSTANDARD LVCMOS33} [get_ports {cam_data[3]}]
set_property -dict {PACKAGE_PIN E13 IOSTANDARD LVCMOS33} [get_ports {cam_data[4]}]
set_property -dict {PACKAGE_PIN B13 IOSTANDARD LVCMOS33} [get_ports {cam_data[5]}]
set_property -dict {PACKAGE_PIN C14 IOSTANDARD LVCMOS33} [get_ports {cam_data[6]}]
set_property -dict {PACKAGE_PIN A13 IOSTANDARD LVCMOS33} [get_ports {cam_data[7]}]
set_property -dict {PACKAGE_PIN G14 IOSTANDARD LVCMOS33} [get_ports cam_vsync]
set_property -dict {PACKAGE_PIN G13 IOSTANDARD LVCMOS33} [get_ports cam_href]
set_property -dict {PACKAGE_PIN H13 IOSTANDARD LVCMOS33} [get_ports {emio_sccb_tri_io[0]}]
set_property -dict {PACKAGE_PIN F15 IOSTANDARD LVCMOS33} [get_ports {emio_sccb_tri_io[1]}]
set_property PULLUP true [get_ports {emio_sccb_tri_io[0]}]
set_property PULLUP true [get_ports {emio_sccb_tri_io[1]}]

set_property -dict {PACKAGE_PIN D11 IOSTANDARD LVCMOS33} [get_ports cam_pclk_2]
set_property -dict {PACKAGE_PIN A11 IOSTANDARD LVCMOS33} [get_ports cam_rst_n_2]
set_property -dict {PACKAGE_PIN B14 IOSTANDARD LVCMOS33} [get_ports cam_pwdn_2]
set_property -dict {PACKAGE_PIN C12 IOSTANDARD LVCMOS33 } [get_ports {cam_data_2[0]}]
set_property -dict {PACKAGE_PIN C11 IOSTANDARD LVCMOS33 } [get_ports {cam_data_2[1]}]
set_property -dict {PACKAGE_PIN B11 IOSTANDARD LVCMOS33 } [get_ports {cam_data_2[2]}]
set_property -dict {PACKAGE_PIN B10 IOSTANDARD LVCMOS33 } [get_ports {cam_data_2[3]}]
set_property -dict {PACKAGE_PIN A10 IOSTANDARD LVCMOS33 } [get_ports {cam_data_2[4]}]
set_property -dict {PACKAGE_PIN E10 IOSTANDARD LVCMOS33 } [get_ports {cam_data_2[5]}]
set_property -dict {PACKAGE_PIN E12 IOSTANDARD LVCMOS33 } [get_ports {cam_data_2[6]}]
set_property -dict {PACKAGE_PIN D10 IOSTANDARD LVCMOS33 } [get_ports {cam_data_2[7]}]
set_property -dict {PACKAGE_PIN A15 IOSTANDARD LVCMOS33} [get_ports cam_vsync_2]
set_property -dict {PACKAGE_PIN A12 IOSTANDARD LVCMOS33} [get_ports cam_href_2]
set_property -dict {PACKAGE_PIN A14 IOSTANDARD LVCMOS33} [get_ports {emio_sccb_tri_io[2]}]
set_property -dict {PACKAGE_PIN D12 IOSTANDARD LVCMOS33} [get_ports {emio_sccb_tri_io[3]}]
set_property PULLUP true [get_ports {emio_sccb_tri_io[2]}]
set_property PULLUP true [get_ports {emio_sccb_tri_io[3]}]

接下来生成bitstream,新建vitis文件夹,并导出xsa文件到vitis文件夹中。
到这里,硬件设计就完成了。

30.4软件设计

打开vitis软件后,将路径指定到上一小节创建的vitis文件夹,然后按照《第一章Hello World实验》创建应用工程。创建完成后,添加如下图所示的文件,读者可以直接从例程中复制这些文件到自己的工程中。
在这里插入图片描述

图 31.4.1添加工程文件
下面我们来分析一下这些文件。首先看一下main.c文件,如下所示:

……

17  //宏定义
18  #define CLK_WIZ_ID         XPAR_CLK_WIZ_0_DEVICE_ID   //时钟IP核器件ID
19  #define DISP_VTC_ID        XPAR_VTC_0_DEVICE_ID       //VTC器件ID
20  #define AXI_GPIO_0_ID      XPAR_AXI_GPIO_0_DEVICE_ID  //PL端  AXI GPIO 0(lcd_id)器件ID
21  #define AXI_GPIO_0_CHANEL  1                          //使用AXI GPIO(lcd_id)通道1
22  
23  unsigned int vdma_id[DISPLAY_VDMA_NUM] = {XPAR_AXIVDMA_0_DEVICE_ID,
24                                            XPAR_AXIVDMA_1_DEVICE_ID};
25  
26  XAxiVdma     vdma[DISPLAY_VDMA_NUM];
27  
28  //全局变量
29  DisplayCtrl  dispCtrl;
30  XGpio        axi_gpio_inst;   //PL端 AXI GPIO 驱动实例
31  VideoMode    vd_mode;
32  
33  unsigned int frame_buffer_addr[DISPLAY_VDMA_NUM];  //frame buffer的起始地址
34  unsigned int lcd_id=0;                             //LCD ID
35  
36  XV_Mix_l2  mix;
37  XVidC_VideoStream VidStream;
38  
39  extern const unsigned char gImage_ov5640_1[88560];
40  extern const unsigned char gImage_ov5640_2[88560];
41  extern const unsigned char gImage_ov5640_1_4342[27000];
42  extern const unsigned char gImage_ov5640_2_4342[27000];
43  
44  int main(void)
45  {
46      u8 *ov56401_point;
47      u8 *ov56402_point;
48      u32 status0,status1;
49      u16 cmos_h_pixel;   //ov5640 DVP 输出水平像素点数
50      u16 cmos_v_pixel;   //ov5640 DVP 输出垂直像素点数
51      u16 total_h_pixel;  //ov5640 水平总像素大小
52      u16 total_v_pixel;  //ov5640 垂直总像素大小
54      
55      XGpio_Initialize(&axi_gpio_inst,AXI_GPIO_0_ID);                //GPIO初始化
56      XGpio_SetDataDirection(&axi_gpio_inst,AXI_GPIO_0_CHANEL,0x07); //设置AXI GPIO为输入
57      lcd_id = LTDC_PanelID_Read(&axi_gpio_inst,AXI_GPIO_0_CHANEL);  //获取LCD的ID
58      XGpio_SetDataDirection(&axi_gpio_inst,AXI_GPIO_0_CHANEL,0x00); //设置AXI GPIO为输出
59      xil_printf("LCD ID: %x\r\n",lcd_id);
60  

……

102     status0 = ov5640_init(CMOS_CH0,cmos_h_pixel,//初始化ov5640 0
103                               cmos_v_pixel,
104                              total_h_pixel,
105                              total_v_pixel);
106     status1 = ov5640_init(CMOS_CH1,cmos_h_pixel,//初始化ov5640 1
107                               cmos_v_pixel,
108                              total_h_pixel,
109                              total_v_pixel);
110     if(status0 == 0 && status1 == 0)
111         xil_printf("Dual OV5640 detected successful!\r\n");
112     else
113         xil_printf("Dual OV5640 detected failed!\r\n");
114 
115     //根据获取的LCD的ID号来进行video参数的选择
116     switch(lcd_id){
117         case 0x4342 : vd_mode = VMODE_480x272; break;  //4.3寸屏,480*272分辨率
118         case 0x4384 : vd_mode = VMODE_800x480; break;  //4.3寸屏,800*480分辨率
119         case 0x7084 : vd_mode = VMODE_800x480; break;  //7寸屏,800*480分辨率
120         case 0x7016 : vd_mode = VMODE_1024x600; break; //7寸屏,1024*600分辨率
121         case 0x1018 : vd_mode = VMODE_1280x800; break; //10.1寸屏,1280*800分辨率
122         default : vd_mode = VMODE_800x480; break;
123     }
124 
125     xil_printf("vd_mode.width  is %d\r\n",vd_mode.width);
126     xil_printf("vd_mode.height is %d\r\n",vd_mode.height);
127 
128     //配置VDMA
129     for(u8 i=0;i<DISPLAY_VDMA_NUM;i++){
130             frame_buffer_addr[i] = XPAR_PSU_DDR_0_S_AXI_BASEADDR+0x1000000*i+0x05000000;
131             //配置VDMA
132             run_vdma_frame_buffer(&vdma[i], vdma_id[i], vd_mode.width, vd_mode.height,
133                                     frame_buffer_addr[i],0,0,BOTH);
134         }
135 
136     xil_printf("VDMA Config succesfull\r\n");
137 
138     //设置clk_wiz时钟IP核输出的时钟频率
139     clk_wiz_cfg(CLK_WIZ_ID,vd_mode.freq);
140 
141     //初始化Display controller
142     DisplayInitialize(&dispCtrl, DISP_VTC_ID);
143     //设置VideoMode
144     DisplaySetMode(&dispCtrl, &vd_mode);
145     DisplayStart(&dispCtrl);
146 
147     XV_mix              xv_mix;
148     XV_mix_Config       *xv_config;
149 
150     xv_config = XV_mix_LookupConfig(XPAR_XV_MIX_0_DEVICE_ID);
151     XV_mix_CfgInitialize(&xv_mix,xv_config,xv_config->BaseAddress);
152 
153     XV_mix_Set_HwReg_layerEnable(&xv_mix,(u32)0x0f);
154     XV_mix_Set_HwReg_width(&xv_mix, vd_mode.width);
155     XV_mix_Set_HwReg_height(&xv_mix, vd_mode.height);
156 
157     XV_mix_Set_HwReg_layerAlpha_1(&xv_mix, 255);
158     XV_mix_Set_HwReg_layerStartX_1(&xv_mix,((vd_mode.width)/2));
159     XV_mix_Set_HwReg_layerStartY_1(&xv_mix,(u32)0);
160     XV_mix_Set_HwReg_layerWidth_1(&xv_mix,((vd_mode.width)/2));
161     XV_mix_Set_HwReg_layerHeight_1(&xv_mix,vd_mode.height);
162 
163     if(lcd_id == 0x4342){
164         ov56401_point =gImage_ov5640_1_4342;
165         ov56402_point =gImage_ov5640_2_4342;
166 
167         XV_mix_Set_HwReg_layerAlpha_2(&xv_mix, 125);
168         XV_mix_Set_HwReg_layerStartX_2(&xv_mix,(u32)((vd_mode.width)/2 + 20));
169         XV_mix_Set_HwReg_layerStartY_2(&xv_mix,(u32)20);
170         XV_mix_Set_HwReg_layerWidth_2(&xv_mix,(u32)200);
171         XV_mix_Set_HwReg_layerHeight_2(&xv_mix,(u32)45);
172         XV_mix_Set_HwReg_layerStride_2(&xv_mix, 200*3);
173         XV_mix_Set_HwReg_layer2_buf1_V(&xv_mix, ov56401_point);
174 
175         XV_mix_Set_HwReg_layerAlpha_3(&xv_mix, 125);
176         XV_mix_Set_HwReg_layerStartX_3(&xv_mix,(u32)20);
177         XV_mix_Set_HwReg_layerStartY_3(&xv_mix,(u32)20);
178         XV_mix_Set_HwReg_layerWidth_3(&xv_mix,(u32)200);
179         XV_mix_Set_HwReg_layerHeight_3(&xv_mix,(u32)45);
180         XV_mix_Set_HwReg_layerStride_3(&xv_mix, 200*3);
181         XV_mix_Set_HwReg_layer3_buf1_V(&xv_mix, ov56402_point);
182     }
183     else{
184         ov56401_point =gImage_ov5640_1;
185         ov56402_point =gImage_ov5640_2;
186 
187         XV_mix_Set_HwReg_layerAlpha_2(&xv_mix, 125);
188         XV_mix_Set_HwReg_layerStartX_2(&xv_mix,(u32)((vd_mode.width)/2 + 50));
189         XV_mix_Set_HwReg_layerStartY_2(&xv_mix,(u32)50);
190         XV_mix_Set_HwReg_layerWidth_2(&xv_mix,(u32)328);
191         XV_mix_Set_HwReg_layerHeight_2(&xv_mix,(u32)90);
192         XV_mix_Set_HwReg_layerStride_2(&xv_mix, 328*3);
193         XV_mix_Set_HwReg_layer2_buf1_V(&xv_mix, ov56401_point);
194 
195         XV_mix_Set_HwReg_layerAlpha_3(&xv_mix, 125);
196         XV_mix_Set_HwReg_layerStartX_3(&xv_mix,(u32)50);
197         XV_mix_Set_HwReg_layerStartY_3(&xv_mix,(u32)50);
198         XV_mix_Set_HwReg_layerWidth_3(&xv_mix,(u32)328);
199         XV_mix_Set_HwReg_layerHeight_3(&xv_mix,(u32)90);
200         XV_mix_Set_HwReg_layerStride_3(&xv_mix, 328*3);
201         XV_mix_Set_HwReg_layer3_buf1_V(&xv_mix, ov56402_point);
202     }
203 
204     XV_mix_EnableAutoRestart(&xv_mix);
205     XV_mix_Start(&xv_mix);
206 
207     return 0;
208 }

第39到第42行,定义了四个字符型数组,其中gImage_ov5640_1和gImage_ov5640_2两个数组用来存储标记层图像所对应的字符,标记层图像如下图所示:
在这里插入图片描述

图 31.4.2标记层图像
上面的图像是笔者先在PPT中分别输入两行“OV5640 1”和“OV5640 2”文字,然后使用截图工具(QQ截图)截取328250像素大小的图片,保存在windows自带的画图工具中,然后使用画图工具自带的裁剪功能,分别将“OV5640 1”、“OV5640 2”裁剪出来,两个图片裁剪的大小都是32890像素,然后分别另存为名为“ov5640_1.bmp”、“ov5640_2.bmp”的图片。注意,图片格式必须为bmp格式的,否则将不能调节透明度。
接下来,我们使用正点原子提供的“资料盘/软件/ Image2Lcd”路径下的“Ima2Lcd.exe”工具,将前面生成的两个图片转换成C语言数组,如下图所示:
在这里插入图片描述

图 31.4.3 Ima2Lcd工具
打开软件如下图所示,点击下图中序号1处,将前面生成“ov5640_1.bmp”导入进来,序号2处显示图片的大小,接下来在序号3处设置将图片输出到c语言数组后的宽度和高度,这里设置为328*90像素,然后点击序号4处的“保存”按钮,点击后弹出一个窗口,我们在里面将数组命名为“ov5640_1.c”,生成的文件如图 31.4.5所示,注意数组名为“gImage_ ov5640_1”。同样的方法生成“gImage_ ov5640_2”数组。
在这里插入图片描述

图 31.4.4将图像导出到数组
在这里插入图片描述

图 31.4.5 ov5640_1数组文件
第102行到第109行,调用ov5640_init()函数初始化双目OV5640摄像头,ov5640_init()函数通过调用sccb_write_reg16()函数实现IIC协议,从而配置OV5640摄像头寄存器。sccb_write_reg16()函数调用Xilinx的XGpioPs_WritePin()函数对EMIO进行读写,从而实现IIC接口时序。
第129行到第134行对vdma进行配置,注意第130行frame_buffer_addr的地址加0x05000000,是为了避免frame_buffer_addr地址和程序中变量存储地址冲突。
第150和第151行,根据video mixer器件id查找配置信息并对video mixe初始化。
第153行,调用XV_mix_Set_HwReg_layerEnable()函数设置Layer enable寄存器,参数“0x0f表示”将Master layer、Overlay Layer1、Overlay Layer2、Overlay Layer3四个图层的使能开关打开。第154行和第155行用来设置背景层的有效宽度和有效高度。
第157行到第161行用来设置Overlay Layer1图层的相关参数,也就是显示摄像头1拍摄的画面。其中XV_mix_Set_HwReg_layerStartX_1()和XV_mix_Set_HwReg_layerStartY_1()两个函数用来设置图层在屏幕上的X轴和Y轴起点,其中X轴参数为“(vd_mode.width)/2”表示X轴方向从屏幕中间开始,Y轴参数“0”表示Y轴方向起点0处开始,从上往下显示。XV_mix_Set_HwReg_layerAlpha_1()函数用来设置透明度的值,“255”表示完全不透明,“0”表示完全透明。
第163行到第202行用来设置Layer2层和Layer3层,由于分辨率为480272的4.3寸屏幕太小,用在其它分辨率屏幕上的标记层图像不能用在分辨率为480272的屏幕上,所以第163行根据480272屏幕id单独设置显示在该屏幕上的标记层图像。不同分辨率屏幕上Layer2和Layer3图层的配置方法都是一样的,这里我们以480272分辨率屏幕为例讲解。
前面讲解使用Ima2Lcd工具将两个32890像素大小的图片生成两个字符数组,而第164行和第165行gImage_ov5640_1_4342和gImage_ov5640_2_4342两个数组是使用20045像素大小的图片生成的,方法同前面讲解的一样。第167行将Layer2图层的透明度设置为125,第168行和第169行设置Layer2层的起点,即水平方向在屏幕的中间位置再右移20个像素,垂直方向从屏幕显示区域最上方往下移20个像素。第170行和第171行设置Layer图层的大小,即20045个像素。第172行XV_mix_Set_HwReg_layerStride_2函数用来设置一行像素数据的总个数,由于Layer2层一行有200个像素,每个像素3个字节数据,所以总共有2003个字节数据。173行调用XV_mix_Set_HwReg_layer2_buf1_V()函数将显示图像数据的数组写入Layer2图层的帧buffer中。第175行到第181行用来配置Layer3图层,方法和配置Layer2图层一样,不同在于Layer3图层的起始位置不同,Layer3图层在水平方向上,从左到右从第20个像素开始,在垂直方向上,从上到下从第20个像素开始。
第204行和第205行的XV_mix_EnableAutoRestart()函数和XV_mix_Start()函数用来配置Video Mixer IP核的“Control”寄存器,从而让Video Mixer IP核开始传输视频。

30.5下载验证

首先我们将下载器与开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用USB连接线将USB UART接口(PS_PORT)与电脑连接,用于串口通信。接下来使用FPC排线一端与RGB LCD液晶屏上的接口连接,另一端连接开发板上的RGB LCD接口。
接下来将双目OV5640摄像头模块插在MPSOC开发板的J19扩展口,注意在连接时,摄像头镜头方向朝外,如下图所示,然后将开发板上的启动模式设置为JTAG模式,最后连接开发板的电源。
在这里插入图片描述

图 31.5.1开发板连接摄像头
打开vitis软件,连接串口并下载程序。程序下载完成后,可以看到LCD屏幕显示如下图所示:
在这里插入图片描述

图 31.5.2实验结果

;