搜索
bottom↓
回复: 0

【正点原子FPGA连载】第八章基于OV5640的视频图像叠加实验--摘自【正点原子】领航者ZYNQ之HLS 开发指南

[复制链接]

出0入234汤圆

发表于 2020-9-27 10:02:52 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2020-9-27 10:04 编辑

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 点击加入:
QQ群头像.png                                                                                                                
6)关注正点原子公众号,获取最新资料


100846rel79a9p4uelap24.jpg

100846f1ce1fg14zbg0va4.png

第八章基于OV5640的视频图像叠加实验



视频图像叠加技术是当前在屏幕显示(onscreen display,OSD)技术的一个重要部分。视频图像叠加技术在雷达监测口、汽车智能、医学成像等方面应用越来越广。本章节我们将使用Vivado HLS生成一个图像彩条叠加的IP核,来进行基于OV5640的视频图像叠加实验。
本章包括以下几个部分:
88.1简介
8.2实验任务
8.3HLS设计
8.4IP验证
8.5下载验证


8.1简介
视频图像叠加是指在原视频图像中叠加另一视频图像信号,使得原视频图像中叠加上字符或图像。其基本原理就是将存放在存储器里的字符或图像转换成满足一定格式要求的信号,同输入的另一组图像或视频信号合并,使被叠加的图像信号与原视频图像信号在指定位置上同步显示。图像叠加过程是以像素为基本单元的。
视频图像叠加采用的算法是Alpha透明混叠算法,其主要公式:
imgL(x,y)= Alpha(x,y) * imgM(x,y)+(1 - Alpha(x,y) * imgN(x,y))
在上述公式中, imgL(x,y)表示叠加后的图像,Alphal(x,y)表示前景图像(被叠加的图像)透明度; imgM(x,y)表示前景图像(被叠加的图像);imgN(x,y)表示背景图像。本章实验中我们将基于上述公式实现视频图像叠加实验。
阿莫论坛发帖领航者专用1556.png

图 8.1.1 前景图、背景图、叠加图

如上图所示:“colorbar”表示前景图像M,“lena”表示背景图像N,”lena_colorbar”表示叠加之后的图像imgL。
图像的透明度是相对的,即一张图的透明程度是与另一张图作为对照的结果。通常我们使用电脑工具看到的图像透明都是以背景为对照的。其原理就是,两张图叠加,M为前景图,N为背景图。此时透明度为Alpha,那么M图RGB分量占比Alpha,N图RGB分量占比(1-Alpha),两者之和即为新图的RGB分量,也就是我们看到的叠加之后的图像,这里前后景是按100%分配,当然你也可以按256分配(数字图像中透明度分为256级)。M、N分别对应公式中的imgM(x,y)和imgN(x,y)。
Alpha取不同值时会产生不同的叠加效果,当Alpha(x,y)取值为1时,图像imgM(x,y)完全不透明,此时只显示图像imgM(x,y);当Alpha(x,y)取值为0时,图像imgM(x,y)完全透明,此时只显示图像imgN(x,y)。下面我们给出公式中Alpha按256分配时的计算公式:
R(L) = (Alpha * R(M) + (255 - Alpha) * R(N) ) >> 8
G(L) =( Alpha * G(M) + (255 - Alpha) * G(N) ) >> 8
B(L) =( Alpha * B(M) + (255 - Alpha) * B(N))  >> 8
上述公式中, R(L)、G(L)、B(L)分别指叠加后的图像L的RGB分量,R(M)、G( M)、B(M)分别指前景图像M的RGB分量,R(N)、G(N)、B(N)分别指背景图像N的RGB分量,易知Alpha值取0表示图像M完全透明,取255则表示图像M完全不透明。
8.2实验任务
本节的实验任务是使用Vivado HLS设计图像彩条叠加的IP核,并在Vivado中对设计出来的IP核进行验证。
8.3HLS设计
我们在电脑中的“F:\ZYNQ\High_Level_Synthesis”目录下新建一个名为add_image_colorbar的文件夹,作为本次实验的工程目录。然后打开Vivado HLS工具,创建一个新的工程。设置工程名为“add_image_colorbar”,选择工程路径为刚刚创建的文件夹。需要注意的是,工程名以及路径只能由英文字母、数字和下划线组成,不能包含中文、空格以及其他特殊字符。如下图所示:
阿莫论坛发帖领航者专用11640.png

图 8.3.1 工程配置界面

设置好工程名及路径之后,点击“Next”,进入如下界面设置顶层函数:
阿莫论坛发帖领航者专用11735.png

图 8.3.2 设置顶层函数

工程创建完成后,在工程面板中的“source”目录上点击右键,然后在打开的列表中选择“New File”新建源文件,在弹出的对话框中输入源文件的名称“add_image_colorbar.cpp”,如图1.3.3所示。源文件默认的保存路径为HLS工程目录,为方便源文件的管理,我们在工程目录下新建一个名为“src”的文件下,将源文件保存在src目录下。
阿莫论坛发帖领航者专用11972.png

图 8.3.3 输入源文件名

我们在这里使用Vivado HLS提供的视频库“hls_video.h”,由于这个视频库使用C++语言编写的,那么后缀名需要设置为“.cpp”。设置好文件名和路径之后,点击“保存”。
“lcd_rgb_colorbar.c”文件源代码如下:
  1. 1  #include "add_image_colorbar.h"
  2. 2  
  3. 3  void add_image_colorbar(RGB_IMAGE& src, RGB_IMAGE& dst, int rows, int cols, int alpha)
  4. 4  {
  5. 5      //定义输入和输出图像像素数据
  6. 6      RGB_PIXEL src_data;
  7. 7      RGB_PIXEL dst_data;
  8. 8  
  9. 9      int color_edge = cols/7;    //7个彩条叠加
  10. 10
  11. 11     //获取图像数据
  12. 12     for(int y_pos = 0; y_pos < rows; y_pos++){
  13. 13         for(int x_pos = 0; x_pos < cols; x_pos++){
  14. 14             if((y_pos < rows) && (x_pos < cols)){
  15. 15                 src >> src_data;
  16. 16             }
  17. 17
  18. 18             //获取输入图像像素RGB通道数据
  19. 19             unsigned char src_B = src_data.val[0];
  20. 20             unsigned char src_G = src_data.val[1];
  21. 21             unsigned char src_R = src_data.val[2];
  22. 22
  23. 23             //定义输出图像像素RGB通道数据
  24. 24             unsigned char dst_B;
  25. 25             unsigned char dst_G;
  26. 26             unsigned char dst_R;
  27. 27
  28. 28             //彩条数据叠加
  29. 29             if(x_pos < color_edge){
  30. 30                 dst_B = ((alpha*0x00) + (255-alpha)*src_B)>>8;    //红色
  31. 31                 dst_G = ((alpha*0x00) + (255-alpha)*src_G)>>8;
  32. 32                 dst_R = ((alpha*0xff) + (255-alpha)*src_R)>>8;
  33. 33
  34. 34             }else if(x_pos >= color_edge && x_pos < color_edge*2){
  35. 35                 dst_B = ((alpha*0x00) + (255-alpha)*src_B)>>8;    //橙色
  36. 36                 dst_G = ((alpha*0x7f) + (255-alpha)*src_G)>>8;
  37. 37                 dst_R = ((alpha*0xff) + (255-alpha)*src_R)>>8;
  38. 38
  39. 39             }else if(x_pos >= color_edge*2 && x_pos < color_edge*3){
  40. 40                 dst_B = ((alpha*0x00) + (255-alpha)*src_B)>>8;    //黄色
  41. 41                 dst_G = ((alpha*0xff) + (255-alpha)*src_G)>>8;
  42. 42                 dst_R = ((alpha*0xff) + (255-alpha)*src_R)>>8;
  43. 43             }else if(x_pos >= color_edge*3 && x_pos < color_edge*4){
  44. 44                 dst_B = ((alpha*0x00) + (255-alpha)*src_B)>>8;    //绿色
  45. 45                 dst_G = ((alpha*0xff) + (255-alpha)*src_G)>>8;
  46. 46                 dst_R = ((alpha*0x00) + (255-alpha)*src_R)>>8;
  47. 47             }else if(x_pos >= color_edge*4 && x_pos < color_edge*5){
  48. 48                 dst_B = ((alpha*0xff) + (255-alpha)*src_B)>>8;    //青色
  49. 49                 dst_G = ((alpha*0xff) + (255-alpha)*src_G)>>8;
  50. 50                 dst_R = ((alpha*0x00) + (255-alpha)*src_R)>>8;
  51. 51             }else if(x_pos >= color_edge*5 && x_pos < color_edge*6){
  52. 52                 dst_B = ((alpha*0xff) + (255-alpha)*src_B)>>8;    //蓝色
  53. 53                 dst_G = ((alpha*0x00) + (255-alpha)*src_G)>>8;
  54. 54                 dst_R = ((alpha*0x00) + (255-alpha)*src_R)>>8;
  55. 55             }else if(x_pos >= color_edge*6 && x_pos < cols){
  56. 56                 dst_B = ((alpha*0xff) + (255-alpha)*src_B)>>8;    //紫色
  57. 57                 dst_G = ((alpha*0x00) + (255-alpha)*src_G)>>8;
  58. 58                 dst_R = ((alpha*0x8b) + (255-alpha)*src_R)>>8;
  59. 59             }
  60. 60
  61. 61             //输出图像数据
  62. 62             dst_data.val[0] = dst_B;
  63. 63             dst_data.val[1] = dst_G;
  64. 64             dst_data.val[2] = dst_R;
  65. 65             dst << dst_data;
  66. 66         }
  67. 67     }
  68. 68 }
  69. 69
  70. 70 //顶层函数
  71. 71 void img_addweighted_top(AXI_STREAM& input_axi,
  72. 72                          AXI_STREAM& output_axi,
  73. 73                          int rows,
  74. 74                          int cols,
  75. 75                          int alpha)
  76. 76 {
  77. 77 #pragma HLS INTERFACE ap_ctrl_none port=return
  78. 78 #pragma HLS INTERFACE s_axilite port=alpha
  79. 79 #pragma HLS INTERFACE s_axilite port=cols
  80. 80 #pragma HLS INTERFACE s_axilite port=rows
  81. 81 #pragma HLS INTERFACE axis register both port=output_axi
  82. 82 #pragma HLS INTERFACE axis register both port=input_axi
  83. 83
  84. 84     RGB_IMAGE img_0(rows,cols);
  85. 85     RGB_IMAGE img_1(rows,cols);
  86. 86
  87. 87 #pragma HLS dataflow
  88. 88
  89. 89     hls::AXIvideo2Mat(input_axi, img_0);
  90. 90     add_image_colorbar(img_0, img_1, rows, cols, alpha);
  91. 91     hls::Mat2AXIvideo(img_1, output_axi);
  92. 92 }
复制代码

在代码的第1行引入头文件“add_image.h”,这是我们定义的一个头文件,在这个头文件里对视频库中的一些数据类型进行了重新定义,同时对顶层函数进行了声明。
本章实验的顶层函数是第71行“img_weighted_top”,这个函数实现了图像叠加的功能。它的5个参数”input_axi”、“output_axi”、”rows”、“cols”、”alpha“分别表示输入的axi4-stream流数据、输出的axi4-stream流数据、输出图像像素的行数、输出图像像素的列数、图像前景的透明度。这个函数里的优化指令在我们在前面的章节已经介绍过,如果对这些优化指令不熟悉,请大家参考前面的几个章节。在代码的第89行通过”AXIvideo2Mat”这个函数实现了将输入的AXI4-Stream流数据转化了Vivado HLS可以处理的Mat格式的数据,在代码的第91行通过”Mat2AXIvideo”实现了将Vivado HLS可以处理的Mat格式的数据转化成了AXI-Stream流数据。由于Vivado HLS输入和输出都是AXI4-Stream格式的数据流,所以我们在这里进行了数据格式的转换。在代码的第90行“add_image_colorbar”实现了彩条图像叠加的功能,它的第一个参数”img_0”表示输入图像,第二个参数“img_1”表示输出图像。
在代码的第2行定义了图像叠加函数“add_image_colorbar”,这个函数传入了5个形参,分别是“src”、”dst”、“rows”、”cols”、”alpha”。其中src表示输入图像,“RGB_IMAGE”表示输入图像的类型是8位无符号三通道的。dst表示输出图像。rows表示输入图像的行数,cols表示输入图像的列数。alpha表示叠加在摄像头输出图像上彩条的透明度。
在代码的第6行和第7行分别定义了输入图像和输出图像的像素数据(src_data和dst_data),后面可以对像素数据的RGB三个通道进行赋值,从而实现彩条图像的叠加。在代码的第9行“color_edge = cols/7”表示实现7个彩条数据的叠加。
在代码的第12行到第15行通过判断“x_pos”和“y_pos”的值确定像素显示的位置,当像素显示位置在图像的宽(clos)和图像的高(rows)范围内时,通过”>>“这个重载操作符从输入的视频流src中读出指定位置的像素数据,并将其保存到src_data中。这里我们介绍一下操作符重载的概念,操作符重载就是把已经定义的、有一定功能的操作符进行重新定义,来完成更为细致具体的运算等功能。操作符重载可以将概括性的抽象操作符具体化,便于外部调用而无须知晓其内部的具体运算过程。
C++有许多内置的数据类型,包括int、char、string等,每种类型都有许多运算符,例如+、-、*、/等,可以利用这些运算符方便地对两个数据进行运算,从而得到相应的运算结果。针对这些内置的数据类型,C++提供了丰富的运算符供我们完成常见的操作。但是当我们定义了新的类的对象时,两个对象之间是不能进行这些操作的。但幸运的事,C++允许我们对这些操作符进行重载,告诉编译器该如何进行自定义对象之间的各种运算。重载操作符之后,可以方便地在类对象之间使用操作符进行各种有意义的运算,就像内置类型的运算一样方便。在功能上,重载操作符等同于类的成员函数,两者并无明显差别,可以简单地讲重载操作符是一类比较特殊的成员函数。虽然成员函数可以提供跟操作符相同的功能,但是运用操作符可以让语句更加简洁,也更加容易理解。
在C++中,声明重载操作符的语法格式如下:
  1. class 类名
  2. {
  3.         public:
  4.                 返回值类型 operator 操作符 (参数列表)
  5.                 {
  6.                         操作符的具体运算过程
  7.                 }
  8.         };
复制代码

从这里可以看到,重载操作符和成员函数虽然在功能上是相同的,但是在语法格式上还是存在一些细微的差别。普通成员函数以标识符作为函数名,而重载操作符以“operator“操作符作为函数名。在使用上,当使用操作符对两个对象进行运算时,实际上是调用第一个对象的操作符,而第二个对象则作为这个操作符的参数。
我们在Vivado HLS找到这个重载运算符的定义,如下所示:
void operator >> (Scalar<HLS_MAT_CN(T), HLS_TNAME(T)>& s);   
函数名为“operator >>”,返回值为void,参数为HLS_MAT_CN(T),HLS_TNAME(T),在代码的第15行,”src>>src_data”会被转换为类似的函数调用形式:src.operator>>(src_data),这实质上就是调用对象的成员函数。
在代码的第19行到第21行通过读取Scalar类中val这个成员变量的值来获取像素点RGB三个通道的值,由于像素点RGB三个通道的颜色数据在opencv中是按照BGR内存分布的,所以“src_data.val[0]”表示像素点B通道的值,“src_data.val[1]”表示像素点G通道的值,”src_data.val[2]”表示像素点R通道的值。
在代码的第28到第59行通过比较“x_pos”与”color_edge”的大小来判断像素点的位置,当像素点位于不同的x轴位置时,给输出像素点的三个通道“dst_B”、”dst_G”、“dst_R”叠加上不同颜色的彩条数据。在代码的第62行到第65行通过”<<“这个重载运算符将输出图像像素数据保存到输出图像中。
“add_image_colorbar.h”头文件源代码如下:
  1. 1  #ifndef _ADD_IMAGE_H_
  2. 2  #define _ADD_IMAGE_H_
  3. 3  
  4. 4  #include "hls_video.h"
  5. 5  #include <string.h>
  6. 6  
  7. 7  //定义图像最大尺寸
  8. 8  #define MAX_WIDTH  1920
  9. 9  #define MAX_HEIGHT 1080
  10. 10
  11. 11 //定义数据类型
  12. 12 typedef hls::stream<ap_axiu<24,1,1,1> >  AXI_STREAM;
  13. 13 typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3> RGB_IMAGE;
  14. 14 typedef hls::Scalar<3, unsigned char> RGB_PIXEL;
  15. 15
  16. 16 void img_addweighted_top(AXI_STREAM& input_axi, AXI_STREAM& output_axi, int rows, int cols, int alpha);
  17. 17
  18. 18 #endif
复制代码

在代码的第1行和第2行是为了避免头文件的重复定义。在代码的第4行引入了“hls_video.h”这个视频库。在代码的第8行和第9行定义图像最大支持的尺寸是1920*1080个像素点。
在代码的第12行到第14行重新定义了HLS中的数据类型,其中第12行和第13行的数据类型在灰度显示实验中已经介绍过,这里我们重点关注第14行的数据类型“hls::Scalar”。这个类模板的第一个参数3表示我们定义图像的通道是3通道类型的,第二个参数unsigned char表示我们定义图像的数据类型是8位无符号数据类型。由于opencv中图像数据在内存中是按照BGR分布的,所以当我们用这个类定义一个对象src时,我们可以通过src.val[0]获取图像蓝色通道的数据,src.val[1]获取图像绿色通道的数据,src.val[2]获取图像红色通道的数据。
代码输入完成后,按快捷键Ctrl+S保存。然后点击工具栏中向右的绿色三角形对C代码进行综合,如下图所示:
阿莫论坛发帖领航者专用19199.png

图 8.3.4 运行C综合

综合完成后,会自动打开综合结果(solution)的报告,如下图所示:
阿莫论坛发帖领航者专用19294.png

图 8.3.5 综合报告

在工具栏中点击黄色的“田”字按钮,导出RTL,如下图所示:
阿莫论坛发帖领航者专用19382.png

图 8.3.6 导出RTL

在弹出的对话框中保持默认设置,直接点击“OK”,如下图所示:
阿莫论坛发帖领航者专用19472.png

图 8.3.7 将设计导出成IP

设计导出完成后,HLS设计部分就结束了,我们在HLS工程目录下可以找到导出的IP核,如下图红色方框所示:
阿莫论坛发帖领航者专用19587.png

图 8.3.8 导出得到的IP

HLS设计结束之后,我们将在Vivado中对导出的IP核进行验证。
8.4IP验证
在IP验证环节,我们会使用Vivado工具的IP集成器将生成的IP核添加到Block Design中,然后完成设计后将程序下载到领航者开发板上进行验证。
用于IP验证的底层硬件可以在《领航者ZYNQ之HLS开发指南》第5章“OV5640 摄像头灰度显示”实验的基础上进行。打开该实验所对应的Vivado工程“ov5640_rgb2gray_ip_test”,将其另存为“add_image_colorbar_ip_test”工程。为了方便工程管理,我们将Vivado工程的目录与HLS工程目录保持一致,如下图所示:
阿莫论坛发帖领航者专用19945.png

图 8.4.1 创建Vivado工程

在通过“另存为”的方式保存工程之后,还要将原来工程中的IP库(名为ip_repo的文件夹)复制到新的Vivado工程目录下, 然后将HLS设计过程中导出的IP核拷贝到“ip_repo”目录下并解压。
在Vivado中重新将当前工程目录下的ip_repo文件夹添加到工程的IP库中,然后将HLS生成的IP核Ov5640_sobel添加到Block Design中,并将其STREAM接口分别连接到Video In to AXI4-Stream模块的video_out接口与VDMA模块的S_AXIS_S2MM接口上。最后点击上图中左上角的“Run Connection Automation”,让工具自动连接该IP核的其他端口,包括时钟、复位以及AXI-Lite从接口,最终完成的设计如下图所示:
然后点击“Run Connnection Automation”,下面列出了会自动连接的模块及其接口,勾选“All Automation”, 然后点击“OK”按钮。最终完成的设计如下图所示:
阿莫论坛发帖领航者专用110455.png

图 8.4.2 完成后的Block Design

到这里我们的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_addweight_colorbar_lcd”。
然后找到《领航者ZYNQ之嵌入式开发指南》第二十三章“OV5640 摄像头 LCD 显示”实验的Vivado工程目录,将“21_ov7725_lcd\ov7725_lcd.sdk\ov7725_lcd”目录下的src文件夹拷贝到新建的应用工程目录下。
在SDK中刷新src目录,然后将“main.c”的代码修改为如下所示:
  1. 1   #include <stdio.h>
  2. 2   #include <stdlib.h>
  3. 3   #include <string.h>
  4. 4   #include "xil_types.h"
  5. 5   #include "xil_cache.h"
  6. 6   #include "xparameters.h"
  7. 7   #include "xgpio.h"
  8. 8   #include "xaxivdma.h"
  9. 9   #include "xaxivdma_i.h"
  10. 10  #include "display_ctrl/display_ctrl.h"
  11. 11  #include "vdma_api/vdma_api.h"
  12. 12  #include "emio_sccb_cfg/emio_sccb_cfg.h"
  13. 13  #include "ov5640/ov5640_init.h"
  14. 14  #include "ximg_addweighted_top.h"
  15. 15  
  16. 16  //宏定义
  17. 17  #define BYTES_PIXEL        3                           //像素字节数,RGB888占3个字节
  18. 18  #define FRAME_BUFFER_NUM   3                           //帧缓存个数3
  19. 19  #define DYNCLK_BASEADDR    XPAR_AXI_DYNCLK_0_BASEADDR  //动态时钟基地址
  20. 20  #define VDMA_ID            XPAR_AXIVDMA_0_DEVICE_ID    //VDMA器件ID
  21. 21  #define DISP_VTC_ID        XPAR_VTC_0_DEVICE_ID        //VTC器件ID
  22. 22  //PL端  AXI GPIO 0(lcd_id)器件 ID
  23. 23  #define AXI_GPIO_0_ID      XPAR_AXI_GPIO_0_DEVICE_ID   
  24. 24  //使用AXI GPIO(lcd_id)通道1
  25. 25  #define AXI_GPIO_0_CHANEL  1                           
  26. 26  
  27. 27  //全局变量
  28. 28  //frame buffer的起始地址
  29. 29  unsigned int const frame_buffer_addr = (XPAR_PS7_DDR_0_S_AXI_BASEADDR
  30. 30                                          + 0x1000000);
  31. 31  XAxiVdma                 vdma;
  32. 32  DisplayCtrl              dispCtrl;
  33. 33  XGpio                    axi_gpio_inst;         //PL端 AXI GPIO 驱动实例
  34. 34  XImg_addweighted_top     addweight_inst;       //PL端addweight_inst驱动实例
  35. 35  VideoMode                vd_mode;
  36. 36  unsigned int lcd_id;
  37. 37  
  38. 38  int main(void)
  39. 39  {
  40. 40      u32 status;
  41. 41      u16 cmos_h_pixel;   //ov5640 DVP 输出水平像素点数
  42. 42      u16 cmos_v_pixel;   //ov5640 DVP 输出垂直像素点数
  43. 43      u16 total_h_pixel;  //ov5640 水平总像素大小
  44. 44      u16 total_v_pixel;  //ov5640 垂直总像素大小
  45. 45  
  46. 46      //获取LCD的ID
  47. 47      XGpio_Initialize(&axi_gpio_inst, AXI_GPIO_0_ID);
  48. 48      lcd_id = LTDC_PanelID_Read(&axi_gpio_inst,AXI_GPIO_0_CHANEL);
  49. 49      xil_printf("lcd_id = %x\n\r",lcd_id);
  50. 50  
  51. 51      //根据获取的LCD的ID号来进行ov5640显示分辨率参数的选择
  52. 52      switch(lcd_id){
  53. 53          case 0x4342 :  //4.3寸屏,480*272分辨率
  54. 54              cmos_h_pixel = 480;
  55. 55              cmos_v_pixel = 272;
  56. 56              total_h_pixel = 1800;
  57. 57              total_v_pixel = 1000;
  58. 58              break;
  59. 59          case 0x4384 :  //4.3寸屏,800*480分辨率
  60. 60              cmos_h_pixel = 800;
  61. 61              cmos_v_pixel = 480;
  62. 62              total_h_pixel = 1800;
  63. 63              total_v_pixel = 1000;
  64. 64              break;
  65. 65          case 0x7084 :  //7寸屏,800*480分辨率
  66. 66              cmos_h_pixel = 800;
  67. 67              cmos_v_pixel = 480;
  68. 68              total_h_pixel = 1800;
  69. 69              total_v_pixel = 1000;
  70. 70              break;
  71. 71          case 0x7016 :  //7寸屏,1024*600分辨率
  72. 72              cmos_h_pixel = 1024;
  73. 73              cmos_v_pixel = 600;
  74. 74              total_h_pixel = 2200;
  75. 75              total_v_pixel = 1000;
  76. 76              break;
  77. 77          case 0x1018 :  //10.1寸屏,1280*800分辨率
  78. 78              cmos_h_pixel = 1280;
  79. 79              cmos_v_pixel = 800;
  80. 80              total_h_pixel = 2570;
  81. 81              total_v_pixel = 980;
  82. 82              break;
  83. 83          default :
  84. 84              cmos_h_pixel = 480;
  85. 85              cmos_v_pixel = 272;
  86. 86              total_h_pixel = 1800;
  87. 87              total_v_pixel = 1000;
  88. 88              break;
  89. 89      }
  90. 90  
  91. 91      emio_init();                         //初始化EMIO
  92. 92      status = ov5640_init( cmos_h_pixel,  //初始化ov5640
  93. 93                            cmos_v_pixel,
  94. 94                           total_h_pixel,
  95. 95                           total_v_pixel);
  96. 96      if(status == 0)
  97. 97          xil_printf("OV5640 detected successful!\r\n");
  98. 98      else
  99. 99          xil_printf("OV5640 detected failed!\r\n");
  100. 100
  101. 101     //根据获取的LCD的ID号来进行video参数的选择
  102. 102     switch(lcd_id){
  103. 103         case 0x4342 : vd_mode = VMODE_480x272;  break;  //4.3寸屏,480*272分辨率
  104. 104         case 0x4384 : vd_mode = VMODE_800x480;  break;  //4.3寸屏,800*480分辨率
  105. 105         case 0x7084 : vd_mode = VMODE_800x480;  break;  //7寸屏,800*480分辨率
  106. 106         case 0x7016 : vd_mode = VMODE_1024x600; break;  //7寸屏,1024*600分辨率
  107. 107         case 0x1018 : vd_mode = VMODE_1280x800; break;  //10.1寸屏,1280*800分辨率
  108. 108         default : vd_mode = VMODE_800x480; break;
  109. 109     }
  110. 110
  111. 111    //初始化灰度转换IP核add_image_color
  112. 112    XImg_addweighted_top_Initialize(&addweight_inst, XPAR_IMG_ADDWEIGHTED_TOP_0_DEVICE_ID);
  113. 113    //配置灰度转换IP核add_image_color的行数
  114. 114    XImg_addweighted_top_Set_rows(&addweight_inst, vd_mode.height);
  115. 115    //配置灰度转换IP核add_image_color的列数
  116. 116    XImg_addweighted_top_Set_cols(&addweight_inst, vd_mode.width);
  117. 117    //配置灰度转换IP核add_image_color的alpha值
  118. 118    XImg_addweighted_top_Set_alpha(&addweight_inst, 138);
  119. 119
  120. 120     //配置VDMA
  121. 121     run_vdma_frame_buffer(&vdma, VDMA_ID, vd_mode.width, vd_mode.height,
  122. 122                             frame_buffer_addr,0,0,BOTH);
  123. 123     //初始化Display controller
  124. 124     DisplayInitialize(&dispCtrl, DISP_VTC_ID, DYNCLK_BASEADDR);
  125. 125     //设置VideoMode
  126. 126     DisplaySetMode(&dispCtrl, &vd_mode);
  127. 127     DisplayStart(&dispCtrl);
  128. 128
  129. 129     return 0;
  130. 130 }
复制代码

在代码的第14行引入了“ximg_addweighted_top.h”头文件,这个头文件是Vivado HLS工具生成的,里面声明了彩条图像叠加IP核的驱动函数。首先在代码的34行定义了彩条图像叠加IP核的驱动实例addweight _inst,该变量会在后面对IP核进行配置时用到。然后在代码的第112行通过“XImg_addweighted_top_Initialize ()”函数来初始化Vivado HLS生成的彩条图像叠加IP核;在代码的第116行通过传入“vd_mode.height”形参来设置彩条图像叠加IP核的行数,在代码的第114行通过传入“vd_mode.width”形参来设置列数。这些数据类型和函数在“xlcd_rgb_color.h”头文件中均有声明。在代码的第118行,XImg_addweighted_top_Set_alpha函数的第二个参数是图像的alpha值,这里设置成了138,该值的取值范围是0~255,设置的值越小,前景彩条图像的透明度越高。
8.5下载验证
编译完工程之后我们就可以开始下载程序了。将 OV5640 摄像头模块插在领航者 Zynq 开发板的“OLED/CAMERA”插座上,并将 LCD 的排线接头插入开发板上的 LCD 接线座。将下载器一端连电脑,
另一端与开发板上的 JTAG 端口连接,连接电源线并打开电源开关。
在 SDK 软件下方的 SDK Terminal 窗口中点击右上角的加号设置并连接串口。然后下载本次实验硬件设计过程中所生成的 BIT 文件,来对 PL 进行配置。最后下载软件程序,下载完成后在LCD上就可以看到液晶屏上显示了摄像头采集图像和彩条数据图像叠加后的图像,如下图所示:
阿莫论坛发帖领航者专用116943.png

图 8.5.1图像叠加实验效果图

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 22:43

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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