【正点原子FPGA连载】 第六章OV5640摄像头灰度显示实验--摘自【正点原子】领航者ZYNQ之HLS 开发指南
本帖最后由 正点原子 于 2020-9-26 12:21 编辑1)实验平台:正点原子领航者ZYNQ开发板
2)购买链接:https://item.taobao.com/item.htm?id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html
4) 正点原子官方B站:https://space.bilibili.com/394620890
5)对正点原子FPGA感兴趣的同学可以加群讨论:876744900 点击加入:
6)关注正点原子公众号,获取最新资料
第六章OV5640摄像头灰度显示实验
OpenCV是计算机视觉领域中常用的图像处理的库,在很多场景下都有着广范的应用,比如安防系统中的人脸识别或者自动驾驶系统中的道路识别等。OpenCV中的函数通常运行在计算机中,也可以用在基于嵌入式设备的计算机视频应用中。除此之外,OpenCV也可以移植到Vivado HLS中,得到可综合的C++代码。本章我们将学习如何在Vivado HLS中使用OpenCV作图像处理。
本章包括以下几个部分:
66.1简介
6.2实验任务
6.3HLS设计
6.4IP验证
6.5下载验证
6.1简介
Vivado HLS中包含了一系列的C库(包括C和C++),方便对一些常用的硬件结构或功能使用C/C++进行建模,并且能够综合成RTL。在Vivado HLS中提供的C库有下面几种类型:
1、任意精度数据类型库
2、HLS Stream库
3、HLS数学库
4、HLS视频库
5、HLS IP库
6、HLS线性代数库
其中第一个“任意精度数据类型库”在《正点原子HLS开发指南》的第一章《LED闪烁实验》中就已经用到过。本章将带大家学习如何使用HLS视频库中的函数来作图像处理。
需要注意的是,HLS视频库与我们通常所说的OpenCV库并不完全相同,尽管其中很多视频的概念以及函数的命名方式都是相似的。由于OpenCV函数是不能直接综合的,因此必须由可综合的库函数进行替换。最主要的原因是因为OpenCV函数涉及到动态内存分配,比如在OpenCV中构造一个任意尺寸的cv::Mat对象时就会涉及,而这种函数是不可综合的。因此我们在使用HLS视频库作图像处理时,既要考虑视频处理函数与OpenCV函数的相似性,又要考虑在综合过程中HLS视频库与OpenCV库的差异。
下面首先给出传统的OpenCV与HLS视频库在图像处理流程上的差异,如下图所示:
图 6.1.1 OpenCV与HLS视频库的图像处理流程
图 6.1.1左边是传统的OpenCV在作图像处理时的流程图,其中算法可以完全通过调用OpenCV库中的函数来实现,包括图像的输入输出以及图像的处理;在中间的流程图中,图像的输入和输出仍然是通过调用OpenCV中的函数来实现,但是在图像处理过程中,原先调用的OpenCV函数已经被Vivado HLS视频库中所对应的可综合的函数所替换;在最右边的流程图中,使用可综合的HLS视频库所实现的图像处理模块已经被综合成FPGA中的硬件模块,可以实时地处理FPGA中的视频流。
Vivado HLS所综合出来的视频处理模块同样可以部署在ZYNQ器件的PL端,其常用的架构如下图所示:
图 6.1.2 ZYNQ图像处理系统中的直接数据流架构
图 6.1.2中红色圆圈所标示的就是通过Vivado HLS所综合出来的视频处理模块,位于ZYNQ的PL端(图中深灰色区域)。该视频处理模块可以实时处理前端模块输入的视频流,并输出到后端模块进行显示。另外ZYNQ的PS端(图中浅灰色区域)还可以通过AXI接口对生成的视频处理模块进行配置。
由于上面的架构中,视频处理过程不需要通过外部存储器(如DDR3)对图像进行帧缓存,而是直接对视频流进行处理,因此被称之为直接数据流架构(Direct Streaming Architecture)。该架构也是我们本章实验最终所要实现的图像处理架构。
6.2实验任务
本节的实验任务是使用Vivado HLS实现一个图像处理的IP核,该IP核能够将OV5640摄像头产生的RGB彩色图像转换成灰度图像,并在Vivado中对IP核进行验证。
6.3HLS设计
我们在电脑中的“F:\ZYNQ\High_Level_Synthesis”目录下新建一个名为ov5640_rgb2gray的文件夹,作为本次实验的工程目录。然后打开Vivado HLS工具,创建一个新的工程。设置工程名为“ov5640_rgb2gray”,选择工程路径为刚刚创建的文件夹。需要注意的是,工程名以及路径只能由英文字母、数字和下划线组成,不能包含中文、空格以及其他特殊字符。如下图所示:
图 6.3.1 工程配置界面
设置好工程名及路径之后,点击“Next”,进入如下界面设置顶层函数:
图 6.3.2 设置顶层函数
然后选择ZYNQ器件所对应的型号,如下图所示:
图 6.3.3 设置时钟周期和器件型号
工程创建完成后,在工程面板中的“source”目录上点击右键,然后在打开的列表中选择“New File”新建源文件,在弹出的对话框中输入源文件的名称“ov5640_rgb2gray.cpp”,如下图所示。源文件默认的保存路径为HLS工程目录,为方便源文件的管理,我们在工程目录下新建一个名为“src”的文件下,将源文件保存在src目录下。
图 6.3.4 输入源文件名
我们输入的源文件的后缀名为“.cpp”,即使用C++语言进行设计。这是因为我们要使用Vivado HLS视频库,那么就只能使用C++来进行设计。
“ov5640_rgb2gray.cpp”文件源代码如下:
1#include "hls_video.h"
2
3#define MAX_HEIGHT 800 //图像最大高度
4#define MAX_WIDTH1024 //图像最大宽度
5
6typedef hls::stream<ap_axiu<24,1,1,1> >AXI_STREAM;
7typedef hls::Mat<MAX_HEIGHT,MAX_WIDTH,HLS_8UC3> RGB_IMAGE;
8typedef hls::Mat<MAX_HEIGHT,MAX_WIDTH,HLS_8UC1> GRAY_IMAGE;
9
10 void ov5640_rgb2gray(AXI_STREAM & INPUT_STREAM,
11 AXI_STREAM & OUTPUT_STREAM,
12 int rows,
13 int cols
14 ){
15
16 #pragma HLS INTERFACE axis port=INPUT_STREAM
17 #pragma HLS INTERFACE axis port=OUTPUT_STREAM
18 #pragma HLS INTERFACE s_axilite port=rows
19 #pragma HLS INTERFACE s_axilite port=cols
20 #pragma HLS INTERFACE ap_ctrl_none port=return
21 #pragma HLS dataflow
22
23//hls::mat格式变量
24RGB_IMAGEimg_0(rows,cols);
25GRAY_IMAGE img_1(rows,cols);
26RGB_IMAGEimg_2(rows,cols);
27
28//将AXI4 Stream数据转换成hls::mat格式
29hls::AXIvideo2Mat(INPUT_STREAM,img_0);
30
31//将RGB888格式的彩色数据转换成灰度数据
32hls::CvtColor<HLS_RGB2GRAY,HLS_8UC3,HLS_8UC1>(img_0,img_1);
33
34//将灰度数据转换成三个通道的灰度图像
35hls::CvtColor<HLS_GRAY2RGB,HLS_8UC1,HLS_8UC3>(img_1,img_2);
36
37//将hls::mat格式数据转换成AXI4 Stream格式
38hls::Mat2AXIvideo(img_2,OUTPUT_STREAM);
39 }
在代码的第1行,包含了一个名为“hls_video.h”的头文件,这个头文件里包含了Vivado HLS提供的所有图像和视频处理相关的视频数据类型和函数。我们使用Vivado HLS视频库就要引用这个头文件。
在代码的第6行到第8行,我们使用typedef对三个类(class)进行了宏定义,相当于是给这三个类分别起了一个更简单的名字,方便在后面的代码中用这个名字来创建相应的对象(object)。
Vivado HLS中提供了一个C++模板类(template class)用于对视频图像处理中常用的流(stream)数据进行建模,即代码第6行的类hls::stream< >。前面的hls::表示它的作用域,即用于指定命名空间;而尖括号内部则用于指定stream的数据类型,这里我们指定的类型为ap_axiu<24,1,1,1>,它是Vivado HLS提供的一个结构体类型,其定义如下所示:
这个结构体ap_axiu用于综合得到带侧通道的Axi4-Stream接口,我们在《HLS彩条显示实验》中用到了该类型的接口,如果对该接口不熟悉的话,请参考相应的章节。这里我们需要注意模板中第一个参数的值24,它表示在Axi4-Stream接口中数据的位宽。OV5640摄像头采集到的像素数据格式为RGB888,因此数据的位宽为24位。
在代码的第7行和第8行使用了Vivado HLS中的另外一个模板类hls::Mat< >,用于表示HLS视频库中的图像,HLS视频处理函数都是针对该类型的图像数据进行处理的。需要特别注意的是,虽然hls::Mat<>用于对图像进行建模,但是它并不是一个位于外部存储器中的数组,其内部是像素数据的数据流,使用的是hls::stream<>数据类型。
在hls::Mat<>模板中,前两个参数参数MAX_HEIGHT和MAX_WIDTH用于指定处理图像的最大分辨率,而第三个参数HLS_8UC3(或HLS_8UC1)用于指定像素数据的类型。对于RGB888格式的彩色图像,它的数据类型为HLS_8UC3,即一个像素点是由3个8位无符号Char型数据表示。而灰度图像的数据类型为HLS_8UC1,即一个像素点的灰度值只需要由1个8位无符号Char型数据表示。
代码的第10行至第14行定义了顶层函数ov5640_rgb2gray,它有四个参数。其中INPUT_STREAM和OUTPUT_STREAM表示输入的视频图像和图像处理之后输出的视频图像,输入输出图像都是以stream的格式传递的。参数rows和cols则分别指定了图像处理的行数和列数。
代码的第16至21行是为设计所添加的编译指令。其中16和17行表示将输入和输出视频图像综合成Axi4-Stream接口;18和19行表示图像的行数和列数由Axi4-Lite接口进行配置;第20行表示没有添加包级别的控制协议。前5条编译指令在前面的例程中都使用过。
第21行的编译指令#pragma HLS dataflow在本设计中用于对视频处理函数进行优化。为了得到高质量和高性能的实现结果,HLS视频库中的视频处理函数是提前优化好的,而添加“DATAFLOW”优化指令是为了确保设计中的各个视频处理函数能够并行执行。
由于Vivado HLS视频处理函数都是针对hls::Mat<>格式的图像进行处理,因此在进行图像处理之前,需要先将调用函数hls::AXIvideo2Mat()将Axi4-Stream格式的输入图像转换成hls::Mat<>格式,如代码第29行所示。在完成图像处理过程之后,要再将hls::Mat<>格式转回Axi4-Stream格式,这一过程由函数hls::Mat2AXIvideo()实现,如代码第38行所示。这两个过程在HLS图像处理流程中有着明确的体现,如下图中两个红色的方框所示:
图 6.3.5 HLS图像处理流程
图 6.3.5中红色圆圈所标示的位置是调用HLS视频库中的函数进行图像处理的过程。本次实验中的图像处理包括代码中第32和35行两个过程,这两行代码都调用了HLS视频库中的函数hls::CvtColor。相关代码重新摘录如下所示:
31//将RGB888格式的彩色数据转换成灰度数据
32hls::CvtColor<HLS_RGB2GRAY,HLS_8UC3,HLS_8UC1>(img_0,img_1);
33
34//将灰度数据转换成三个通道的灰度图像
35hls::CvtColor<HLS_GRAY2RGB,HLS_8UC1,HLS_8UC3>(img_1,img_2);
函数hls::CvtColor可以在彩色图像和灰度图像之间进行转换。第32行代码中,函数模板第一个参数HLS_RGB2GRAY表示将RGB图像(img_0)转换成灰度图像(img_1)。其中img_0被称之为源图像,img_1被称之为目标图像,而函数模板中的第二个和第三个参数则分别指定了源图像和目标图像的像素数据格式。由于源图像为OV5640摄像头采集的RGB888格式的图像,因此第二个参数为HLS_8UC3;而函数hls::CvtColor通过HLS_RGB2GRAY转换类型转换得到的图像为8位数据表示的灰度值,因此第三个参数为HLS_8UC1。
由于转换得到的灰度图像需要在显示器或液晶显示屏上显示,因此需要将8位灰度数据表示的图像重新转换成显示器或液晶屏上能够显示的图像。因此我们在代码的第35行再次调用了函数hls::CvtColor,但是函数模板的第一个参数变成了HLS_GRAY2RGB,即由灰度图像转换成RGB图像。需要注意的是,该过程仅仅是将源图像的灰度数据赋值给了RGB三个通道,并不能将灰度图像还原成彩色图像。由于三个通道上的数据是一致的,因此显示器或液晶屏上显示出来的仍然是灰度图像。
由上面的过程分析我们可以知道,img_0和img_2的图像像素数据都包含了三个通道,而img_1的像素数据只包含了一个灰度通道。因此在代码的24行至26行,img_0和img_2的类型被定义成RGB_IMAGE,而img_1的类型被定义成GRAY_IMAGE。
代码输入完成后,按快捷键Ctrl+S保存。然后点击工具栏中向右的绿色三角形对设计进行综合。
综合完成后,会自动打开综合结果(solution)的报告,如下图所示:
图 6.3.6 综合报告
从图1.3.6中可以看出,目标时钟周期为20ns,综合后的设计所估计的时钟周期为16.517ns,满足时序要求。除此之外,在综合报告中还给出了设计的性能评估、资源评估以及接口等信息。本次实验中,我们重点关注综合工具为我们生成的接口信息,如下图所示:
图 6.3.7 接口信息
从图中可以看出,设计综合出了一个“AXI4-Lite”从接口和两个“AXI4-Stream”接口(一个输入,一个输出)。其中“AX4-Lite”总线接口用于控制视频处理的分辨率,而两个“AXI4-Stream”总线接口分别用于输入待处理的视频以及输出处理之后的视频流。
在综合结果正确的情况下,在工具栏中点击黄色的“田”字按钮,导出RTL。
在导出RTL结束之后,我们到工程目录所指向的文件夹中可以看到以ZIP压缩文件形式存在的IP核,如下图所示:
图 6.3.18 文件夹中的IP核
HLS设计结束之后,我们将在Vivado中对导出的IP核进行验证。
6.4IP验证
在IP验证环节,我们会使用Vivado工具的IP集成器将生成的IP核添加到Block Design中,然后完成设计后将程序下载到领航者开发板上进行验证。
用于IP验证的底层硬件可以在《领航者ZYNQ之嵌入式开发指南》第二十三章“OV5640 摄像头 LCD 显示”实验的基础上进行。打开该实验所对应的Vivado工程“ov5640_lcd”,将其另存为“ov5640_rgb2gray_ip_test”工程。为了方便工程管理,我们将Vivado工程的目录与HLS工程目录保持一致,如下图所示:
图 6.4.1 创建Vivado工程
Vivado工程保存完成之后,本次实验的工程目录如下图所示:
图 6.4.2 OV5640摄像头灰度显示实验工程目录
在通过“另存为”的方式保存工程之后,还要将原来工程中的IP库(名为ip_repo的文件夹)复制到新的Vivado工程目录下,即上图中的ov5640_rgb2gray_ip_test文件夹中。
然后将HLS设计过程中导出的IP核拷贝到“ip_repo”目录下并解压,解压完成后如下图所示:
图 6.4.3 拷贝并解压IP
最后在Vivado中重新将当前工程目录下的ip_repo文件夹添加到工程的IP库中。
接下来打开Vivado工程中的Block Design,将Video In to AXI4-Stream模块的video_out接口与VDMA模块的S_AXIS_S2MM接口之间的连线删除,即删除下图中橙色高亮的连线:
图 6.4.4删除Block Design中橙色高亮的连线
然后将HLS生成的IP核Ov5640_rgb2gray添加到Block Design中,并将其STREAM接口分别连接到Video In to AXI4-Stream模块的video_out接口与VDMA模块的S_AXIS_S2MM接口上。如下图所示:
图 6.4.5 插入灰度转换IP核
最后点击上图中左上角的“Run Connection Automation”,让工具自动连接该IP核的其他端口,包括时钟、复位以及AXI-Lite从接口,如下图所示:
图 6.4.6 自动连接灰度转换IP的其他接口
最后对设计重新进行布局,最终的设计如下图所示:
图 6.4.7 用于验证灰度转换IP核的最终设计
在图 6.4.7中,ov5640_capture_data模块获取OV5640摄像头采集的图像,然后通过Video In to AXI4-Stream模块将摄像头图像转换成AXI4-Stream格式的视频流。该视频流输入HLS生成的灰度转换IP核ov5640_rgb2gray,将RGB888格式的彩色图像转换成灰度图像,然后同样以AXI4-Stream格式将处理后的视频流输出给VDMA。
另外从图 6.4.7中两条蓝色高亮的数据线可以看出,ZYNQ PS端的M_AXI_GP0接口通过AXI 互联模块最终连接到IP核ov5640_rgb2gray的s_axi_AXILiteS接口。然后我们就可以在ZYNQ PS端,通过AXI-Lite接口对IP核ov5640_rgb2gray所处理的图像分辨率进行配置,包括行数和列数。
如果大家对设计中其他各模块的功能不了解的话,请大家参考《领航者ZYNQ之嵌入式开发指南》第二十三章“OV5640 摄像头 LCD 显示”实验。
到这里我们的Block Design就设计完成了,在Diagram窗口空白处右击,然后选择“Validate Design”验证设计。验证完成后弹出对话框提示“Validation Successful”表明设计无误,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。
接下来在Source窗口中右键点击Block Design设计文件“system.bd”,然后依次执行“Generate Output Products”和“Create HDL Wrapper”。
最后在左侧Flow Navigator导航栏中找到PROGRAM AND DEBUG,点击该选项中的“Generate Bitstream”,对设计进行综合、实现、并生成Bitstream文件。
在生成 Bitstream 之后,在菜单栏中选择 File > Export > Export hardware 导出硬件,并在弹出的对话框 中,勾选“Include bitstream”。然后在菜单栏选择 File > Launch SDK,启动 SDK 软件。
在Vivado SDK中新建空的应用工程,工程名为“ov5640_rgb2gray_lcd”。
然后找到《领航者ZYNQ之嵌入式开发指南》第二十三章“OV5640 摄像头 LCD 显示”实验的Vivado工程目录,将“21_ov7725_lcd\ov7725_lcd.sdk\ov7725_lcd”目录下的src文件夹拷贝到新建的应用工程目录下。
在SDK中刷新src目录,然后将“main.c”的代码修改为如下所示:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
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#include "xov5640_rgb2gray.h"
15
16//宏定义
17#define BYTES_PIXEL 3 //像素字节数,RGB888占3个字节
18#define FRAME_BUFFER_NUM 3 //帧缓存个数3
19#define DYNCLK_BASEADDR XPAR_AXI_DYNCLK_0_BASEADDR//动态时钟基地址
20#define VDMA_ID XPAR_AXIVDMA_0_DEVICE_ID //VDMA器件ID
21#define DISP_VTC_ID XPAR_VTC_0_DEVICE_ID //VTC器件ID
22//PL端AXI GPIO 0(lcd_id)器件 ID
23#define AXI_GPIO_0_ID XPAR_AXI_GPIO_0_DEVICE_ID
24//使用AXI GPIO(lcd_id)通道1
25#define AXI_GPIO_0_CHANEL1
26
27//全局变量
28//frame buffer的起始地址
29unsigned int const frame_buffer_addr = (XPAR_PS7_DDR_0_S_AXI_BASEADDR
30 + 0x1000000);
31XAxiVdma vdma;
32DisplayCtrl dispCtrl;
33XGpio axi_gpio_inst; //PL端 AXI GPIO 驱动实例
34XOv5640_rgb2gray rgb2gray_inst; //PL端XOv5640_rgb2gray驱动实例
35VideoMode vd_mode;
36unsigned int lcd_id;
37
38int main(void)
39{
40 u32 status;
41 u16 cmos_h_pixel; //ov5640 DVP 输出水平像素点数
42 u16 cmos_v_pixel; //ov5640 DVP 输出垂直像素点数
43 u16 total_h_pixel;//ov5640 水平总像素大小
44 u16 total_v_pixel;//ov5640 垂直总像素大小
45
46 //获取LCD的ID
47 XGpio_Initialize(&axi_gpio_inst, AXI_GPIO_0_ID);
48 lcd_id = LTDC_PanelID_Read(&axi_gpio_inst,AXI_GPIO_0_CHANEL);
49 xil_printf("lcd_id = %x\n\r",lcd_id);
50
51 //根据获取的LCD的ID号来进行ov5640显示分辨率参数的选择
52 switch(lcd_id){
53 case 0x4342 ://4.3寸屏,480*272分辨率
54 cmos_h_pixel = 480;
55 cmos_v_pixel = 272;
56 total_h_pixel = 1800;
57 total_v_pixel = 1000;
58 break;
59 case 0x4384 ://4.3寸屏,800*480分辨率
60 cmos_h_pixel = 800;
61 cmos_v_pixel = 480;
62 total_h_pixel = 1800;
63 total_v_pixel = 1000;
64 break;
65 case 0x7084 ://7寸屏,800*480分辨率
66 cmos_h_pixel = 800;
67 cmos_v_pixel = 480;
68 total_h_pixel = 1800;
69 total_v_pixel = 1000;
70 break;
71 case 0x7016 ://7寸屏,1024*600分辨率
72 cmos_h_pixel = 1024;
73 cmos_v_pixel = 600;
74 total_h_pixel = 2200;
75 total_v_pixel = 1000;
76 break;
77 case 0x1018 ://10.1寸屏,1280*800分辨率
78 cmos_h_pixel = 1280;
79 cmos_v_pixel = 800;
80 total_h_pixel = 2570;
81 total_v_pixel = 980;
82 break;
83 default :
84 cmos_h_pixel = 480;
85 cmos_v_pixel = 272;
86 total_h_pixel = 1800;
87 total_v_pixel = 1000;
88 break;
89 }
90
91 emio_init(); //初始化EMIO
92 status = ov5640_init( cmos_h_pixel,//初始化ov5640
93 cmos_v_pixel,
94 total_h_pixel,
95 total_v_pixel);
96 if(status == 0)
97 xil_printf("OV5640 detected successful!\r\n");
98 else
99 xil_printf("OV5640 detected failed!\r\n");
100
101 //根据获取的LCD的ID号来进行video参数的选择
102 switch(lcd_id){
103 case 0x4342 : vd_mode = VMODE_480x272;break;//4.3寸屏,480*272分辨率
104 case 0x4384 : vd_mode = VMODE_800x480;break;//4.3寸屏,800*480分辨率
105 case 0x7084 : vd_mode = VMODE_800x480;break;//7寸屏,800*480分辨率
106 case 0x7016 : vd_mode = VMODE_1024x600; break;//7寸屏,1024*600分辨率
107 case 0x1018 : vd_mode = VMODE_1280x800; break;//10.1寸屏,1280*800分辨率
108 default : vd_mode = VMODE_800x480; break;
109 }
110
111 //初始化灰度转换IP核OV5640_RGB2GRAY
112 XOv5640_rgb2gray_Initialize(&rgb2gray_inst, XPAR_OV5640_RGB2GRAY_0_DEVICE_ID);
113 //配置灰度转换IP核OV5640_RGB2GRAY的行数
114 XOv5640_rgb2gray_Set_rows(&rgb2gray_inst, vd_mode.height);
115 //配置灰度转换IP核OV5640_RGB2GRAY的行数
116 XOv5640_rgb2gray_Set_cols(&rgb2gray_inst, vd_mode.width);
117
118 //配置VDMA
119 run_vdma_frame_buffer(&vdma, VDMA_ID, vd_mode.width, vd_mode.height,
120 frame_buffer_addr,0,0,BOTH);
121 //初始化Display controller
122 DisplayInitialize(&dispCtrl, DISP_VTC_ID, DYNCLK_BASEADDR);
123 //设置VideoMode
124 DisplaySetMode(&dispCtrl, &vd_mode);
125 DisplayStart(&dispCtrl);
126
127 return 0;
128 }
在代码的第14行引入了“xov5640_rgb2gray.h”头文件,这个头文件是Vivado HLS工具生成的,里面声明了灰度转换IP核的驱动函数。
首先在代码的34行定义了灰度转换IP核的驱动实例rgb2gray_inst,该变量会在后面对IP核进行配置时用到。然后在代码的第112行通过“XOv5640_rgb2gray_Initialize()”函数来初始化Vivado HLS生成的灰度转换IP核;在代码的第116行通过传入“vd_mode.height”形参来设置灰度转换IP核的行数,在代码的第114行通过传入“vd_mode.width”形参来设置列数。这些数据类型和函数在“xlcd_rgb_color.h”头文件中均有声明。
6.59下载验证
编译完工程之后我们就可以开始下载程序了。将 OV5640 摄像头模块插在领航者 Zynq 开发板的“OLED/CAMERA”插座上,并将 LCD 的排线接头插入开发板上的 LCD 接线座。将下载器一端连电脑,
另一端与开发板上的 JTAG 端口连接,连接电源线并打开电源开关。
在 SDK 软件下方的 SDK Terminal 窗口中点击右上角的加号设置并连接串口。然后下载本次实验硬件设计过程中所生成的 BIT 文件,来对 PL 进行配置。最后下载软件程序,下载完成后在LCD上就可以看到摄像头采集的彩色图像被转换成了灰度图像,如下图所示:
图 6.5.1 RGB LCD液晶屏显示灰度图像
页:
[1]