本节作为前一节的补充,将TF卡上的图片通过VDMA的方式显示在HDMI设备上,作为VDMA的实战演练
因为本文内容和前一节有大量的重复雷同工作,所以本节只介绍关键的几个地方,需要看详细工程创建的请看 VDMA的第一节内容
- 此章节内容适用于Smart ZYNQ SP SP2和 SL 版的板子 ( 不包含Smart ZYNQ 标准版 ),如是标准版或本站其他板子请看对应板子目录
- 本文在 vivado2018.3版本上演示
一、VIVADO工程的修改
1)vivado 在blockdesign里 使能SD功能,(之前工程其实已经做了这一步)
2)将上一章的工程分辨率修改设置为720P分辨率
a) 将 clk_out1 修改成1080p的 74.25 ,而clk_out2修改成5倍的clk_out1 即371.25即可。
2)VTC的 参数修改: 为了把hdmi显示的分辨率修改成720P,我们这里还需要修改Video Timing Controller 的参数,如下图所示,双击模块,然后将Video Mode 修改成720p即可
之后对工程进行重新编译和综合,并重新export一遍工程。
二、SDK部分的修改
1)接下来SDK部分我们作如下修改, 因为我们要加载TF卡,而TF卡的格式是FAT32的,所以这里我们需要加载FatFs库,xilinx已经帮我们集成了这部分库的功能,我们只需要使能功能就好,操作如下
2)添加 XILFFS 库
3)接下来需要使能 文件名称的功能 默认是关闭的
4)代码的编写
复制以下代码到main.c中
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "xil_types.h" #include "xil_cache.h" #include "xparameters.h" #include "xaxivdma.h" #include "xaxivdma_i.h" #include "ff.h" #include "sleep.h" void load_sd_bmp(u8 *frame,unsigned char mode); void load_sd_bmp(u8 *frame,unsigned char mode) { static FATFS fatfs; FIL fil; u8 bmp_head[54]; UINT *bmp_width,*bmp_height; UINT br; int i; //挂载文件系统 f_mount(&fatfs,"",1); //打开文件 if(mode==0)f_open(&fil,"C.bmp",FA_READ); else f_open(&fil,"D.bmp",FA_READ); //移动文件读写指针到文件开头 f_lseek(&fil,0); //读取BMP文件头 f_read(&fil,bmp_head,54,&br); //打印BMP图片分辨率和大小 bmp_width = (UINT *)(bmp_head + 0x12); bmp_height = (UINT *)(bmp_head + 0x16); //读出图片,写入DDR for(i=*bmp_height-1;i>=0;i--){ f_read(&fil,frame+i*(*bmp_width)*3,(*bmp_width)*3,&br); } //关闭文件 f_close(&fil); Xil_DCacheFlush(); //刷新Cache,数据更新至DDR3中 } #define H_STRIDE 1280 #define H_ACTIVE 1280 #define V_ACTIVE 720 #define VDMA_BASEADDR XPAR_AXI_VDMA_0_BASEADDR #define VIDEO_BASEADDR0 0x01000000//帧存0地址 #define VIDEO_BASEADDR1 0x02000000//帧存1地址 void VDMA_init(){ Xil_Out32((VDMA_BASEADDR + 0x000), 0x3); // enable circular mode Xil_Out32((VDMA_BASEADDR + 0x05c), VIDEO_BASEADDR0); // start address Xil_Out32((VDMA_BASEADDR + 0x060), VIDEO_BASEADDR0); // start address Xil_Out32((VDMA_BASEADDR + 0x064), VIDEO_BASEADDR0); // start address Xil_Out32((VDMA_BASEADDR + 0x058), (H_STRIDE*3)); // h offset (H_STRIDE * 3) bytes Xil_Out32((VDMA_BASEADDR + 0x054), (H_ACTIVE*3)); // h size (H_ACTIVE * 3) bytes Xil_Out32((VDMA_BASEADDR + 0x050), V_ACTIVE); // v size (V_ACTIVE) } int main(void) { load_sd_bmp((u8*)VIDEO_BASEADDR0,0); load_sd_bmp((u8*)VIDEO_BASEADDR1,1); VDMA_init(); while(1){ Xil_Out32((VDMA_BASEADDR + 0x05c), VIDEO_BASEADDR0); // start address Xil_Out32((VDMA_BASEADDR + 0x060), VIDEO_BASEADDR0); // start address Xil_Out32((VDMA_BASEADDR + 0x064), VIDEO_BASEADDR0); // start address Xil_Out32((VDMA_BASEADDR + 0x058), (H_STRIDE*3)); // h offset (H_STRIDE * 3) bytes Xil_Out32((VDMA_BASEADDR + 0x054), (H_ACTIVE*3)); // h size (H_ACTIVE * 3) bytes Xil_Out32((VDMA_BASEADDR + 0x050), V_ACTIVE); // v size (V_ACTIVE) sleep(1); Xil_Out32((VDMA_BASEADDR + 0x05c), VIDEO_BASEADDR1); // start address Xil_Out32((VDMA_BASEADDR + 0x060), VIDEO_BASEADDR1); // start address Xil_Out32((VDMA_BASEADDR + 0x064), VIDEO_BASEADDR1); // start address Xil_Out32((VDMA_BASEADDR + 0x058), (H_STRIDE*3)); // h offset (H_STRIDE * 3) bytes Xil_Out32((VDMA_BASEADDR + 0x054), (H_ACTIVE*3)); // h size (H_ACTIVE * 3) bytes Xil_Out32((VDMA_BASEADDR + 0x050), V_ACTIVE); // v size (V_ACTIVE) sleep(1); } return 0; }
代码非常好理解,load_sd_bmp 实现的功能是挂载TF卡,并且根据mode的值来读取TF卡中对应名称的图片,并将图片的内容存入对应的DDR地址内。(mode 是我自己加入的,通过mode的值的大小分别载入两张不同的图片,最终在主函数里实现两张图片显示的动态切换)
VIDEO_BASEADDR0,和VIDEO_BASEADDR1分别对应两帧的缓存地址(只要两个帧之间的地址相差的位置可以调整,只要不要彼此相互有交集产生内存相互影响就可以了)
#define VIDEO_BASEADDR0 0x01000000//帧存0地址 #define VIDEO_BASEADDR1 0x02000000//帧存1地址
VDMA 的初始化,因为之前我们在VIVADO 的blockdesign 里设计的VDMA缓存有3个,所以这里需要同时定义3个缓存区对应的DDR上的地址映射(为了方便演示,我们这里将3个缓存区映射到同一个DDR区)
备注 多缓存区存在的目的是为了防止画面的撕裂,但是VDMA会在多个缓存区中循环的显示内容,比方说如果我们这里设置成3个缓存区地址不同的话,实际我们修改了其中一帧的内容会造成,3帧中的一帧不一致,导致画面另外两帧还是原先的内容,然后画面快速三帧循环,效果就是不停的切换闪烁, 所以这里为了方便演示将VDMA三个缓存区映射到相同的地址进行演示(等效于VDMA 只有一帧的效果)
主程序里 加载两张照片分别到VIDEO_BASEADDR0内存区域,和VIDEO_BASEADDR1内存区域,并完成VDMA初始化
load_sd_bmp((u8*)VIDEO_BASEADDR0,0); load_sd_bmp((u8*)VIDEO_BASEADDR1,1); VDMA_init();
主循环也很简单,通过调整VDMA 帧缓存的映射地址(这些地址在之前已存入TF图片信息),来实现HDMI不同画面的切换
三、上机调试
1)之后在电脑上插入FAT32格式的TF卡,并将下列文件下载并解压并存入TF卡的根目录(切记 TF卡必须为fat32格式) HDMI 测试图片 image下载
其中A,B是1080P的BMP,C,D是720P的BMP图片文件
2)将TF卡从电脑正常退出,并插入到我们主板的 TF卡卡槽中
3) 回到SDK界面,对程序进行正常编译,并debug,没问题的话,应该可以看到两张720p的图片在循环播放的。
写在实验后:
1)如果要修改分辨率,只需要对VTC的分辨率,以及Clock时钟,以及main函数分辨率对应位置进行修改即可(本节和上一节内容均有提及),备注TF卡中的图片A.bmp 和B.bmp 对应1080p,C.bmp和D.bmp对应720P。大家调用的时候注意选择。
2)切换vdma缓存的方法暂时没有找到更简单的,所以就像上面这样,通过寄存器完整的修改了,后续如果找到更简单的方法我再修改
3)PS和前两节的VDMA初始化不同,前两节用的是库函数,而这里直接对寄存器写入来实现VDMA初始化,两种方式都是可以的
- 本文的完整工程下载:20_VDMA_HDMI_TF_TEST
- 需放在TF卡下的测试图片下载:测试图片 image下载
- VIVADO的版本:2018.3
- 工程创建目录:E:\Smart_ZYNQ_SP_SL\SDK\20_VDMA_HDMI_TF_TEST
- 工程适用主板: Smart ZYNQ (SP / SP2 / SL) (不适用于Smart ZYNQ 标准版 )