[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA_SDK高级篇连载-05读取SD卡图片方案(VDMA/HDMI)

文档创建者:FPGA课程
浏览次数:127
最后更新:2024-09-30
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-SOC » 1_SDK应用方案(仅旗舰型号) » 2-SDK高级应用方案
本帖最后由 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 视频数据。
652f8f4d72374a95b420434faa38cb56.jpg

如果是米联客 7 寸液晶屏,可以支持 LVDS(必须修改 BANK 电压支持 LVDS 通信,如果有不清楚请咨询米联 客技术支持)接口或者 RGB 接口,比如 LVDS 接口的系统框图中也是把 RGB 时序转为 LVDS 驱动液晶屏。
c9d0f2466a81434f8f1f2299f12ff73b.jpg
如果是使用RGB接口可以直接用RGB时序驱动液晶屏.
bc7b3e6771494153ac42b82f0ae954df.jpg
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工程
274959bebe644dd8a1443361477acab3.jpg
勾选对于FAT格式文件系统的支持
f1c5ea16db2a41b3b0abbc2beb38d927.jpg
5.2创建sd_img_read测试工程
Sd_img_read APP使用到了parked模式,使用parked对缓存切换控制可以防止图片的撕裂
50a3682f81cf49b388c2000030659570.jpg
6程序分析
6.1sd_img_test.c主程序
  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. #include "xsHDMIs.h"
  26. #include "ff.h"
  27. extern XAxiVdma VDMA;
  28. extern XAxiVdma_DmaSetup WriteCfg;
  29. extern XAxiVdma_DmaSetup ReadCfg;
  30. #define BUF_SIZE 1280*720*3 //图像大小
  31. u32 GFrame[3][BUFFERSIZE] __attribute__ ((__aligned__(256)));
  32. static FATFS SD_Dev; // File System instance
  33. char *SD_Path = "0:/";  //  string pointer to the logical drive number
  34. u8 RD_Buf[3][BUF_SIZE] __attribute__ ((aligned(32))); //分配3个缓存保持图像
  35. //把图像数据读出后,改成4字节对齐后再写入到内存中
  36. void show_img( unsigned char * addr,u32 * vdma_pbuf,u32 size_x, u32 size_y)
  37. {
  38.         u32 x=0;
  39.         u32 y=0;
  40.         u32 r,g,b;
  41.         for(y=size_y;y>0;y--)
  42.         {
  43.                 for(x=0;x<size_x;x++)
  44.                 {
  45.                         b = *(addr++);
  46.                         g = *(addr++);
  47.                         r = *(addr++);
  48.                         Xil_Out32((vdma_pbuf+(((y-1)*size_x)+x)),((r<<16)|(g<<8)|(b<<0)));//write image data to ddr
  49.                 }
  50.         }
  51.         // flush all dcache data to ddr
  52.         Xil_DCacheFlushRange((u32)(vdma_pbuf), 1280*720*4);
  53. }
  54. //Initialize SD
  55. int SD_init()
  56. {
  57.         FRESULT result;
  58.         //-----------------------mount dev-----------------------------------------------
  59.         result = f_mount(&SD_Dev,SD_Path, 0);
  60.         if (result != 0) {
  61.                 return XST_FAILURE;
  62.         }
  63.         return XST_SUCCESS;
  64. }
  65. int main()
  66. {
  67.         u8 mode_s =0;
  68.         u32 vcnt,hcnt;
  69.         u32* img_ptr;
  70.         SD_init();
  71.         //sleep(1);
  72.         //read image from sd card to ddr buffer
  73.         BMP_Picture((u8 *)"0001.bmp" , &RD_Buf[0],BUF_SIZE); //读取第1张图片
  74.         BMP_Picture((u8 *)"0002.bmp" , &RD_Buf[1],BUF_SIZE); //读取第2张图片
  75.         BMP_Picture((u8 *)"0003.bmp" , &RD_Buf[2],BUF_SIZE); //读取第三张图片
  76.         //set mm2s read channel address
  77.         ReadCfg.FrameStoreStartAddr[0] = (UINTPTR)(&GFrame[0]); //VDMA第1个缓存地址
  78.         ReadCfg.FrameStoreStartAddr[1] = (UINTPTR)(&GFrame[1]); //VDMA第2个缓存地址
  79.         ReadCfg.FrameStoreStartAddr[2] = (UINTPTR)(&GFrame[2]); //VDMA第3个缓存地址
  80.         //setup mm2s read channel
  81.         MM2S_ReadSetup(XPAR_AXIVDMA_0_DEVICE_ID, &VDMA, ReadCfg); //设置VDMA的读通道
  82.         xil_printf("VDMA Generic Video start! \r\n");
  83.         //read image from sd to ddr buffer
  84.         show_img(&RD_Buf[0],&GFrame[0],1280,720); //第1张图片刷入到DDR
  85.         show_img(&RD_Buf[1],&GFrame[1],1280,720); //第2张图片刷入到DDR
  86.         show_img(&RD_Buf[2],&GFrame[2],1280,720); //第3张图片刷入到DDR
  87.     while(1)
  88.     {
  89.             //parking ddr buffer to screen
  90.             XAxiVdma_StartParking(&VDMA, mode_s,XAXIVDMA_READ); //通过Park模式,每间隔1S更新一张图片
  91.             sleep(1);
  92.             if(mode_s >=2 ) mode_s = 0;
  93.             else mode_s ++;
  94.     }
  95.     return XST_SUCCESS;
  96. }
复制代码

以上函数中主要2个知识点:1、读取SD卡中的bmp格式图片到DDR内存中;2、把DDR内存中的图片通过VDMA的Park模式输出到显示器上。
读bmp格式图片
0719ea92857f4948b53a690945ae4c86.jpg
由于bmp格式图片读出来是rgb888(24bit)对齐,所以还用使用以下函数把rgb888改为rgbx888(32bits)
49a163c85eab4ec3a10f9a0db4e3e277.jpg
这样三副图片会保存到GFrame[0]、GFrame[1]、GFrame[2]内存地址中。
6.2bmp.c程序
  1. #include "bmp.h"
  2. #include "ff.h"
  3. /****************************************************************************
  4. * Function Name  : BMP_ReadHeader
  5. * Description    : 将读取到的数组函数转换位BPM文件信息结构体类型。由于在内存
  6. *                * 上面数组的存储方式与结构体不同,所以要转换,而且SD读取到的
  7. *                * 文件信息是小端模式。高位是低字节,低位是高字节,跟我们常用
  8. *                * 的正好相反所以将数据转换过来。
  9. * Input          : header:要转换的数组
  10. *                * bmp:转换成的结构体
  11. * Output         : None
  12. * Return         : None
  13. ****************************************************************************/
  14. void BMP_ReadHeader(uint8_t *header, BMP_HeaderTypeDef *bmp)
  15. {
  16.         bmp->fileHeader.bfType = ((*header) << 8) | (*(header + 1));
  17.         header += 2;
  18.         bmp->fileHeader.bfSize = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
  19.                                  ((*(header + 1)) << 8) | (*header);
  20.         header += 8;
  21.         bmp->fileHeader.bfOffBits = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
  22.                                     ((*(header + 1)) << 8) | (*header);
  23.         header += 4;
  24.         bmp->infoHeader.bitSize = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
  25.                                   ((*(header + 1)) << 8) | (*header);
  26.         header += 4;
  27.         bmp->infoHeader.biWidth = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
  28.                                   ((*(header + 1)) << 8) | (*header);
  29.         header += 4;
  30.         bmp->infoHeader.biHeight = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
  31.                                    ((*(header + 1)) << 8) | (*header);
  32.         header += 6;
  33.         bmp->infoHeader.biBitCount = ((*(header + 1)) << 8) | (*header);
  34.                                  
  35.         header += 2;
  36.         bmp->infoHeader.biCompression = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
  37.                                         ((*(header + 1)) << 8) | (*header);
  38.         header += 4;
  39.         bmp->infoHeader.biSizeImage = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
  40.                                       ((*(header + 1)) << 8) | (*header);
  41.         header += 4;
  42.         bmp->infoHeader.biXPelsPerMeter = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
  43.                                           ((*(header + 1)) << 8) | (*header);
  44.         header += 4;
  45.         bmp->infoHeader.biYPelsPerMeter = ((*(header + 3)) << 24) | ((*(header + 2)) << 16) |
  46.                                           ((*(header + 1)) << 8) | (*header);
  47. }
  48. /****************************************************************************
  49. * Function Name  : BMP_Picture
  50. * Description    : 显示BMP格式的图片
  51. * Input          : dir:要显示的图片路径和名字
  52. * Output         : None
  53. * Return         : None
  54. ****************************************************************************/
  55. void BMP_Picture(uint8_t *dir , uint8_t  * buf ,uint32_t len)
  56. {
  57.                 FRESULT res;
  58.                 FIL fsrc;
  59.                 UINT  br;
  60.                 UINT  a;
  61.                 uint8_t buffer[1024];
  62.                 BMP_HeaderTypeDef bmpHeader;
  63.                
  64.                 /* 打开要读取的文件 */
  65.                 res = f_open(&fsrc, (const TCHAR*)dir, FA_READ);
  66.                 if(res == FR_OK)   //打开成功
  67.             {
  68.                         /* 读取BMP文件的文件信息 */
  69.                 res = f_read(&fsrc, buffer, sizeof(buffer), &br);
  70.                         /* 将数组里面的数据放入到结构数组中,并排序好 */
  71.                         BMP_ReadHeader(buffer, &bmpHeader);
  72.                         a = bmpHeader.fileHeader.bfOffBits;    //去掉文件信息才开始是像素数据
  73.                         res=f_lseek(&fsrc, a);
  74.                         if(res)
  75.                         {
  76.                                 return 0;
  77.                         }
  78.                         res = f_read(&fsrc, buf, len, &br);
  79.             }
  80.     f_close(&fsrc);  //不论是打开,还是新建文件,一定记得关闭
  81. }
复制代码

Bmp.c函数中重要是需要对BMP图片格式的头部进行解析,并且获取图像数据的开始位置,然后用 f_lseek(&fsrc, a)函数,定位到图像数据的位置,之后再读出图像的数据。
  1. #ifndef _bmp_H
  2. #define _bmp_H
  3. #include <stdio.h>
  4. typedef struct
  5. {
  6.         uint16_t bfType;        //文件类型,BMP格式为字符串BM
  7.         uint32_t bfSize;                //图片大小,单位为KB
  8.         uint16_t bfReserved1;        //保留位
  9.         uint16_t bfReserved2;        //保留位
  10.         uint32_t bfOffBits;          //从文件头到实际图像数据之间的字节偏移量
  11. } BMP_FileHeaderTypeDef;
  12. typedef struct
  13. {
  14.         uint32_t bitSize;                 //BMP_InfoHeaderTypeDef结构体所需要的字节数
  15.         uint32_t biWidth;                 //图片宽度,像素位单位
  16.         int32_t  biHeight;                 //图片高度,像素为单位。正为倒立,负为正向。
  17.         uint16_t biPlanes;                 //颜色平面数,总为1
  18.         uint16_t biBitCount;         //比特数/像素。其值为:1、4、8、16、24或32
  19.         uint32_t biCompression;  //数据压缩类型
  20.         uint32_t biSizeImage;         //图像大小
  21.         uint32_t biXPelsPerMeter;//水平分辨率
  22.         uint32_t biYPelsPerMeter;//垂直分辨率
  23.         uint32_t biClrUsed;                 //颜色索引数
  24.         uint32_t biClrImportant; //重要颜色索引数
  25.                
  26. }BMP_InfoHeaderTypeDef;
  27. typedef struct
  28. {
  29.         BMP_FileHeaderTypeDef fileHeader;
  30.         BMP_InfoHeaderTypeDef infoHeader;
  31.                
  32. }BMP_HeaderTypeDef;
  33. void BMP_ReadHeader(uint8_t *header, BMP_HeaderTypeDef *bmp);
  34. void BMP_Picture(uint8_t *dir , uint8_t * buf ,uint32_t len);
  35. #endif
复制代码

7HDMI方案演示
对于 MLK-H3-CZ08-7100FC 不支持从 SD 卡启动, 因此必须通过 JTAG 调试模式完成本实验
7.1硬件准备
复制路径soc_prj/uisrc/06_doc/testimage路径下的测试图片到SD卡,并且插入到开发中。
817ce5560680430daad59af46e620c23.jpg
本实验需要用到 JTAG 下载器、USB 转串口外设,另外需要把核心板上的 2P 模式开关设置到 JTAG 模式,即 ON ON (注意新版本的 MLK-H3-CZ08-7100FC(米联客 7X 系列),支持 JTAG 模式,对于老版本的核心板,JTAG 调试 的时候一定要拔掉 TF 卡,并且设置模式开关为 OFF OFF)
c446768667084d04a3c031a8dfd04ff3.jpg
7.2实验结果
4ba5768ef0a0498aa8d94c819b956639.jpg
8液晶屏LVDS方案演示
对于 MLK-H3-CZ08-7100FC 不支持从 SD 卡启动, 因此必须通过 JTAG 调试模式完成本实验
8.1硬件准备
复制路径soc_prj/uisrc/06_doc/testimage路径下的测试图片到SD卡,并且插入到开发中。
83fd629e224a4612baacdf20dab0e822.jpg
液晶屏,模式开关1脚切到OFF
bd1de29eb01d40678c592930eac2d37b.jpg
本实验需要用到 TF 卡方测试图片
4f187a319758473ba5da6321ca07d603.jpg
8.2实验结果
7f5f7bdf9a1345a08ada8f608f3ab6f7.jpg

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

本版积分规则