本帖最后由 正点原子运营 于 2020-9-28 10:25 编辑
1)实验平台:正点原子领航者ZYNQ开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-301505-1-1.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:905624739 点击加入群聊:
5)关注正点原子公众号,获取最新资料更新
第二十九章 双目OV5640摄像头LCD显示实验
双目摄像头是在一个模组上集成了两个摄像头,实现了双通道的图像采集。双目摄像头一般应用于安防监控、立体视觉测距、三维重建等领域。本章我们将使用领航者ZYNQ开发板实现对双目OV5640摄像头的图像采集并通过LCD屏实时显示。 26 1 1.1 简介 1.2 实验任务 1.3 硬件设计 1.4 软件设计 1.5 下载验证 1.1 简介摄像头在日常生活中非常常见,一般分为单目摄像头、双目摄像头和多目摄像头。单目摄像头凭借着成本低和易用的特点,目前使用最为广泛;双目摄像头主要应用于单目摄像头无法胜任的场合,如三维坐标定位等;当然针对一些特殊的应用,目前市场上也出现了多目摄像头,以应对更加复杂的场景。 我们在前面的例程中实现了单目OV5640摄像头LCD屏的实时显示,本章我们在此基础上实现双目OV5640摄像头的实时显示,即两路摄像头的图像视频显示在LCD屏上,并叠加字符的显示。值得一提的是,Xilinx官方提供了专门用于视频混合显示和简单文本叠加的视频处理模块,即OSD(On Screen Display)IP核。OSD IP核最大支持8层外部视频的输入(来自帧缓冲区或者AXI4数据流接口视频)和内部图形控制器(如:文本控制),该IP核通过一个寄存器接口来设置和控制屏幕大小、背景颜色、图层位置等。 下图为通过Xilinx OSDIP核实现图像视频和文本叠加显示的例子。 图 26.1.1 OSD IP核跌在字符显示Demo 图 31.1.1是一个具有多个视频源、文本和方框通过OSD叠加输出的示例。图中的Video1、Video2和Video3这三个视频层可以是静态图像或实时视频,位于可编程的背景色之上;图中的黄色文本和菜单栏是通过OSD自带的内部图形控制器生成,并与其它图层混合;另外,图中的Xilinx logo同样是一个视频层,可以由片内RAM或者片外存储器生成。 OSD IP核的输入接口和输出支持AXI4-Stream接口协议。输入接口为AXI4-Stream SlaveInterface,输出接口为AXI4-Stream Master Interface,因此OSD IP核可以非常容易的和Xilinx其它的IP核集成在一起,如AXI VDMA、VideoScaler和Video Timing Controller等,同样也支持其它的AXI4-Stream Video IP核。 OSD IP核能够工作在1080P 60Hz,最大支持的分辨率为4096x4096。OSD IP核实例化了一组内部图形控制器,每个图形控制器可以独立配置。图形控制器包括方框和文本生成器,可以在运行时通过AXI4-Lite接口重新配置,以移动或调整文本和方框的大小。其中文本可以按照比例放大至2倍、4倍或者8倍。 OSD配置为三个图层的示例框图如图31.1.2所示: 图 26.1.2 OSD框图 由上图可知,图层0和图层1配置为外部视频输入,用于连接AXI4-Stream类型接口;图层2配置为内部图形控制器,用于显示字符或者方框等。每个图层分配一个优先级,数值越大表示优先级越高,优先级高的图层会叠加在优先级低的图层之上,没有叠加图层的区域将会显示背景色。除此之外,OSD IP核也有一个可选的AXI4-Lite接口和可选的中断状态输出接口,处理器可以通过AXI4-Lite实时配置OSD IP核的控制器寄存器,从而控制每个图层的分辨率、背景色、字符的显示和位置等;而中断状态输出接口用于输出中断状态位包括图形控制器指令错误等。 1.2 实验任务本章的实验任务是通过调用OSDIP核,在领航者Zynq开发板上实现双目OV5640摄像头LCD屏的实时显示,并在LCD屏上叠加字符。 1.3 硬件设计我们的领航者Zynq开发板上有一个扩展接口(位于ATK MODULE附近),该接口可以用来连接双目OV5640摄像头、高速ADDA等模块,扩展接口原理图如图31.3.1所示:
图 26.3.1 扩展口接口原理图 由上图可知,扩展口的引脚和开发板上的部分外设是复用的,因此当扩展口上连接双目OV5640摄像头时,开发板上复用至扩展口上的外设将不能使用。需要注意的是,UART2_TX、UART2_RX、CAN_TX和CAN_RX也是复用至扩展口上的,因此需要拿掉开发板上P3和P1排针上的跳帽,否则会影响双目OV5640摄像头的使用。 ATK-Dual-OV5640是正点原子推出的一款双目OV5640摄像头,该模块通过2*20排母(2.54mm间距)同外部连接,连接时将双目摄像头的排母直接插在开发板上的扩展口即可,模块外观如图31.3.2所示: 图 26.3.2 双目OV5640模块 由于双目OV5640的引脚较多,这里不再给出引脚分配列表,在硬件设计的最后给出了本次实验管脚分配的约束代码,大家在搭建工程时可以直接拷贝。 我们在前面的例程中实现了单目OV5640摄像头LCD屏的实时显示,本章我们在此基础上,增加了一路摄像头的图像采集与显示。Xilinx官方提供的OSD(On Ccreen Display)最大支持8层外部视频的输入(来自帧缓冲区或者AXI4数据流接口视频)和内部图形控制器(如:文本控制),可以很好的帮助我们实现这一功能。OSD IP核的输入接口为AXI4-Stream Slave Interface,输出接口为AXI4-Stream Master Interface,因此输入接口可以直接和VDMAIP核连接,输出接口和AXI4-Stream to Video Out IP核连接。本次实验的系统框图如下图所示: 图 26.3.3 系统框图 由于本次实验增加了一路OV5640摄像头的图像采集与显示,因此框图中增加了OV5640图像采集IP核、VideoIn to AXI4-Stream IP核和VDMA IP核,两个VDMA IP核连接至OSD IP核,然后将OSD IP核的输出接口连接至AXI4-Stream to Video Out IP核的输入接口。另外,本次实验还需要对新增的一路摄像头进行配置,SCCB配置驱动同样是在PS中完成。 需要注意的是,在“OV5640摄像头LCD显示”的例程中,通过AXI SmartConnect IP核将一个(或多个)AXI存储器映射的主器件连接到一个(或多个)存储器映射的从器件,由于AXISmartConnect IP核和OSD等IP核会消耗较多的逻辑资源,甚至超出了ZYNQ-7010芯片的最大逻辑单元,因此这里统一将AXI SmartConnectIP核替换成AXI Interconnect IP核,以节省逻辑单元。 最终图中的数据流走向为:OV5640图像采集是我们自定义的IP核,负责将OV5640的数据转成视频流数据;视频流数据经过Video in to AXI4-Stream IP核转换成AXI4-Stream IP格式数据流,然后通过VDMA的写通道转成AXI4 Memory Map格式,并最终写入DDR内存中。VDMA通过AXISmartconnect IP核与AXI_HP端口进行连接,从而高效访问DDR3。由于本次实验是对双目OV5640摄像头进行图像采集和显示,因此OV5640图像采集IP核、VideoIn to AXI4-Stream IP核和VDMA IP核需要添加两次。 两路VDMA输出的AXI4-Stream格式的图像数据连接至OSD IP核,经OSD IP核拼接和叠加字符后,连接至AXI4-Stream to Video Out IP核。AXI4-Stream to Video Out IP核在VTC IP核的控制下,把AXI4-Stream格式的数据转换成视频输出的数据格式(如RGB888),并将输出的视频数据流连接至RGB2LCD IP核(rgb2lcd)的输入端。RGB2LCD IP核是本次实验自定义的IP核,实现了获取LCD屏的ID,以及将LCD屏的引脚封装到总线接口上,以方便将LCD引脚引出至顶层模块端口上。 本次实验的硬件平台在“OV5640摄像头LCD显示”实验基础上搭建。由于本次实验需要对两个摄像头的SCCB接口进行配置,因此ZYNQ7处理系统中的EMIO引脚的个数由2改为4,如下图所示: 图 26.3.4 修改EMIO GPIO位宽 接下来按照系统框图中的架构,依次添加OV5640图像采集IP核、VideoIn to AXI4-Stream IP核和VDMA IP核,如下图所示: 图 26.3.5 添加IP核 这三个IP核的配置和框图里已经存在的三个IP核保持一致。 接下来添加OSD IP核,点击框图界面上方的“ADD IP”按钮,在弹出的IP目录中搜索“OSD”,最后双击搜索结果中的“AXI GPIO”将其添加到设计中,如下图所示: 图 26.3.6 添加OSD IP核 双击打开新添加的OSD IP核,对OSD IP核进行配置,弹出的界面如下图所示: 图 26.3.7 OSD IP核配置界面 IncludeAXI4-Lite Interface:可选的配置接口,处理器可以通过AXI4-Lite接口实现对OSD的动态配置。由于本次实验需要根据不同分辨率的LCD屏修改OSD IP核的分辨率,并且需要配置OSD IP核来叠加字符,因此这里勾选这个配置接口。 Include INTCInterface:可选的中断接口,如果勾选这个选项,OSD IP核将会产生一个INTC_IF端口,这个端口提供了对中断状态的并行访问,包括帧处理的状态和产生错误的条件等。本次实验不需要对OSD IP核的状态进行监控,因此这里不勾选。 Video Format:视频格式,本次实验的视频格式为RGB888。先点击Video Format左侧的AUTO按钮将其切换至MANUAL,然后将视频格式修改为RGB。 Video ComponentWidth:视频组件宽度,这里可以理解为RGB颜色分量的位宽,本次实验的颜色分量位宽为8位。先点击Video Component Width左侧的AUTO按钮将其切换至MANUAL,然后将颜色分量位宽修改为8。 Number of Layers:图层的数量,范围是1~8。本次实验需要显示两路摄像头的视频数据,且每路视频上各叠加一组字符显示,因此图层的数量配置为4。 Maximum ScreenWidth:OSD最大屏幕分辨率的宽度,范围是128~4095。本次实验连接的最大分辨率的屏幕为10.1寸LCD屏,分辨率为1280*800,因此这里设置为1280。 LayerConfiguration:各图层的类型,External AXIS指外部输入的AXI4-Stream数据流,Internal Graphics Controller指内部的图形控制器,用于叠加字符或者方框等。本次实验显示两路摄像头+两路字符叠加,因此LAYER0 Type和LAYER1 Type为External AXIS,LAYER2 Type和LAYER3 Type为Internal Graphics Controller。当LAYER2 Type和LAYER3 Type修改为Internal Graphics Controller之后,标签配置页会多出LAYER 2 Options和LAYER 3 Options。 接下来点击ScreenLayout Options标签页,配置如下图所示: 图 26.3.8 OSD IP核图层选项配置 Screen LayoutOptions标签页的选项这里全部保持默认,这是因为这些配置我们都可以在PS软件中通过AXI4-Lite接口进行修改,这里介绍下各个选项的含义。 Background Size:背景分辨率。 Background Color:背景颜色。需要说明的是,OSD的背景颜色是按照RBG来显示的,而我们给OSD 的数据是按RGB的数据格式,因此当背景颜色设置成绿色(G:255 B:0 R:0)时,实际上背景颜色显示的蓝色。本次实验两路视频数据全部填充了LCD屏,因此看不到背景颜色。 界面最下面分别对四个图层进行设置,依次为: HorizontalPosition:各图层的水平起始位置; VerticalPosition:各图层的垂直起始位置; Width:各图层的宽度; Height:各图层的高度; Layer Priority:图层的优先级显示,高优先级的图层显示在低优先级图层之上; Layer Enable:配置图层的使能状态; Global AlphaValue:透明度; Global AlphaEnable:配置透明度使能状态。 接下来点击Layout 2Options标签页,配置如下图所示: 图 26.3.9 OSD IP核LAYER 2配置 图层2配置选项,只有Internal Graphics Controller类型的图层才会有此选项。这里仅介绍该页面修改的两个配置参数。 Number ofCharacters:内部字体RAM中的字符个数,这里设置为128,否则字符显示不支持小写字母; ASCII Offset:第一个ASCII码位于字体RAM中的位置,这里设置为0,否则在叠加字符显示时出现错位的现象。 其余选项保持默认即可。 接下来点击Layout 3Options标签页,配置和Layout 2 Options保持一致,如下图所示: 图 26.3.10 图 OSD IP核LAYER 3配置 最后点击OK按钮,完成OSD IP核的配置。 我们在前面讲过,AXISmartConnect IP核(axi_smc)会消耗较多的逻辑资源,因此我们这里将AXI SmartConnect IP核替换成AXI Interconnect IP核(如果大家使用的是ZYNQ-7020核心板,由于XC7Z020逻辑资源充足,也可不替换;如果使用ZYNQ-7010核心板,则必须替换,否则超过XC7Z010的逻辑资源,编译会报错)。先将图31.3.11的AXISmartConnect IP核删除,如下图所示: 图 26.3.11 删除AXI SmartConnect IP核 然后添加AXIInterconnect IP核,点击框图界面上方的“ADD IP”按钮,在弹出的IP目录中搜索“axi inter”,最后双击搜索结果中的“AXI Interconnect”将其添加到设计中,如下图所示: 图 26.3.12 添加 AXI Interconnect IP核 双击打开新添加的AXIInterconnect IP核,对AXI Interconnect IP核进行配置,弹出的界面如下图所示: 图 26.3.13 AXI Interconnect配置 AXI InterconnectIP核用于将一个(或多个)AXI存储器映射的主器件连接到一个(或多个)存储器映射的从器件。本次实验共添加两个VDMA IP核,每个VDMA IP核开启了一个写通道和一个读通道,因此将从器件设置为4,主器件和PS的HP接口连接,主器件个数设置为1。 接下来开始连线。由于VDMAIP核输出的AXI-Stream需要先经过OSD IP核,再输出给AXI4-Stream to Video Out IP核,因此原先VDMA IP核和AXI4-Stream to Video Out IP核连接的线需要删除,如下图所示: 图 26.3.14 删除连线 将axi_vdma_0 IP核的M_AXIS_MM2S端口和v_osd_0 IP核的video_s0_in端口连接;将v_osd_0 IP核的video_out端口axi4s_vid_out_0 IP 核的video_in端口连接,如下图所示: 图 26.3.15 连接OSD IP核端口 新增加的OV5640图像采集IP核、Video In to AXI4-Stream IP核和VDMA IP核,连线同已经存在的这三个IP核类似,这里需手动连线部分端口,连线完成后如下图所示: 图 26.3.16 新增的IP核手动连线 接下来点击图31.3.17箭头指向的位置,让软件自动完成剩余连线。如果自动连线后,仍然出现这个自动连线的提示,则继续点击。 图 26.3.17 自动连线 接下来引出新增的OV5640图像采集IP核(ov5640_capture_data_1)的端口信号,为了和ov5640_capture_data_0 IP核引出的端口进行区分,为端口信号添加后缀名“_0”和“_1”进行区分。先重命名ov5640_capture_data_0IP核引出的全部端口信号,端口后缀名添加“_0”,如下图所示: 图 26.3.18 v5640_capture_data_0 IP核端口重命名 将ov5640_capture_data_1IP核的端口引出,在选择端口后,右键点击“Make External”,此时软件会自动为端口添加后缀名“_1”,引出所有端口后,如下图所示: 图 26.3.19 v5640_capture_data_1 IP核端口引出 整体系统框图,如下图所示: 图 26.3.20 整体系统框图 到这里我们的BlockDesign就设计完成了,在Diagram窗口空白处右击,然后选择“Validate Design”验证设计。验证完成后弹出对话框提示“ValidationSuccessful”表明设计无误,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。 接下来在Source窗口中右键点击Block Design设计文件“system.bd”,然后依次执行“Generate Output Products”和“Create HDLWrapper”。 为工程添加约束文件,约束文件如下: 管脚分配完成后按快捷键Ctrl+S保存管脚约束。 最后在左侧FlowNavigator导航栏中找到PROGRAM AND DEBUG,点击该选项中的“Generate Bitstream”,对设计进行综合、实现、并生成Bitstream文件。 在生成Bitstream之后,在菜单栏中选择 File > Export > Export hardware导出硬件,并在弹出的对话框中,勾选“Include bitstream”。然后在菜单栏选择File > LaunchSDK,启动SDK软件。 1.4 软件设计在 软 件 设 计 部 分 中 ,和“OV5640摄像头LCD显示”实 验 相 比, 源 文 件 下 新 增 了osd文件,实现了对OSD IP核配置的功能。需要说明的是,osd文件夹下的osd.c和osd.h是从Xilinx官方提供的xapp742应用文档下的源码xapp742.zip移植而来,本次实验对这些函数做了部分的修改。除此之外,由于本次实验需要分别对两个摄像头进行配置,因此对其它文件中的源码也进行了修改。 main文件的代码如下所示: - #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "xil_types.h"
- #include "xil_cache.h"
- #include "xparameters.h"
- #include "xgpio.h"
- #include "xaxivdma.h"
- #include "xaxivdma_i.h"
- #include "display_ctrl/display_ctrl.h"
- #include "vdma_api/vdma_api.h"
- #include "emio_sccb_cfg/emio_sccb_cfg.h"
- #include "ov5640/ov5640_init.h"
- #include "osd/osd.h"
- //宏定义
- #define FRAME_BUFFER_NUM 3 //帧缓存个数3
- #define DYNCLK_BASEADDR XPAR_AXI_DYNCLK_0_BASEADDR //动态时钟基地址
- #define VDMA_ID XPAR_AXIVDMA_0_DEVICE_ID //VDMA器件ID
- #define DISP_VTC_ID XPAR_VTC_0_DEVICE_ID //VTC器件ID
- //PL端 AXI GPIO 0(lcd_id)器件 ID
- #define AXI_GPIO_0_ID XPAR_AXI_GPIO_0_DEVICE_ID
- //使用AXI GPIO(lcd_id)通道1
- #define AXI_GPIO_0_CHANEL 1
- //全局变量
- //frame buffer的起始地址
- unsigned int frame_buffer_addr[DISPLAY_VDMA_NUM];
- unsigned int vdma_id[DISPLAY_VDMA_NUM] = {XPAR_AXIVDMA_0_DEVICE_ID,
- XPAR_AXIVDMA_1_DEVICE_ID};
- XAxiVdma vdma[DISPLAY_VDMA_NUM];
- DisplayCtrl dispCtrl;
- XGpio axi_gpio_inst; //PL端 AXI GPIO 驱动实例
- VideoMode vd_mode;
- unsigned int lcd_id;
- int main(void)
- {
- u32 status0,status1;
- u16 cmos_h_pixel; //ov5640 DVP 输出水平像素点数
- u16 cmos_v_pixel; //ov5640 DVP 输出垂直像素点数
- u16 total_h_pixel; //ov5640 水平总像素大小
- u16 total_v_pixel; //ov5640 垂直总像素大小
- //获取LCD的ID
- XGpio_Initialize(&axi_gpio_inst, AXI_GPIO_0_ID);
- lcd_id = LTDC_PanelID_Read(&axi_gpio_inst,AXI_GPIO_0_CHANEL);
- xil_printf("lcd_id = %x\r\n",lcd_id);
- //根据获取的LCD的ID号来进行ov5640显示分辨率参数的选择
- switch(lcd_id){
- case 0x4342 :
- cmos_h_pixel = 480;
- cmos_v_pixel = 272;
- total_h_pixel = 1800;
- total_v_pixel = 1000;
- break;
- case 0x4384:
- cmos_h_pixel = 800;
- cmos_v_pixel = 480;
- total_h_pixel = 1800;
- total_v_pixel = 1000;
- break;
- case 0x7084:
- cmos_h_pixel = 800;
- cmos_v_pixel = 480;
- total_h_pixel = 1800;
- total_v_pixel = 1000;
- break;
- case 0x7016 :
- cmos_h_pixel = 1024;
- cmos_v_pixel = 600;
- total_h_pixel = 2200;
- total_v_pixel = 1000;
- break;
- case 0x1018 :
- cmos_h_pixel = 1280;
- cmos_v_pixel = 800;
- total_h_pixel = 2570;
- total_v_pixel = 980;
- break;
- default :
- cmos_h_pixel = 480;
- cmos_v_pixel = 272;
- total_h_pixel = 1800;
- total_v_pixel = 1000;
- break;
- }
- emio_init(); //初始化EMIO
- status0 = ov5640_init(CMOS_CH0,cmos_h_pixel,//初始化ov5640 0
- cmos_v_pixel,
- total_h_pixel,
- total_v_pixel);
- status1 = ov5640_init(CMOS_CH1,cmos_h_pixel,//初始化ov5640 1
- cmos_v_pixel,
- total_h_pixel,
- total_v_pixel);
- if(status0 == 0 && status1 == 0)
- xil_printf("Dual OV5640 detected successful!\r\n");
- else
- xil_printf("Dual OV5640 detected failed!\r\n");
- //根据获取的LCD的ID号来进行video参数的选择
- switch(lcd_id){
- case 0x4342 : vd_mode = VMODE_480x272; break;
- case 0x4384 : vd_mode = VMODE_800x480; break;
- case 0x7084 : vd_mode = VMODE_800x480; break;
- case 0x7016 : vd_mode = VMODE_1024x600; break;
- case 0x1018 : vd_mode = VMODE_1280x800; break;
- default : vd_mode = VMODE_800x480; break;
- }
- for(u8 i=0;i<DISPLAY_VDMA_NUM;i++){
- frame_buffer_addr[i] = XPAR_PS7_DDR_0_S_AXI_BASEADDR+0x1000000*i;
- //配置VDMA
- run_vdma_frame_buffer(&vdma[i], vdma_id[i], vd_mode.width/2, vd_mode.height,
- frame_buffer_addr[i],0,0,BOTH);
- }
- //OSD初始化
- osd_init(lcd_id,vd_mode.width,vd_mode.height);
- //初始化Display controller
- DisplayInitialize(&dispCtrl, DISP_VTC_ID, DYNCLK_BASEADDR);
- //设置VideoMode
- DisplaySetMode(&dispCtrl, &vd_mode);
- DisplayStart(&dispCtrl);
- return 0;
- }
复制代码本次实验使用了两个VDMA,因此宏定义DISPLAY_VDMA_NUM的值等于2。在代码的第28行将帧缓存起始地址声明了数组的形式,共两个元素,分别表示VDMA 0和VDMA 1的帧缓存起始地址。 在main函数中,同样是先获取LCD屏的ID,然后根据LCD屏的ID来对摄像头进行初始化,并选择VIDEO的参数,初始化函数为ov5640_init()。本次实验对ov5640_init()函数进行了修改,为函数增加了一个输入参数,用于表示对哪个摄像头进行初始化,并将检测双目OV5640摄像头的结果值打印出来,如程序中第46行至第113行代码所示。 在程序的第115行至第120行代码中,首先为每个VDMA分配帧缓存起始地址,然后配置VDMA。需要注意的是,VDMA帧缓存的宽度进行了除以2的处理,这是因为两路摄像头的图像数据同时显示在LCD屏上,单路摄像头图像数据只显示一半,因此这里对VDMA帧缓存的宽度进行了除2的处理。 在程序的第122行代码中,通过osd_init()函数对OSD进行配置以及初始化。输入的参数分别为LCD ID,OSD输出分辨率的宽度和高度,这里的LCD ID主要用于调节叠加字符的大小。 在程序的第124行至第127行代码中,通过DisplayInitialize()函数对显示相关的IP核进行初始化,包括VTC和动态时钟配置;DisplaySetMode函数设置VTC输出的分辨率;最后通过DisplayStart()函数启动VTC开始工作。 OSD的配置和初始化是本次实验的重点,由osd_init()函数实现,源代码如下: - //OSD初始化
- void osd_init(u16 lcd_id,int screen_width,int screen_height)
- {
- u8 text_size = 1; //字符文本放大比例,范围1、2、4、8
- OsdCfgPtr = XOSD_LookupConfig(OSD_DEVICE_ID);
- XOSD_CfgInitialize(&Osd, OsdCfgPtr, OsdCfgPtr->BaseAddress); //初始化OSD IP核
- XOSD_RegUpdateEnable(&Osd); //更新OSD寄存器配置使能
- XOSD_Enable(&Osd); //使能OSD IP核
- osd_layer_cfg(screen_width,screen_height); //OSD图层配置
- if(lcd_id == 0x4342)
- text_size = 2; //4.3'屏,放大2倍
- else
- text_size = 4; //其它屏,放大4倍
- //参数:XOSD实例,图层索引,X方向起始位置,Y方向起始位置,颜色索引,文本索引,文本大小
- //图层2显示文本设置
- osd_draw_text(Osd,2,screen_width/10,10,11,0,text_size);
- //图层3显示文本设置
- osd_draw_text(Osd,3,screen_width/2+screen_width/10,10,11,1,text_size);
- }
复制代码OSD初始化函数实现了对OSD的配置并使能OSD。在程序的第181行至第184行代码,为Xilinx官方提供的OSDAPI函数,实现了对OSD的配置初始化、更新OSD寄存器配置使能和使能OSD。在程序的第185行代码中,osd_layer_cfg()函数是我们自己编写的函数,用于对OSD的图层进行配置,包括配置OSD输出分辨率、各个图层的配置以及内部图形控制器(字体和颜色)配置等。在程序的第193行至第195行代码中,通过osd_draw_text()函数分别在图层2和图层3中显示字符串。 OSD图层的配置函数代码如下: - //OSD图层配置
- void osd_layer_cfg(int screen_width,int screen_height)
- {
- //配置OSD输出分辨率
- XOSD_SetScreenSize(&Osd, screen_width, screen_height);
- xil_printf("OSD output resolution is %d,%d\r\n",screen_width,screen_height);
- //配置OSD背景色 颜色参数:RED BLUE GREEN
- XOSD_SetBackgroundColor(&Osd,0x00,0xff,0x00);
- //配置图层0:外部视频
- OSD_LCfg = OSD_Layer0;
- XOSD_SetLayerAlpha(&Osd,OSD_LCfg.index,OSD_LCfg.alpha_enable,OSD_LCfg.alpha_value);
- XOSD_SetLayerPriority(&Osd,OSD_LCfg.index,OSD_LCfg.priority);
- XOSD_SetLayerDimension(&Osd,OSD_LCfg.index,0,0,screen_width/2,screen_height);
- XOSD_EnableLayer(&Osd,OSD_LCfg.index);
- //配置图层1:外部视频
- OSD_LCfg = OSD_Layer1;
- XOSD_SetLayerAlpha(&Osd,OSD_LCfg.index,OSD_LCfg.alpha_enable,OSD_LCfg.alpha_value);
- XOSD_SetLayerPriority(&Osd,OSD_LCfg.index,OSD_LCfg.priority);
- XOSD_SetLayerDimension(&Osd,OSD_LCfg.index,screen_width/2,
- 0,screen_width/2,screen_height);
- XOSD_EnableLayer(&Osd,OSD_LCfg.index);
- //配置图层2:内部字符显示
- OSD_LCfg = OSD_Layer2;
- XOSD_SetLayerAlpha(&Osd,OSD_LCfg.index,OSD_LCfg.alpha_enable,OSD_LCfg.alpha_value);
- XOSD_SetLayerPriority(&Osd,OSD_LCfg.index,OSD_LCfg.priority);
- XOSD_SetLayerDimension(&Osd,OSD_LCfg.index,0,0,screen_width,screen_height);
- XOSD_EnableLayer(&Osd,OSD_LCfg.index);
- //向OSD图像控制器BANK 0中加载颜色表
- XOSD_LoadColorLUTBank(&Osd,OSD_LCfg.index, 0, color_arr);
- //向OSD图像控制器BANK 0中加载字符(字体)
- XOSD_LoadCharacterSetBank(&Osd,OSD_LCfg.index, 0, (u32 *)Font);
- //向OSD图像控制器BANK 0中加载文本
- XOSD_LoadTextBank(&Osd,OSD_LCfg.index, 0, (u32 *)TextData);
- //选择OSD图像控制器的有效BANK(BANK 0)
- XOSD_SetActiveBank(&Osd,OSD_LCfg.index, 0, 0, 0, 0);
- //配置图层3:内部字符显示
- OSD_LCfg = OSD_Layer3;
- XOSD_SetLayerAlpha(&Osd,OSD_LCfg.index,OSD_LCfg.alpha_enable,OSD_LCfg.alpha_value);
- XOSD_SetLayerPriority(&Osd,OSD_LCfg.index,OSD_LCfg.priority);
- XOSD_SetLayerDimension(&Osd,OSD_LCfg.index,0,0,screen_width,screen_height);
- XOSD_EnableLayer(&Osd,OSD_LCfg.index);
- //向OSD图像控制器BANK 0中加载颜色表
- XOSD_LoadColorLUTBank(&Osd,OSD_LCfg.index, 0, color_arr);
- //向OSD图像控制器BANK 0中加载字符(字体)
- XOSD_LoadCharacterSetBank(&Osd,OSD_LCfg.index, 0, (u32 *)Font);
- //向OSD图像控制器BANK 0中加载文本
- XOSD_LoadTextBank(&Osd,OSD_LCfg.index, 0, (u32 *)TextData);
- //选择OSD图像控制器的有效BANK(BANK 0)
- XOSD_SetActiveBank(&Osd,OSD_LCfg.index, 0, 0, 0, 0);
- xil_printf("OSD config done!\r\n" );
- }
复制代码OSD图层配置函数的输入参数为LCD屏的显示分辨率,根据输入的参数来配置OSD的分辨率,如程序中第202行代码所示,由XOSD_SetScreenSize()函数实现;而配置OSD背景颜色由XOSD_SetBackgroundColor()函数实现。 程序中第206行至第211行代码根据图层0的配置参数对图层0进行配置。OSD_LCfg是用于图层配置参数的结构体变量,其结构体的定义如下: 图 26.4.1 OSD_LayerCfg结构体 结构体中各成员分别表示图层索引、图层透明度、图像优先级和透明度使能。OSD_Layer0是OSD_LCfg结构体变量中图层0各成员的配置初始值,定义如下: 图 26.4.2 OSD图层0配置参数 这里需要说明的是,在程序的第210行代码中,XOSD_SetLayerDimension()函数用于配置图层0的显示位置和尺寸大小。配置图层0显示的水平方向位置和垂直方向位置都是0,而水平方向宽度和垂直方向宽度分别为screen_width/2和screen_height。因此,图层0的显示位置位于LCD屏左侧的位置,尺寸大小占LCD屏分辨率的一半。 其它图层的配置与图层0类似,这里不再赘述。需要注意的是,由于图层2和图层3的类型为内部图形控制器,因此需要对这两个图层额外配置字体颜色、显示的字符串和字体。图层2的内部图形控制器配置函数如代码中第225行至第232行代码所示。 字体颜色表、显示的字符串和字体的定义如下: - //Color table definition
- u32 color_arr[16] = {
- 0x00000000, 0xa0a25f58, 0xa08080ff, 0xa0808010,
- 0xa0ef5a51, 0x00000000, 0xa0465289, 0x00000000,
- 0xa065ba6b, 0x00000000, 0xa09017c5, 0xa0a9c860,
- 0xa0bc3198, 0xa010a5a9, 0xa0808080, 0xa0ada1ab
- };
-
- //Text table definition
- char __attribute__ ((aligned (4))) TextData[8][32] = {
- "OV5640 1", //"String #1",
- "OV5640 2", //"String #2",
- "Hello",
- "Xilinx",
- "String #5",
- "String #6",
- "String #7",
- "String #8"
- };
-
- //Font definition
- unsigned char __attribute__ ((aligned (4))) Font[128][8] = {
- {0x00, 0x36, 0x7F, 0x7F, 0x3E, 0x1C, 0x08, 0x00}, // NULL
- {0x18, 0x18, 0x18, 0x1F, 0x1F, 0x18, 0x18, 0x18},
- 代码较长,省略部分源代码……
- {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // 127 DEL
- };
复制代码这些字体颜色表、显示的字符串和字体由官方提供的xapp742应用文档下的源码移植而来。其中数组颜色表color_arr的每一位元素由32位组成,其中高8位表示了颜色的透明度,低24位为RGB888颜色格式。颜色表中前一个元素用于表示字体颜色的前景色,后一个元素表示颜色表的背景色。 TextData数组存储了需要叠加显示的字符,两路摄像头显示的字符串分别为“OV5640 1”和“OV5640 2”。如果想要修改LCD屏显示的字符,可以直接修改TextData数组元素的字符串。 Font数组存储了字符显示的字体,每一行表示一个字符。字符和像素的对应关系如图31.4.3所示。 图 26.4.3 字符'A' 每一个字符所占的像素为8*8,通过为每一个像素赋值前景色和背景色,即可显示出字符。TextData数组中的元素是字符串,字符串中的每一个字符对应一个ASCII码,ASCII码对应的十进制为0~127,分别索引Font数组的第一行至第128行。 osd_draw_text()函数用于设置图层显示的字符值、字符位置和大小,函数源代码如下: - void osd_draw_text(XOSD Osd,int layer_index,int x_start, int y_start,
- int color_index, int text_index, int text_size)
- {
- u32 instruction[XOSD_INS_SIZE];
- u16 ins_opcode_type = XOSD_INS_OPCODE_TXT;
- u8 txt_size = (text_size<<4); //高4位表示文本放大的倍数
- //创建OSD指令
- XOSD_CreateInstruction(&Osd, instruction, layer_index,ins_opcode_type,txt_size,
- x_start, y_start, x_start, y_start,text_index, color_index);
- //加载指令
- XOSD_LoadInstructionList(&Osd, layer_index, 0, instruction, 1);
- xil_printf("OSD Layer %d draw text done!\r\n",layer_index);
- }
复制代码osd_draw_text()函数输入的参数分别为XOSD实例、图层索引、X方向起始位置、Y方向起始位置、颜色索引、文本索引和文本大小。该函数通过通过创建一个OSD指令和加载指令,对该图层显示的字符进行配置。 1.5 下载验证首先我们将下载器与领航者底板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用Mini USB连接线将USB UART接口与电脑连接,用于串口通信。使用FPC排线一端与RGB LCD液晶屏上的接口连接,另一端连接领航者底板上的RGB TFT-LCD接口。双目OV5640摄像头连接底板上的扩展口连接(扩展口位于ATK_MODULE旁边,连接时双目摄像头镜头方向朝外),如所示。最后连接开发板的电源,并打开电源开关。 图 26.5.1 双目摄像头镜头方向朝外 在SDK软件下方的SDK Terminal窗口中点击右上角的加号设置并连接串口。然后下载本次实验硬件设计过程中所生成的BIT文件,来对PL进行配置。最后下载软件程序,下载完成后,在下方的SDK Terminal中可以看到应用程序打印的信息,打印的信息如下图所示: 图 26.5.2 串口终端中打印的信息 从串口打印的信息可以看出硬件连接的状态和OSD配置的状态。此时LCD屏实时显示两路摄像头的视频图像,并且在每一路摄像头的上方叠加了字符串,分别为“OV5640 1”和“OV5640 2”,LCD屏显示画面如下图所示: 图 26.5.3 LCD屏显示画面
|