| 软件版本: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概述
 本文的FPGA硬件工程部分和前文一样,本文的demo实现从SD卡读取图片在液晶屏上的显示。 本文实验目的: 1:掌握基于VDMA IP的图像显示系统的搭建方法 2:掌握VDMA Parked模式的简单应用 4:掌握HDMI显示器、7寸液晶屏的使用 5:掌握从SD卡读取bmp格式图片,并通过显示器显示2系统框图 如下图所示,该方案中 PS 部分的 CPU 读取 SD 卡中的图片到 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 视频数据。
 
     
 
 如果是米联客 7 寸液晶屏,可以支持 LVDS(必须修改 BANK 电压支持 LVDS 通信,如果有不清楚请咨询米联 客技术支持)接口或者 RGB 接口,比如 LVDS 接口的系统框图中也是把 RGB 时序转为 LVDS 驱动液晶屏。     
 如果是使用RGB接口可以直接用RGB时序驱动液晶屏.     3硬件电路分析
 硬件接口和子卡模块请阅读“附录1” 配套工程的FPGA PIN脚定义路径为soc_prj/uisrc/04_pin/ fpga_pin.xdc。4搭建SOC系统工程 请阅读“04视频图形显示方案(VDMA)”一文中“5.4搭建SOC系统工程”这一章节5搭建Vitis-sdk工程 创建soc_base sdk platform和APP工程的过程不再重复,如果不清楚请参考本章节第一个demo。 5.1创建SDK Platform工程 
     
 勾选对于FAT格式文件系统的支持     5.2创建sd_img_read测试工程
 Sd_img_read APP使用到了parked模式,使用parked对缓存切换控制可以防止图片的撕裂     6程序分析
 6.1sd_img_test.c主程序
 
 | #include   "xil_exception.h" 
 #include "xil_printf.h"
 #include "xil_cache.h"
 #include "vdma_pl.h"
 #include "sleep.h"
 #include "xsdps.h"
 #include "ff.h"
 extern XAxiVdma VDMA;
 extern XAxiVdma_DmaSetup WriteCfg;
 extern XAxiVdma_DmaSetup ReadCfg;
 #define BUF_SIZE 1280*720*3 //图像大小
 u32 GFrame[3][BUFFERSIZE] __attribute__ ((__aligned__(256)));
 static FATFS SD_Dev; // File System instance
 char *SD_Path = "0:/"; // string pointer to the logical drive number
 u8 RD_Buf[3][BUF_SIZE] __attribute__ ((aligned(32))); //分配3个缓存保存图像
 //把图像数据读出后,改成4字节对齐后再写入到内存中
 void show_img( unsigned char * addr,u32 * vdma_pbuf,u32 size_x, u32 size_y)
 {
 u32 x=0;
 u32 y=0;
 u32 r,g,b;
 for(y=size_y;y>0;y--)
 {
 for(x=0;x<size_x;x++)
 {
 b = *(addr++);
 g = *(addr++);
 r = *(addr++);
 Xil_Out32((vdma_pbuf+(((y-1)*size_x)+x)),((r<<16)|(g<<8)|(b<<0)));//write image data to ddr
 }
 
 }
 // flush all dcache data to ddr
 Xil_DCacheFlushRange((u32)(vdma_pbuf), 1280*720*4);
 }
 //Initialize SD
 int SD_init()
 {
 FRESULT result;
 //-----------------------mount dev-----------------------------------------------
 result = f_mount(&SD_Dev,SD_Path, 0);
 if (result != 0) {
 return XST_FAILURE;
 }
 return XST_SUCCESS;
 }
 int main()
 {
 u8 mode_s =0;
 u32 vcnt,hcnt;
 u32* img_ptr;
 SD_init();
 //sleep(1);
 //read image from sd card to ddr buffer
 BMP_Picture((u8 *)"0001.bmp" , &RD_Buf[0],BUF_SIZE); //读取第1张图片
 BMP_Picture((u8 *)"0002.bmp" , &RD_Buf[1],BUF_SIZE); //读取第2张图片
 BMP_Picture((u8 *)"0003.bmp" , &RD_Buf[2],BUF_SIZE); //读取第三张图片
 //set mm2s read channel address
 ReadCfg.FrameStoreStartAddr[0] = (UINTPTR)(&GFrame[0]); //VDMA第1个缓存地址
 ReadCfg.FrameStoreStartAddr[1] = (UINTPTR)(&GFrame[1]); //VDMA第2个缓存地址
 ReadCfg.FrameStoreStartAddr[2] = (UINTPTR)(&GFrame[2]); //VDMA第3个缓存地址
 //setup mm2s read channel
 MM2S_ReadSetup(XPAR_AXIVDMA_0_DEVICE_ID, &VDMA, ReadCfg); //设置VDMA的读通道
 xil_printf("VDMA Generic Video start! \r\n");
 //read image from sd to ddr buffer
 show_img(&RD_Buf[0],&GFrame[0],1280,720); //第1张图片刷入到DDR
 show_img(&RD_Buf[1],&GFrame[1],1280,720); //第2张图片刷入到DDR
 show_img(&RD_Buf[2],&GFrame[2],1280,720); //第3张图片刷入到DDR
 
 while(1)
 
 {
 
 //parking ddr buffer to screen
 
 XAxiVdma_StartParking(&VDMA, mode_s,XAXIVDMA_READ); //通过Park模式,每间隔1S更新一张图片
 
 sleep(1);
 
 if(mode_s >=2 ) mode_s = 0;
 
 else mode_s ++;
 
 }
 
 return XST_SUCCESS;
 }
 
 
 | 
 以上函数中主要2个知识点:1、读取SD卡中的bmp格式图片到DDR内存中;2、把DDR内存中的图片通过VDMA的Park模式输出到显示器上。 读bmp格式图片     
 由于bmp格式图片读出来是rgb888(24bit)对齐,所以还用使用以下函数把rgb888改为rgbx888(32bits)     
 这样三副图片会保存到GFrame[0]、GFrame[1]、GFrame[2]内存地址中。6.2bmp.c程序 
 | void BMP_ReadHeader(uint8_t *header, BMP_HeaderTypeDef *bmp) {
 bmp->fileHeader.bfType = ((*header) << 8) | (*(header + 1));
 header += 2;
 bmp->fileHeader.bfSize = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
 
 ((*(header + 1)) << 8) | (*header);
 header += 8;
 bmp->fileHeader.bfOffBits = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
 
 ((*(header + 1)) << 8) | (*header);
 header += 4;
 bmp->infoHeader.bitSize = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
 
 ((*(header + 1)) << 8) | (*header);
 header += 4;
 bmp->infoHeader.biWidth = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
 
 ((*(header + 1)) << 8) | (*header);
 header += 4;
 bmp->infoHeader.biHeight = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
 
 ((*(header + 1)) << 8) | (*header);
 header += 6;
 bmp->infoHeader.biBitCount = ((*(header + 1)) << 8) | (*header);
 
 header += 2;
 bmp->infoHeader.biCompression = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
 
 ((*(header + 1)) << 8) | (*header);
 header += 4;
 bmp->infoHeader.biSizeImage = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
 
 ((*(header + 1)) << 8) | (*header);
 header += 4;
 bmp->infoHeader.biXPelsPerMeter = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
 
 ((*(header + 1)) << 8) | (*header);
 header += 4;
 bmp->infoHeader.biYPelsPerMeter = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
 
 ((*(header + 1)) << 8) | (*header);
 }
 /****************************************************************************
 * Function Name : BMP_Picture
 * Description : 显示BMP格式的图片
 * Input : dir:要显示的图片路径和名字
 * Output : None
 * Return : None
 ****************************************************************************/
 void BMP_Picture(uint8_t *dir , uint8_t * buf ,uint32_t len)
 {
 FRESULT res;
 FIL fsrc;
 UINT br;
 UINT a;
 uint8_t buffer[1024];
 
 BMP_HeaderTypeDef bmpHeader;
 /* 打开要读取的文件 */
 res = f_open(&fsrc, (const TCHAR*)dir, FA_READ);
 if(res == FR_OK) //打开成功
 
 {
 /* 读取BMP文件的文件信息 */
 
 res = f_read(&fsrc, buffer, sizeof(buffer), &br);
 /* 将数组里面的数据放入到结构数组中,并排序好 */
 BMP_ReadHeader(buffer, &bmpHeader);
 a = bmpHeader.fileHeader.bfOffBits; //去掉文件信息才开始是像素数据
 res=f_lseek(&fsrc, a);
 if(res)
 {
 return 0;
 }
 res = f_read(&fsrc, buf, len, &br);
 
 }
 
 f_close(&fsrc); //不论是打开,还是新建文件,一定记得关闭
 }
 
 
 | 
 
 Bmp.c函数中重要是需要对BMP图片格式的头部进行解析,并且获取图像数据的开始位置,然后用 f_lseek(&fsrc, a)函数,定位到图像数据的位置,之后再读出图像的数据。 | #indef_bmp_H 
 #define _bmp_H
 #include <stdio.h>
 typedef struct
 {
 uint16_t bfType; //文件类型,BMP格式为字符串BM
 uint32_t bfSize;
 //图片大小,单位为KB
 uint16_t bfReserved1; //保留位
 uint16_t bfReserved2; //保留位
 uint32_t bfOffBits; //从文件头到实际图像数据之间的字节偏移量
 } BMP_FileHeaderTypeDef;
 typedef struct
 {
 uint32_t bitSize;
 //BMP_InfoHeaderTypeDef结构体所需要的字节数
 uint32_t biWidth;
 //图片宽度,像素位单位
 int32_t biHeight;
 //图片高度,像素为单位。正为倒立,负为正向。
 uint16_t biPlanes;
 //颜色平面数,总为1
 uint16_t biBitCount; //比特数/像素。其值为:1、4、8、16、24或32
 uint32_t biCompression; //数据压缩类型
 uint32_t biSizeImage; //图像大小
 uint32_t biXPelsPerMeter;//水平分辨率
 uint32_t biYPelsPerMeter;//垂直分辨率
 uint32_t biClrUsed;
 //颜色索引数
 uint32_t biClrImportant; //重要颜色索引数
 }BMP_InfoHeaderTypeDef;
 typedef struct
 {
 BMP_FileHeaderTypeDef fileHeader;
 BMP_InfoHeaderTypeDef infoHeader;
 }BMP_HeaderTypeDef;
 void BMP_ReadHeader(uint8_t *header, BMP_HeaderTypeDef *bmp);
 void BMP_Picture(uint8_t *dir , uint8_t * buf ,uint32_t len);
 #endif
 
 | 
7HDMI方案演示
 对于 MLK-H3-CZ08-7100FC 不支持从 SD 卡启动, 因此必须通过 JTAG 调试模式完成本实验7.1硬件准备 复制路径soc_prj/uisrc/06_doc/testimage路径下的测试图片到SD卡,并且插入到开发中。     
 本实验需要用到 JTAG 下载器、USB 转串口外设,另外需要把核心板上的 2P 模式开关设置到 JTAG 模式,即 ON ON (注意新版本的 MLK-H3-CZ08-7100FC(米联客 7X 系列),支持 JTAG 模式,对于老版本的核心板,JTAG 调试 的时候一定要拔掉 TF 卡,并且设置模式开关为 OFF OFF)     7.2实验结果
 
     8液晶屏LVDS方案演示
 对于 MLK-H3-CZ08-7100FC 不支持从 SD 卡启动, 因此必须通过 JTAG 调试模式完成本实验8.1硬件准备 复制路径soc_prj/uisrc/06_doc/testimage路径下的测试图片到SD卡,并且插入到开发中。     
 液晶屏,模式开关1脚切到OFF     
 本实验需要用到 TF 卡方测试图片     8.2实验结果
 
     
 
 
 |