[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA_SDK高级篇连载-04视频图形显示方案(VDMA/HDMI)

文档创建者:FPGA课程
浏览次数:242
最后更新:2024-09-29
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-SOC » 1_SDK应用方案(仅旗舰型号) » 2-SDK高级应用方案
​ 软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!




1概述
本文对 Xilinx 提供的一款 IP 核—AXI VDMA(Video Direct Memory Access) 进行详细讲解,首先分析 VDMA 应用意义;然后详细介绍 VDMA 的特点、寄存器作空间;最后阐述如何使用 VDMA,包括 IP 核的配置方法、代码编写流程等。
VDMA IP可以从PS DDR中读取数据,然后以AXI-Stream流的方式输出。虽然使用DMA和FDMA也可以从PS DDR中读取数据,但是使用VDMA输出的优势在于LINUX下使用的时候,具有现成的驱动可以使用。
本文在PS DDR中开辟了一段缓存地址用于VDMA的视频缓存,通过PS的ARM写入测试图形数据,测试图形就会在显示器或者液晶屏上显示。
PS DDR中的数据也可以通过HDMI通道输出,考虑到2CG板卡并没有自带的HDMI输出接口,所以本文仅介绍使用VDMA输出至LCD屏以及HDMI输出两种输出方式。如果用户在学习的过程中想使用HDMI输出,可以参考我们的教程购买HDMI子卡自行修改Demo。
本文实验目的:
1:掌握基于VDMA IP的图像显示系统的搭建方法可以参考“附录2”
2:了解点缓存方式图像的撕裂问题
3:掌握VDMA polled模式的基本使用
4:掌握米联客 HDMI IP ZYNQ 中的使用
5:掌握米联客7寸液晶屏驱动显示
2系统框图
如下图所示,该方案中 PS CPU DDR 中绘制测试图形,VDMA 自主从 PS DDR 中读取绘制的图形数据,发送给 AXI4-Stream to Video Out IP(简称 VID OUT),VID OUT IP Stream 流数据转为 RGB 时序给 HDMI 输出 IP,这样就能驱动 HDMI 显示器了。
这里还用到了 Video Timing Controller IP(简称 VTC),IP 产生 RGB 视频时序,和VID OUT IP 配合实现 AXIStream 视频数据流转为 RGB 视频数据。
e580a54c37fd4637ba0b2b337c618b1e.jpg
如下图所示,该方案中PS 的CPU往DDR中绘制测试图形,VDMA自主从PS DDR中读取绘制的图形数据,发送给AXI4-Stream to Video Out IP(简称VID OUT),VID OUT IP 把Stream流数据转为RGB时序给LVDS输出IP,这样就能驱动LCD显示器了。
这里还用到了Video Timing Controller IP(简称VTC),该IP产生RGB视频时序,和VID OUT IP配合实现AXI-Stream视频数据流转为RGB视频数据。
米联客7寸液晶屏可以支持LVDS(必须修改BANK电压支持LVDS通信,如果有不清楚请咨询米联客技术支持)接口或者RGB接口,比如LVDS接口的系统框图中也是把RGB时序转为LVDS驱动液晶屏。
c36e455d55e64f53bec3d0d43029de2c.jpg
如果是使用RGB接口可以直接用RGB时序驱动液晶屏.
9a25dcac13ef4315a15dbec007b648f7.jpg
3硬件电路分析
硬件接口和子卡模块请阅读“附录1”
配套工程的FPGA PIN脚定义路径为soc_prj/uisrc/04_pin/ fpga_pin.xdc。
4搭建SOC系统工程
详细的搭建过程这里不再重复,对于初学读者如果还不清楚如何创建SOC工程的,请学习“3-1-01米联客2024版ZynqSocSDK入门篇”中第一个工程 “01Vitis Soc开发入门”这个实验。
4.1Zynq IP PS部分设置
本文中的PS设置内容是新增加的配置部分,关于DDR、MIO、CPU时钟等设置请参考“3-1-01米联客2024版ZynqSocSDK入门篇”中第一个工程 “01Vitis Soc开发入门”这个实验。
1:PS复位设置
ba7dd4d600e84206a8496992383767f6.jpg
2:设置 PS GT Master 接口和 HP Slave 接口
99e326922dad40b9b85677cabbeda0e6.jpg
3:设置PL到PS的中断
Interurptsà勾选 Fabric Interrupt,勾选IRQ_F2P[15:0]。
95c7363180364162abe8bae5984674e1.jpg
4:设置 PL 的时钟
勾选FCLK_CLK0,设置为100,即PS的PLL提供本系统的时钟100MHZ。
67d882b3e28446859eccbefaabdfbc87.jpg
5:ZYNQ IP设置完成后
下图中IRQ_F2P[0:0]会根据xconcat IP自动扩展
27a7a5e6479d442b95701a374c1fdd5f.jpg
4.2添加IP
以实现 HDMI 输出为演示 demo 说明,需要添加的 IP 如下。以下 IP 是已经完成配置后的形态:

406a2ca8819941febacd4f141a5ffb16.jpg
1:VDMA IP
AXI Video Direct Memory Access该IP简称VDMA IP 可以用于AXI-Stream流视频输入和输出,以下对使用到的配置参数做一些简单介绍,更多关于VDMA IP的介绍阅读“附录:2”介绍
本方案中只用到Read通道,因此只需要使能read 通道。
Address Width 设置AXI4总线可以访问的地址范围,对于32bit系统设置32即可
FrameBuffers 设置3,最大可以支持3帧缓存
Memory Map Data Width 用于设置AXI4接口的数据位宽,位宽越大通信速度越高,但是总线占用率也越高
Read Burst Size 设置读通道的AXI4 Burst长度
Stream Data Width 设置AXI4-Stream接口的数位宽,这里是32bit对齐
Line Buffer Depth设置FIFO大小,这里设置512
对于写通道的定义一样,这里没用到,也就不介绍了。
3615ab2209ff4bd6bf034e6725912b8a.jpg
这里写通道没有使用依然不设置,Fsync Options设置
62e7cef9699d41ca8317400a96204249.jpg
2:VTC IP
Video Timming Controller该IP简称VTC IP用于产生HS VS等视频时序给VID OUT IP用
VTC IP可以支持AXI-LITE接口通过PS动态修改分辨率,但是我们这里只设置固定的分辨率
788d953a8427481d983ee86246b5fe88.jpg
HDMI 液晶屏这里的分辨率设置 720P
aee40a8546644fc5877a5851e36cd85e.jpg
米联客的7寸液晶屏
分辨率是1024x600设置如下:
f0e1cde2168d489794c7b3ef8c28ce30.jpg
3:VIDOUT IP
AXI4-Stream to Video Out该IP简称VID OUT IP 用于把AXI-Stream 视频流转为RGB视频时序输出,该IP设置如下
Pixels Per Clock 设置1个像素多少个时钟
Video Format 设置为RGB
AX4S Video Input Component Width设置视频格式的数据位宽,对于RGB 888 这里是8
Native Video Output Component Width
主要参数设置RGB接口,FIFO深度1024,T
f1f7e31432b4467792f44ddb5d6ce039.jpg
4:uihdmitx IP
IP 为米联客自定义的用 FPGA 自带 serdes 完成 HDMI 输出的 IP
a4d941b528b94534b763345d31b147ff.jpg
5:AXI Interconnect IP
用于互联AXI4总线接口的IP,本方案用的2个interconnect ip 设置一样
a444eb8b72164dd09db1501f151924dd.jpg
6:Clocking Wizard IP
以下内容介绍 HDMI 显示器 720P 以及米联客 7 寸液晶屏的分辨率对应时钟设置
对于 HDMI 液晶屏,需要设置 2 个时钟,并且是 5 倍关系,设置 720P 分辨率时钟用 74.25M
e14ada4a957248a781a0164310c2466a.jpg
对于7寸液晶屏,分LVDS接口用法和RGB接口用法,1024*640分辨率RGB接口时钟设置如下:
50516b9803164669a8f7297652f11f57.jpg
对于7寸液晶屏,分LVDS接口用法和RGB接口用法,1024*640分辨率LVDS接口时钟设置如下:
374329508e5647b693f20c9c97959d17.jpg
4.3PL图形编程
1:7寸液晶RGB接口
FPGA IO BANK 电压为 3.3V 使用 FEP-BASE-CARD-3.3V 接口卡
c0e36148695547a88ab4175f3f1e06de.jpg
2:7寸液晶LVDS接口
FPGA IO BANK  电压为 2.5V 或者 1.8V,需要匹配使用 2.5V 的 FEP-BASE-CARD-2.5V 接口卡或者 1.8V 的 FEP- BASE-CARD-1.8V 接口卡
a24fed5332894b52867ccde5c3c02033.jpg
3:HDMI 输出编程
7925aff4aa2243b4866409d86060c917.jpg
4地址空间分配
ef59b02516c7403dbcecce5d81bb7aaa.jpg
5编译并导出平台文件
1:单击Block文件à右键àGenerate the Output ProductsàGlobalàGenerate。
2:单击Block文件à右键à Create a HDL wrapper(生成HDL顶层文件)àLet vivado manager wrapper and auto-update(自动更新)。
3:生成Bit文件。
4:导出到硬件: FileàExport HardwareàInclude bitstream
5:导出完成后,对应工程路径的soc_hw路径下有硬件平台文件:system_wrapper.xsa的文件。根据硬件平台文件system_wrapper.xsa来创建需要Platform平台。
8edb8c20167a46f8aaea9ad4d50d4fb7.jpg
5搭建Vitis-sdk工程
创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。
5.1创建SDK Platform工程
0aef016963c842cb9ae8df2111be12fe.jpg
5.2创建vdma APP测试工程
Vdma_out_test APP工程为单缓存方式
e84d0abcec304001bc4d3f865fc4c004.jpg
6程序分析
本方案路径下也提供了7寸液晶屏的方案源码。
6.1vdma_out_test程序
  1. /********************MILIANKE**************************
  2. *Company : MiLianKe Electronic Technology Co., Ltd.
  3. *WebSite:https://www.milianke.com
  4. *TechWeb:https://www.uisrc.com
  5. *tmall-shop:https://milianke.tmall.com
  6. *jd-shop:https://milianke.jd.com
  7. *taobao-shop1: https://milianke.taobao.com
  8. *Create Date: 2021/10/15
  9. *File Name: vdma_out_test.c
  10. *Description:
  11. *Declaration:
  12. *The reference demo provided by Milianke is only used for learning.
  13. *We cannot ensure that the demo itself is free of bugs, so users
  14. *should be responsible for the technical problems and consequences
  15. *caused by the use of their own products.
  16. *Copyright: Copyright (c) MiLianKe
  17. *All rights reserved.
  18. *Revision: 1.0
  19. ****************************************************/
  20. #include "xil_exception.h"
  21. #include "xil_printf.h"
  22. #include "xil_cache.h"
  23. #include "vdma_pl.h"
  24. #include "sleep.h"
  25. extern XAxiVdma VDMA; //定义VDMA
  26. extern XAxiVdma_DmaSetup WriteCfg; //定义VDMA写通道结构体
  27. extern XAxiVdma_DmaSetup ReadCfg; //定义VDMA
  28. u32 GFrame[BUFFERSIZE] __attribute__ ((__aligned__(256))); //定义图像缓存,自动分配地址
  29. int main()
  30. {
  31.         u8 mode_s =0;
  32.         u32 vcnt,hcnt;
  33.         u32 *ARGB = (u32*) GFrame;
  34.         sleep(1);
  35.         ReadCfg.FrameStoreStartAddr[0] = (UINTPTR)GFrame;//获取缓存地址,这里只设置1帧缓存
  36.         MM2S_ReadSetup(XPAR_AXIVDMA_0_DEVICE_ID, &VDMA, ReadCfg);//设置读通道参数
  37.         xil_printf("VDMA Generic Video start! r\n");
  38. //产生测试图形
  39.     while(1)
  40.     {
  41.             for(vcnt =0 ; vcnt < 720 ; vcnt++)
  42.             {//video color test
  43.                     for(hcnt = 0; hcnt < 1280; hcnt++)
  44.                     {
  45.                             switch(mode_s)
  46.                             {
  47.                             case 0: ARGB[hcnt+vcnt*1280] = 0x000000ff;break; //纯蓝
  48.                             case 1: ARGB[hcnt+vcnt*1280] = 0x0000ff00;break; //纯绿
  49.                             case 2: ARGB[hcnt+vcnt*1280] = 0x00ff0000;break; //纯红
  50.                             case 3: ARGB[hcnt+vcnt*1280] = 0x00ff0000;break; //纯红
  51.                             case 4: //彩条
  52.                                              if(hcnt<200 ) ARGB[hcnt+vcnt*1280] = 0x000000ff;
  53.                                 else if(hcnt<400 ) ARGB[hcnt+vcnt*1280] = 0x0000ff00;
  54.                                 else if(hcnt<600 ) ARGB[hcnt+vcnt*1280] = 0x00ff0000;
  55.                                 else if(hcnt<800 ) ARGB[hcnt+vcnt*1280] = 0x00ff00ff;
  56.                                 else if(hcnt<1000) ARGB[hcnt+vcnt*1280] = 0x0000ffff;
  57.                                 else if(hcnt<1200) ARGB[hcnt+vcnt*1280] = 0x00ffff00;
  58.                                 else if(hcnt<1400) ARGB[hcnt+vcnt*1280] = 0x00ffffff;
  59.                             break;
  60.                             case 5: //黑白方格
  61.                                              if((hcnt&0x10)^(vcnt&0x10))
  62.                                                      ARGB[hcnt+vcnt*1280] = 0x00000000;
  63.                                              else
  64.                                                      ARGB[hcnt+vcnt*1280] = 0xffffffff;
  65.                             break;
  66.                             default:mode_s = 0;break;
  67.                             }
  68.                     }
  69.             }
  70.             //更新cache 数据到DDR
  71.             Xil_DCacheFlushRange((u32)(GFrame), 1280*720*4);
  72.             if(mode_s <5 )        mode_s ++;
  73.             else                         mode_s =0;
  74.             sleep(1);
  75.     }
  76.     return XST_SUCCESS;
  77. }
复制代码

这个程序中定义了1个图像缓冲区,缓存区的大小是1280*720*4,由于我们在VDMA IP中设置了1个缓存,所以这里我们可以把上面定义的1个缓存的地址赋值给ReadCfg.FrameStoreStartAddr参数:
ReadCfg.FrameStoreStartAddr[0] = (UINTPTR)GFrame;//获取缓存地址,这里只设置1帧缓存
以下我们重点看下vdma_pl.c程序。
6.2vdma_pl程序分析
这个程序中2个函数MM2S_ReadSetup和S2MM_WriteSetup分别代表了VDMA读操作配置和VDMA写操作配置,这两个程序配置过程一样,我们这里也只使用到MM2S_ReadSetup函数用于初始化VDMA的读通道。
  1. int MM2S_ReadSetup(u16 DeviceID, XAxiVdma * InstancePtr , XAxiVdma_DmaSetup ReadCfg)
  2. {
  3.         int Status;
  4.         XAxiVdma_Config *Config;
  5.         ReadCfg.VertSizeInput = MM2S_V; /*设置视频图形的垂直像素大小*/
  6.         ReadCfg.HoriSizeInput = MM2S_H*4; /*设置视频图形的水平像素大小*/
  7.         ReadCfg.Stride = MM2S_H*4; /*设置视频图形的水平Stride大小*/
  8.         ReadCfg.FrameDelay = 0;  /*设置帧延迟*/
  9.         ReadCfg.EnableCircularBuf = 0; /* 是否使能circular 缓存模式 */
  10.         ReadCfg.EnableSync = 1;  /* 使能Gen-Lock模式 */
  11.         ReadCfg.PointNum = 1;    /* 与Master同步*/
  12.         ReadCfg.EnableFrameCounter = 0; /* 帧计数器使能 */
  13.         Config = XAxiVdma_LookupConfig(DeviceID);
  14.         if (NULL == Config) {
  15.                 xil_printf("XAxiVdma_LookupConfig failure\r\n");
  16.                 return XST_FAILURE;
  17.         }
  18. /* 从SDK参数定义中获取配置参数 */
  19.         Status = XAxiVdma_CfgInitialize(InstancePtr, Config, Config->BaseAddress);
  20.         if (Status != XST_SUCCESS) {
  21.                 xil_printf("XAxiVdma_CfgInitialize failure\r\n");
  22.                 return XST_FAILURE;
  23.         }
  24. /*设置VDMA寄存器*/
  25.         Status = XAxiVdma_DmaConfig(InstancePtr, XAXIVDMA_READ, &ReadCfg);
  26.         if (Status != XST_SUCCESS) {
  27.                 xil_printf("Read channel config failed %d\r\n", Status);
  28.                 return XST_FAILURE;
  29.         }
  30.         /* Set the buffer addresses for transfer in the DMA engine
  31.          * The buffer addresses are physical addresses
  32.          */
  33.         Status = XAxiVdma_DmaSetBufferAddr(InstancePtr, XAXIVDMA_READ,ReadCfg.FrameStoreStartAddr);
  34.         if (Status != XST_SUCCESS) {
  35.                 xil_printf("Read channel set buffer address failed %d\r\n", Status);
  36.                 return XST_FAILURE;
  37.         }
  38.         //start vdma
  39.         Status = XAxiVdma_DmaStart(InstancePtr, XAXIVDMA_READ);
  40.         if (Status != XST_SUCCESS) {
  41.                 xil_printf(
  42.                     "Start read transfer failed %d\r\n", Status);
  43.                 return XST_FAILURE;
  44.         }
  45.         return XST_SUCCESS;
  46. }
复制代码

1:参数配置
除了main函数中对地址空间的参数做了配置,以下几个参数分别是行像素大小、垂直像素大小、Stride参数大小。以及用于MM2S VDMA 控制寄存器中相关参数的初始化,包括延迟中断使能设置为0、Circular_Park模式设置0代表VDMA的帧地址无法自动切换,这种设置可以在1个缓存或者使用parked模式下使用。GenlockEn设置为1,由于这里只有VDMA读通道实际这个也起不到作用。
由于这里只有读通道,也不涉及2个VDMA通道之间的同步,所以帧延迟设置为0
e01590c2c275426382590540eb7b9818.jpg
2:Config = XAxiVdma_LookupConfig(DeviceID)函数
这个函数通过VDMA IP中配置获取VDMA的配置参数。
dca7e57577bd4d8c907162176f7451b1.jpg
fadb51c8e1264f449671a32efbe136f9.jpg
3:XAxiVdma_CfgInitialize(InstancePtr, Config, Config->BaseAddress)函数
以用Config参数来配置XDMA的指针参数。别看这么一堆参数,实际上就是赋值参数到VDMA指针,后面主要用VDMA指针来管理这些参数信息。
f7fb9df0b6244156891ae493cd00ce75.jpg

8e2fa22c3bcb4aae980468e72a00bb8b.jpg

7280329bb8cf42c1964fceeaee335383.jpg
4:XAxiVdma_DmaConfig(InstancePtr, XAXIVDMA_READ, &ReadCfg)函数
先追踪下这个函数,如下图:
37a562e376f84facbd9cf76d874fadb9.jpg
Channel = XAxiVdma_GetChannel(InstancePtr, Direction)函数其实干上面事情,就是判断下之前的XDMA参数中是否有设置读通道,利用XILINX的库函数就是有这种显得非常累赘做法。低效代码非常多。真要吐槽下。
f19e96e1ff624b3dae4047433af8642c.jpg
XAxiVdma_ChannelConfig(Channel, (XAxiVdma_ChannelSetup *)DmaConfigPtr)函数,这个函数中才有一些货真价实的东西。我们看下关键的三个寄存器,分别是MMS控制寄存器,MMS_Hsize寄存器,MMS_Stride寄存器

55f92886df224a4cb3a884c795553e93.jpg
a38461a0510d41dea9f3beaa66335dd1.jpg
5:XAxiVdma_DmaSetBufferAddr(InstancePtr, XAXIVDMA_READ,ReadCfg.FrameStoreStartAddr)函数
d8d150398ae448bca57ce65a4c0b3aaa.jpg
XAxiVdma_ChannelSetBufferAddr(Channel, BufferAddrSet,Channel->NumFrames)函数中设置帧缓存地址,这里最大支持3帧,我们前面的main函数也定义三帧图像的起始地址。
450ad0a13abe428692418d5ac4407514.jpg
6:XAxiVdma_DmaStart(InstancePtr, XAXIVDMA_READ)函数
这个函数启动VDMA传输,
3bfc9c701146448ebb40b491bb2bd677.jpg
XAxiVdma_ChannelStart(Channel)函数首选判断通道是否已经启动,通过写控制寄存器启动VDMA.
3e4d7ed5c16145fb9e4b43bfc3e6f3df.jpg
之后通过更新MMS_vsize寄存器,实现VDMA的MMS启动。这里注意MMS_vsize寄存器需要最后一个写。
5617c7fdf9414c6b965e9cf7ceca3038.jpg
7:XAxiVdma_StartParking(&VDMA, mode_s,XAXIVDMA_READ)函数
这个函数中通过PARK寄存器设置那一帧输出到MMS
973bb58311d14f659740da8f20c33530.jpg
7 HDMI方案演示
本实验需要用到 JTAG 下载器、USB 转串口外设,另外需要把核心板上的 2P 模式开关设置到 JTAG 模式,即 ON ON (注意新版本的 MLK-H3-CZ08-7100FC(米联客 7X 系列),支持 JTAG 模式,对于老版本的核心板,JTAG 调试 的时候一定要拔掉 TF 卡,并且设置模式开关为 OFF OFF)
7.1硬件准备
本方案中,没有使用到 TF 卡
449f07983cd8448ea427fe5700abbf88.jpg
7.2实验结果
7840fecefa984a5bb2c3ca3dfb8200cd.jpg
8液晶屏LVDS方案演示
MLK-H3-CZ08-7100FC 开发板默认 FEP 扩展接口使用 1.8V 接口,配 1.8V 的扩展模块。
8.1硬件准备
2f04abace708489fbc8f10f4ab7e6264.jpg

下图中,SD 卡不是必须的
f2156cdc83e7421dbc78a4f75451c9d8.jpg
8.2实验结果
0888ca5986aa4fc39c4bf251b2c48790.jpg

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则