本帖最后由 RZJM 于 2016-1-26 20:46 编辑
本编文章在ADI官方HDMI例程的基础上进行修改,实现视频通路,为使用ZYNQ视频处理做好必要准备。
在 【ZYNQ-7000开发之九】使用VDMA在PL和PS之间传输视频流数据 这篇文章中,介绍了如何使用VDMA传输stream类型的视频流数据,本次实验将结合 【ZYNQ-7000开发之三】ZYNQ平台的HDMI驱动测试 这篇文章,本次实验默认大家已经完成了【三】和【九】,至少要完成【三】,本次实验将在【三】的基础上直接修改。
本篇文章的思想是使用TPG产生stream类型的数据,然后用VDMA转换成Memory Map类型的数据写入到DDR3缓存,最后再用VDMA 从DDR3读出来转换成stream类型,发送给HDMI控制器,进而显示在HDMI显示器上。
TGP VDMA等的规范说明可以到官网下载最新版本
本文所使用的开发板是Miz702(兼容zedboard)
PC 开发环境版本:Vivado 2015.2 Xilinx SDK 2015.2
其它:HDMI显示器,串口线
修改硬件系统工程 打开【ZYNQ-7000开发之三】做好的vivado工程,打开Block Design 找到VDMA双击,选中Enable Write Channel,同时设置Memory Map Data Width为64,点击OK
点击Run Connection Automation,在弹出的对话框选择ALL,点击OK 点击ADD IP,添加一个TPG IP Core 双击刚刚添加的TPG,按照如图所示配置,这个主要为了简洁一些,去掉了一些不必要的功能
把TPG的video_out和VDMA的S_AXIS_S2MM连接在一起,如图所示
同理把TPG的aclk连接到VDMA的aclk,把TPG的aresetn连接到VDMA的resetn,此外VDMA的s_axis_s2mm_aclk也和VDMA的aclk连在一起 然后按下F6快捷键,验证下系统是不是有问题,没有问题就保存工程,点击generate bitstream 完成后, 导出硬件,Launch SDK
修改软件工程
找到cf_hdmi.c文件,把InitHdmiVideoPcore函数修改如下:
/***************************************************************************//**
* @brief InitHdmiVideoPcore.
*******************************************************************************/
void InitHdmiVideoPcore(unsigned short horizontalActiveTime,
unsigned short horizontalBlankingTime,
unsigned short horizontalSyncOffset,
unsigned short horizontalSyncPulseWidth,
unsigned short verticalActiveTime,
unsigned short verticalBlankingTime,
unsigned short verticalSyncOffset,
unsigned short verticalSyncPulseWidth)
{
unsigned short horizontalCount = 0;
unsigned short verticalCount = 0;
unsigned short horizontalBackPorch = 0;
unsigned short verticalBackPorch = 0;
unsigned short horizontalDeMin = 0;
unsigned short horizontalDeMax = 0;
unsigned short verticalDeMin = 0;
unsigned short verticalDeMax = 0;
DDRVideoWr(horizontalActiveTime, verticalActiveTime);
horizontalCount = horizontalActiveTime +
horizontalBlankingTime;
verticalCount = verticalActiveTime +
verticalBlankingTime;
horizontalBackPorch = horizontalBlankingTime -
horizontalSyncOffset -
horizontalSyncPulseWidth;
verticalBackPorch = verticalBlankingTime -
verticalSyncOffset -
verticalSyncPulseWidth;
horizontalDeMin = horizontalSyncPulseWidth +
horizontalBackPorch;
horizontalDeMax = horizontalDeMin +
horizontalActiveTime;
verticalDeMin = verticalSyncPulseWidth +
verticalBackPorch;
verticalDeMax = verticalDeMin +
verticalActiveTime;
Xil_Out32((CFV_BASEADDR + AXI_HDMI_REG_HTIMING1),
((horizontalActiveTime << 16) | horizontalCount));
Xil_Out32((CFV_BASEADDR + AXI_HDMI_REG_HTIMING2),
horizontalSyncPulseWidth);
Xil_Out32((CFV_BASEADDR + AXI_HDMI_REG_HTIMING3),
((horizontalDeMax << 16) | horizontalDeMin));
Xil_Out32((CFV_BASEADDR + AXI_HDMI_REG_VTIMING1),
((verticalActiveTime << 16) | verticalCount));
Xil_Out32((CFV_BASEADDR + AXI_HDMI_REG_VTIMING2),
verticalSyncPulseWidth);
Xil_Out32((CFV_BASEADDR + AXI_HDMI_REG_VTIMING3),
((verticalDeMax << 16) | verticalDeMin));
Xil_Out32((CFV_BASEADDR + AXI_HDMI_REG_RESET), 0x1);
Xil_Out32((CFV_BASEADDR + AXI_HDMI_REG_SOURCE_SEL), 0x0);
Xil_Out32((CFV_BASEADDR + AXI_HDMI_REG_SOURCE_SEL), 0x1);
Xil_Out32(VDMA_BASEADDR + 0x30, 0x4); //reset S2MM VDMA Control Register
Xil_Out32(VDMA_BASEADDR + 0x30, 0x8); //genlock
Xil_Out32(VDMA_BASEADDR + 0xAC, 0x08000000);//S2MM Start Addresses
Xil_Out32(VDMA_BASEADDR + 0xAC+4, 0x0A000000);
Xil_Out32(VDMA_BASEADDR + 0xAC+8, 0x0D000000);
Xil_Out32(VDMA_BASEADDR + 0xA4, 640*4);//S2MM Horizontal Size
Xil_Out32(VDMA_BASEADDR + 0xA8, 640*4);//S2MM Frame Delay and Stride
Xil_Out32(VDMA_BASEADDR + 0x30, 0x3);//S2MM VDMA Control Register
Xil_Out32(VDMA_BASEADDR + 0xA0, 480);//S2MM Vertical Size start an S2M
Xil_DCacheFlush();
Xil_Out32((VDMA_BASEADDR + AXI_VDMA_REG_DMA_CTRL),
0x00000003); // enable circular mode
Xil_Out32((VDMA_BASEADDR + AXI_VDMA_REG_START_1),
0x08000000); // start address
Xil_Out32((VDMA_BASEADDR + AXI_VDMA_REG_START_2),
0x0A000000); // start address
Xil_Out32((VDMA_BASEADDR + AXI_VDMA_REG_START_3),
0x0D000000); // start address
Xil_Out32((VDMA_BASEADDR + AXI_VDMA_REG_FRMDLY_STRIDE),
(horizontalActiveTime*4)); // h offset
Xil_Out32((VDMA_BASEADDR + AXI_VDMA_REG_H_SIZE),
(horizontalActiveTime*4)); // h size
Xil_Out32((VDMA_BASEADDR + AXI_VDMA_REG_V_SIZE),
verticalActiveTime); // v size
} 复制代码
分别配置好FPGA和ARM后,在串口终端按‘6’,选择640*480分辨率,看到如下所示的效果就成功了。大家可以更换TPG的输出类型,查看不同的效果。
总结 目前通过zynq实现了640*480的视频显示,大家可以调试下其它的分辨率。此外,串口终端发送数据的时候会断开,不知道大家是否会遇到这个BUG。