第七章 ZYNQ视频图像处理系统——SD卡图片播放器
7.1 BMP图片格式简介
本章我们将通过ZYNQ的PS端读取SD卡中的图片,并将其显示在HDMI显示器上。我们常用的图片格式有很多,如JPEG(或JPG)、PNG和BMP等。其中BMP格式图片的文件结构相对简单,易于程序读取和写入,因此这里我们使用BMP图片格式。
BMP(全称Bitmap)是Windows操作系统中的标准图像文件格式,文件后缀名为“.bmp”,应用非常广泛。它直接记录图像中每一个像素点的颜色信息,几乎不进行任何压缩。因此,BMP文件所占用的存储空间较大,但是图片没有失真。下面是一张分辨率为1280 x 720p的BMP格式图像,我们以此为例给大家介绍BMP图像的文件格式:

典型的BMP图像文件由四部分组成:
1、BMP文件头,它包含BMP图像文件的类型、大小等信息;
2、BMP信息头,它包含有BMP图像的宽度、高度、压缩方法,以及定义颜色等信息;
3、调色板,这个部分是可选的,如果使用索引来表示图像,调色板就是索引与其对应颜色的映射表;
4、位图数据,即图像数据,在位深度为24位时直接使用RGB格式,而小于24位时使用调色板中颜色的索引值。
上述各组成部分的字节大小如下表所示:

我们一般见到的图像以24位图像为主,即R、G、B三种颜色各用8个bit来表示,这样的图像我们称之为真彩色。在这种情况下是不需要调色板的,位图信息头后面紧跟的就是位图数据了。也就是说,位图文件从文件头开始偏移54个字节就是图像数据了。
首先我们来看一下图 7.1.1在Windows系统中的属性信息,如下图所示:

图 7.1.2 示例图片属性
上图中包括该BMP图片的“图像”属性以及“文件”属性:图像分辨率为1280 x 720,每个像素点的颜色使用24位表示;文件大小为2.63MB,。
接下来,我们在Notepad++中以十六进制格式打开该BMP图像(需要在Notepad++中安装名为HEX-Editor的插件),如下图所示:

图 7.1.3 在Notepad++中以十六进制打开BMP文件
文件打开后如下图所示:

图 7.1.4 BMP图像(1280x720p)的16进制数据
上图中红色矩形区域为BMP的文件头,共14字节;蓝色区域为信息头,共40字节;剩余部分为图像的像素数据。左下角红色椭圆区域表明整个BMP文件共2764854个字节,除去文件头和信息头所占的54个字节,图像像素数据为2764800个字节。由于示例图片每个像素点使用3个字节表示颜色,因此我们可以计算出图像数据的大小为1280*720*3 = 2764800字节,与从Notepad++中计算得到的结果一致。
首先来了解一下BMP文件头的数据结构,如下表所示:

我们将表格 7.1.2中橙色区域与下图中矩形区域的数据一一对应:

图 7.1.5 BMP图像(1280x720p)文件头
对比后可得到如下结果:
1、bfSize:位图文件的大小为0x2a3036,即2764854字节(2.63MB),与示例图片的属性界面一致。需要注意的是,在BMP文件中,如果一个数据需要用多个字节来表示的话,那么该数据的低字节存放在低地址,高字节存放在高地址;
2、bfOffBits:文件头到图像数据之间的偏移量为0x36,即54字节。这个偏移量非常有用,我们可以利用它快速定位BMP文件中的图像像素数据的位置。
接下来是BMP信息头的数据结构,如下表所示:
同样,将表格 7.1.3中橙色区域与下图中矩形区域的数据一一对应:

图 7.1.6 BMP图像(1280x720p)信息头
对比后可得到如下结果:
- biWidth:图像的宽度为0x500,即1280像素;
- biHeight:图像的高度为0x2d0,即720像素;
- biBitCount:像素的位深度为0x18,即24位;
- biSizeImage:图像的大小为0x2a3000,即2764800字节。
最后需要注意的是,BMP文件存储数据时,图像的扫描方式是按从下到上、从左到右的顺序。也就是说,BMP文件中第一个像素数据,对应的是图像的左下角。而我们习惯性地认为图像的扫描是从左上角开始的,这一点在编写代码时非常容易出错。
在SD卡图片显示的过程中,需要在ZYNQ的PS端解析出BMP图片的文件头,并从文件中取出图像的像素数据。在后续的章节中,我们会将BMP图片用于FPGA图像处理算法的仿真。因此,了解BMP图片的文件格式是非常必要的。
7.2 软件设计
打开Vitis Classic版本,在启动界面选择DL1_ZYNQ_VIP_OV5640_HDMI_720P工程目录下的Vitis文件夹作为工作空间。然后按照6.1章节中的流程,在Vitis中创建一个空白的应用工程用于SD卡图片显示,工程名为“ZYNQ_VIP_SD_PIC_HDMI”。创建完成后的应用工程如下图所示,我们需要在图中红色箭头所指示的src目录中添加源文件:

图 7.2.1 SD卡图片显示应用工程
磊哥把Vitis软件工程的源文件放在了DaLei_FPGA\vitis_src文件夹里面。打开工程目录下的DaLei_FPGA\vitis_src\ ZYNQ_VIP_SD_PIC_HDMI文件夹,其中包含SD卡图片显示所使用的C程序源码:

图 7.2.2 SD卡图片显示的C程序源码
将上述源码拷贝到Vitis应用工程目录下的src文件夹中,路径为 Vitis\ZYNQ_VIP_SD_PIC_HDMI\src,如下图所示:

图 7.2.3 将C源码拷贝到应用工程路径中
接下来我们返回到Vitis中,在ZYNQ_VIP_SD_PIC_HDMI工程的src目录下已经可以看到我们刚刚添加的源文件。如果没有显示出来,可以在src目录上点击右键,然后选择Refresh手动刷新。

图 7.2.4 在Vitis中刷新应用工程的src目录
双击打开src目录中的main.c文件,可以查看本次实验软件设计的源码。它的功能是从SD卡中读取多张BMP格式的图片,并将其循环显示在HDMI显示器上。其中主函数的源码如下:

在main()函数中,VDMA配置相关的代码与《第六章 HDMI彩条显示实验》完全相同,大家可以参考6.1章节中的讲解。在代码的第52行,通过一个for循环从SD卡中读取多张BMP格式的图片,并将其依次写入到VDMA的帧缓存中。其中第54行指定了BMP图片的文件名格式,我们放在SD卡中的图片必须按照该格式进行命名,如PIC001.bmp,PIC002.bmp等。程序能够读取的BMP图片总数由变量pic_total_num指定,这里我们将图片个数设置为5,如代码第33行所示。
在代码的第57行调用了一个名为load_sd_bmp()的函数,用来读取SD卡中的图片,其定义如下所示:

上面的程序主要是通过调用FATFS库函数,来读取SD卡中的BMP图片文件。如代码第84行所示,我们首先要读取BMP文件开头54个字节的数据,其中包含了BMP图片的“文件头”和“信息头”。然后在代码的90至96行,我们根据BMP信息头中各数据的偏移地址,找到并打印出图像的分辨率和大小等信息。
在读取BMP文件之前,我们先通过调用f_lseek(&fil,0)函数将文件的读写指针移动到文件开头,如代码第83行所示。然后在读取54个字节的BMP文件头和信息头之后,读写指针便移动到了BMP文件中图像数据的起始位置。接下来就可以通过f_read()函数读取后续的图像像素数据。
由于BMP格式的图片在存储数据时,图像的扫描方式是按照从下到上、从左到右的顺序,因此如果我们直接将读取的图像数据写入帧缓存,那么最终显示出来的将是一个上下颠倒的图片。在代码的第99至101行,我们通过一个for循环来读取一整幅BMP图片,每次读取一行图像;然后把先读出的像素数据放到了帧缓存中靠后的地址。也就是说,读出来的第一行像素数据实际上是BMP图片的最后一行图像,因此要把它放在帧缓存的最后一行。通过这个for循环,我们就可以将上下颠倒的图像给反过来。
在load_sd_bmp()函数的最后,通过调用Xil_DCacheFlush()函数将缓存在DataCache中的数据刷新到DDR3中,供PL中的模块读取并在HDMI显示器上显示出来。
在ZYNQ_VIP_SD_PIC_HDMI应用工程上点击鼠标右键,然后在弹出的菜单栏中选择Build Project进行编译,如下图所示:

编译完成后,在控制台可以看到报错信息“fatal error: ff.h: No such file or directory”,如下图所示:

图 7.2.6 控制台中的编译报错信息
这是因为我们在代码的第6行引用了一个名为“ff.h”的头文件,它包含了FAT文件系统相关的函数声名,我们要靠这些函数来实现对SD卡中文件的读写操作。
FATFS是一个完全开源免费的FAT文件系统模块,专门为小型的嵌入式系统而设计。它采用标准C语言编写,具有良好的硬件平台独立性,可以很方便的移植到各种嵌入式处理器中。
FATFS支持FAT12/FAT16和FAT32文件系统,其应用非常广泛。在应用层,使用者无需理会FATFS的内部结构和复杂的FAT协议,只需要调用FATFS 模块提供给用户的一系列应用接口函数,如f_open(),f_read(),f_write()和f_close()等,就可以像在PC上读写文件一样简单。
Vitis在Standalone软件层中已经移植好了FATFS文件系统,但是需要在BSP(板级支持包)中手动添加xilffs库,否则在编译时就会报错。在BSP中添加xilffs库的步骤如下:
- 展开下图中红色方框所标记的软件开发平台DL_ZYNQ_VIP_SYSTEM,双击其中的platform.spr;
- 在右侧详情界面双击红色箭头2所指示的Board Support Package(板级支持包);
- 最后点击Modify BSP Settings,修改板级支持包的设置,如红色箭头3所指示。

图 7.2.7 修改BSP设置
板级支持包的设置界面如图 7.2.8,在Supported Libraries中勾选红色箭头1所指示的xilffs库;然后在左上角的Overview > standalone目录中就会出现xilffs库,由红色箭头2所指示:

图 7.2.8 板级支持包(BSP)设置界面
点击左侧standalone目录下的xilffs库可以打开其配置界面,如下图所示:
在图 7.2.9中,use_lfn一栏默认的Value值为0,将其修改为1(如红色箭头1所示),这样就可以在FATFS中使能长文件名(Long File Name)。最后点击BSP配置界面右下角的OK完成配置。
由于修改了BSP配置,Vitis中的软件开发平台会显示过期(Out-of-date),选中DL_ZYNQ_VIP_SYSTEM后点击鼠标右键,然后在菜单栏中选择Build Project重新编译,如下图所示:
图 7.2.10 重新编译软件开发平台
到这里,我们就已经成功地为软件工程添加了xilffs库。接下来按照图 7.2.5中的步骤重新编译ZYNQ_VIP_SD_PIC_HDMI应用工程,控制台显示“Finished building: ZYNQ_VIP_SD_PIC_HDMI.elf.size”则说明SD卡图片显示应用工程编译成功,如下图所示:
图 7.2.11 应用工程编译成功
7.3 下载验证
SD卡图片显示实验的下载验证需要准备以下物品:1、【征服者】ZYNQ开发板;2、USB Type C数据线;3、HDMI显示器;4、HDMI线缆;5、Micro SD卡(TF卡)及读卡器。
首先将Micro SD卡插入读卡器,然后将读卡器插到电脑的USB接口上。接下来在Windows系统中对SD卡进行格式化,并确认文件系统选择为“FAT32”,如下图所示:
图 7.3.1 格式化SD卡为FAT32
磊哥在工程文件夹的DaLei_FPGA\pic目录下准备了5张BMP格式的测试图片,分辨率为1280 x 720p,如下图所示: 
图 7.3.2 分辨率为1280x720的测试图片
如果大家要添加自己的图片,需要确认图片的格式为BMP,分辨率为1280x720p,并按照PIC001.bmp的格式进行重命名。当图片的数量超过5张时,还需要同步修改main.c代码第33行中的变量pic_total_num。
将上述测试图片拷贝到SD卡的根目录下,然后将Micro SD卡插到【征服者】开发板背面的卡槽中,如下图所示:

图 7.3.3 将Micro SD卡插入卡槽
我们使用Type C数据线连接电脑与【征服者】开发板上的JTAG接口,然后使用HDMI线缆连接开发板的HDMI接口与HDMI显示器;最后将开发板上的两个“编程模式开关”拨到左侧,即使用JTAG模式下载。如下图所示:

图 7.3.4 连接征服者ZYNQ开发板
接下来按照5.3.2章节所描述的流程在Vitis中打开串口终端,选择与开发板连接的串口端口,并将波特率设置为115200。
然后打开软件的下载配置界面。在Vitis IDE左侧的Explorer栏中右键点击ZYNQ_VIP_SD_PIC_HDMI工程,在弹出的菜单栏里选择Run as > Run Configurations。
在下载配置界面中,双击左侧的Single Application Debug,如下图中红色箭头1所指示:

图 7.3.5 下载配置界面
在Run Configurations界面左侧选中新增的Debugger_ZYNQ_VIP_SD_PIC_HDMI-Default,然后在界面右侧切换到Target Setup标签页,最后点击右下角的Run开始下载。
下载过程中分为两个步骤进行。首先使用DL_ZYNQ_VIP_SYSTEM_wrapper.bit文件对ZYNQ的PL端进行硬件编程,Bitstream下载完成后,开发板上的FPGA配置指示灯会点亮。
然后使用ZYNQ_VIP_SD_PIC_HDMI.elf文件对PS端进行软件编程,下载完成后在串口终端会打印出信息“ZYNQ VIP System: SD Card Picture Display!”,如下图所示:
图 7.3.6 串口终端打印信息
在程序运行过程中,串口终端会循环打印SD卡中读取的BMP图片的分辨率和图像大小,间隔为1s。此时观察HDMI显示器,可以看到循环显示的SD卡图片,如下图所示:

图 7.3.7 HDMI显示器上的SD卡图片1

图 7.3.8 HDMI显示器上的SD卡图片2
到这里,我们已经成功地验证了ZYNQ视频图像处理系统的SD卡图片显示功能。在接下来的章节中,我们将继续进行OV5640摄像头显示等功能的开发。

907

被折叠的 条评论
为什么被折叠?



