本帖最后由 FPGA课程 于 2024-9-30 11:20 编辑
软件版本: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主程序- /********************MILIANKE**************************
- *Company : MiLianKe Electronic Technology Co., Ltd.
- *WebSite:https://www.milianke.com
- *TechWeb:https://www.uisrc.com
- *tmall-shop:https://milianke.tmall.com
- *jd-shop:https://milianke.jd.com
- *taobao-shop1: https://milianke.taobao.com
- *Create Date: 2021/10/15
- *File Name: vdma_out_test.c
- *Description:
- *Declaration:
- *The reference demo provided by Milianke is only used for learning.
- *We cannot ensure that the demo itself is free of bugs, so users
- *should be responsible for the technical problems and consequences
- *caused by the use of their own products.
- *Copyright: Copyright (c) MiLianKe
- *All rights reserved.
- *Revision: 1.0
- ****************************************************/
- #include "xil_exception.h"
- #include "xil_printf.h"
- #include "xil_cache.h"
- #include "vdma_pl.h"
- #include "sleep.h"
- #include "xsHDMIs.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程序- #include "bmp.h"
- #include "ff.h"
- /****************************************************************************
- * Function Name : BMP_ReadHeader
- * Description : 将读取到的数组函数转换位BPM文件信息结构体类型。由于在内存
- * * 上面数组的存储方式与结构体不同,所以要转换,而且SD读取到的
- * * 文件信息是小端模式。高位是低字节,低位是高字节,跟我们常用
- * * 的正好相反所以将数据转换过来。
- * Input : header:要转换的数组
- * * bmp:转换成的结构体
- * Output : None
- * Return : None
- ****************************************************************************/
- 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)函数,定位到图像数据的位置,之后再读出图像的数据。 - #ifndef _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实验结果
|