《DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南》第二十四章 SD卡读BMP图片LCD显示实验​
  95kVyaJuybju 2023年11月12日 16 0

SD卡读BMP图片LCD显示实验

在“SD卡读写TXT文本实验”中利用FATFS在SD卡中实现TXT文本的创建写入与读取。在本次实验中,我们将学习如何SD卡中读取BMP图片,并将其显示在LCD上

本章包括以下几个部分:

  1. 简介
  2. 实验任务
  3. 硬件设计
  4. 软件设计
  5. 下载验证


简介​

我们常用的图片格式有很多,一般最常用的有四种:JPEG(或JPG)、BMP、PNG和GIF。其中JPEG(或JPG)、BMP和PNG是静态图片,而GIF则是动态图片。BMP全称是Bitmap(位图)的缩写,其特点是几乎不进行压缩,由此导致了它与生俱来的缺点,即占用磁盘空间较大;而其它三种图片格式均进行了不同程度的压缩,以节省磁盘空间。在本次实验中,我们选择使用不压缩的BMP图片格式,解析该格式的图片最为简单。

BMP(全称Bitmap)是Window操作系统中的标准图像文件格式,文件后缀名为“.bmp”,使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大,但是没有失真。BMP文件的图像深度可选lbit、4bit、8bit、16bit、24bit及32bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。

典型的BMP图像文件由四部分组成:

1、BMP文件头,它包含BMP图像文件的类型、大小等信息;

2、BMP信息头,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;

3、调色板,这个部分是可选的,如果使用索引来表示图像,调色板就是索引与其对应颜色的映射表;

4、位图数据,即图像数据,在位深为24位直接使用RGB格式,而小于24位时使用调色板中颜色的索引值。

各个部分的大小如下图所示:

《DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南》第二十四章 SD卡读BMP图片LCD显示实验​_数据


24.1.1 BMP文件各部分及其大小

我们一般见到的图像以24位图像为主,即R、G、B三种颜色各用8个bit来表示,这样的图像我们称之为真彩色。在这种情况下是不需要调色板的,位图信息头后面紧跟的就是位图数据了。也就是说,位图文件从文件头开始偏移54个字节就是图像数据了。在这里我们24位BMP图片为,如24.1.2所示,详细介绍其文件结构

《DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南》第二十四章 SD卡读BMP图片LCD显示实验​_数据_02


24.1.2 示例图片:24位BMP图片

首先我们来看一下图片在Window中的属性信息,如下图所示:

《DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南》第二十四章 SD卡读BMP图片LCD显示实验​_位图_03


24.1.3 示例图片属性

24.1.3包括BMP图片的文件属性以及图像属性,文件大小为1.09MB,图像分辨率为800*480,每个像素的颜色使用24位表示

下来,我们使用Notepad++十六进制格式打开BMP文件,如下图所示:

《DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南》第二十四章 SD卡读BMP图片LCD显示实验​_位图_04


24.1.4 示例图片16进制数据

24.1.4红色矩形区域为BMP文件头,共14字节;蓝色区域BMP信息头,共40字节;剩余部分为图像数据。左下角红色椭圆区域表明整个BMP文件共1152054个字节,除去文件头和信息头所占的54个字节,图像数据为1152000字节由于示例图片个像素点使用3个字节表示颜色此我们可以计算出图像数据的小为800*480*3 = 1152000字节,Notepad++计算得到的结果一致

首先来了一下BMP文件头的数据结构,如下表所示:

24.1.1 BMP文件数据结构

变量

址偏移

大小

作用

bfType

00h

2Bytes

说明文件的类型,该值必须是0x424d,即字符'BM'

bfSize

02h

4 Bytes

说明该位图文件的大小,单位为字节

bfReserved1

06h

2Bytes

保留,必须设置为0

bfReserved2

08h

2Bytes

保留,必须设置为0

bfOffBits

0Ah

4 Bytes

从文件头开始到实际的图像数据之间的偏移量

24.1.1橙色区域与下图矩形区域中的数据一一对应

《DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南》第二十四章 SD卡读BMP图片LCD显示实验​_#include_05


24.1.5 BMP文件

对比得到如下结果

1、bf_Size:位图文件的大小为0x119436,1152054字节(1.09MB),与示例图片属性一致。需要注意的是,在BMP文件中,如果一个数据需要用几个字节来表示的话,那么该数据的低字节存放在低地址,高字节存放在高地址

2、bfOffBits:文件头到图像数据之间的偏移量为0x36,即54字节个偏移非常有用,们可以利用它快速定位BMP文件中的图像数据位置。

接下来是BMP信息头的数据结构,如下表所示:

24.1.2 BMP信息数据结构

变量

址偏移

大小

作用

biSize

0Eh

4 Bytes

说明BMP信息结构所占据的字节数

biWidth

12h

4 Bytes

图像的宽度,单位为像素

biHeight

16h

4 Bytes

图像的高度,单位为像素

biPlanes

1Ah

2 Bytes

为目标设备说明位面数,其值将总是被设为1

biBitCount

1Ch

2 Bytes

像素点,其值为1、4、8、16、24、32

biCompression

1Eh

4 Bytes

图像数据压缩的类型,0(BI_RGB)表示不压缩

biSizeImage

22h

4 Bytes

图像的大小,以字节为单位

biXPelsPerMeter

26h

4 Bytes

水平分辨率,单位为像素/米,可忽略

biYPelsPerMeter

2Ah

4 Bytes

垂直分辨率,单位为像素/米,可忽略

biClrUsed

2Eh

4 Bytes

位图实际使用的调色板中的颜色索引数

0表示使用所有调色板颜色

biClrImportant

32h

4 Bytes

对图象显示有重要影响的颜色索引的数目

0表示所有颜色都重要

样,将表中橙色区域与下图矩形区域中的数据一一对应

《DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南》第二十四章 SD卡读BMP图片LCD显示实验​_数据_06


24.1.6 BMP信息

对比得到如下结果

  1. biWidth:图像的宽度为0x320,即800像素;
  2. biHeight:图像的高度为0x1e0,即480像素;
  3. biBitCount:像素的位深度为0x18,等于24位,即RGB888数据格式;
  4. biSizeImage:图像的大小为0x119400即1152000字节

BMP信息头之后,如果没有调色板,紧跟着就是BMP的位图数据了,位图数据存储了BMP有效的图像数据,其数据个数由图片的分辨率和颜色深度决定。需要说明的,BMP图像数据格式是BGR,以BMP 24位真彩色为例,颜色分量“B”位于低地址位,颜色分量“G”位于中间地址位,颜色分量“R”位于高地址位。

实验任务​

本章的实验任务是使用MPSOC开发板读取SD卡中存放的BMP格式图片并将其显示在LCD上。

硬件设计​

根据实验任务我们可以画出本次实验的系统框图,如下图所示:

《DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南》第二十四章 SD卡读BMP图片LCD显示实验​_位图_07


24.3.1 系统框图

24.3.1与“PS通过VDMA驱动LCD显示实验”中的系统框图基本相同,其中各个模块的功能介绍请大家参考相应的章节。唯一不同的地方是,在“PS通过VDMA驱动LCD显示实验”中我们显示在LCD上的是CPU在DDR4内存中绘制的彩条图案。而在本次实验中,我们需要从SD卡中读取BMP图片,并将其写到DDR4相应的内存空间,用于LCD显示

次实验硬件环境可以在“PS通过VDMA驱动LCD显示实验”的基础上搭建。由于需要读SD卡的功能,因此在配置Zynq UltraScale+ MPSOC模块时,需要使能SD卡控制外设,具体方法请参考“SD卡读写TXT文本实验”中的硬件设计部分。

软件设计​

为了能够在软件中读取SD卡中的文件们需要设置BSP工程,添加设置FATFS库具体的方法请参考“SD卡读写TXT文本实验”软件设计部分。

接下来修改main.c文件中的代码,修改完成后代码部分如下所示:

1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "xil_types.h"
5 #include "xil_cache.h"
6 #include "xparameters.h"
7 #include "xgpio.h"
8 #include "xaxivdma.h"
9 #include "xaxivdma_i.h"
10 #include "display_ctrl/display_ctrl.h"
11 #include "vdma_api/vdma_api.h"
12 #include "clk_wiz/clk_wiz.h"
13 #include "ff.h"
14
15 //宏定义
16 #define CLK_WIZ_ID XPAR_CLK_WIZ_0_DEVICE_ID //时钟IP核器件ID
17 #define VDMA_ID XPAR_AXIVDMA_0_DEVICE_ID //VDMA器件ID
18 #define DISP_VTC_ID XPAR_VTC_0_DEVICE_ID //VTC器件ID
19 #define AXI_GPIO_0_ID XPAR_AXI_GPIO_0_DEVICE_ID //PL端 AXI GPIO 0(lcd_id)器件ID
20 #define AXI_GPIO_0_CHANEL 1 //使用AXI GPIO(lcd_id)通道1
21
22 //函数声明
23 void load_sd_bmp(u8 *frame);
24
25 //全局变量
26 XAxiVdma vdma;
27 DisplayCtrl dispCtrl;
28 XGpio axi_gpio_inst; //PL端 AXI GPIO 驱动实例
29 VideoMode vd_mode;
30 //frame buffer的起始地址
31 unsigned int const frame_buffer_addr = (XPAR_PSU_DDR_0_S_AXI_BASEADDR + 0x1000000);
32 unsigned int lcd_id=0; //LCD ID
33
34 int main(void)
35 {
36 XGpio_Initialize(&axi_gpio_inst,AXI_GPIO_0_ID); //GPIO初始化
37 XGpio_SetDataDirection(&axi_gpio_inst,AXI_GPIO_0_CHANEL,0x07); //设置AXI GPIO为输入
38 lcd_id = LTDC_PanelID_Read(&axi_gpio_inst,AXI_GPIO_0_CHANEL); //获取LCD的ID
39 XGpio_SetDataDirection(&axi_gpio_inst,AXI_GPIO_0_CHANEL,0x00); //设置AXI GPIO为输出
40 xil_printf("LCD ID: %x\r\n",lcd_id);
41
42 //根据获取的LCD的ID号来进行video参数的选择
43 switch(lcd_id){
44 case 0x4342 : vd_mode = VMODE_480x272; break; //4.3寸屏,480*272分辨率
45 case 0x4384 : vd_mode = VMODE_800x480; break; //4.3寸屏,800*480分辨率
46 case 0x7084 : vd_mode = VMODE_800x480; break; //7寸屏,800*480分辨率
47 case 0x7016 : vd_mode = VMODE_1024x600; break; //7寸屏,1024*600分辨率
48 case 0x1018 : vd_mode = VMODE_1280x800; break; //10.1寸屏,1280*800分辨率
49 default : vd_mode = VMODE_800x480; break;
50 }
51
52 //配置VDMA
53 run_vdma_frame_buffer(&vdma, VDMA_ID, vd_mode.width, vd_mode.height,
54 frame_buffer_addr,0, 0,ONLY_READ);
55
56 //设置时钟IP核输出的时钟频率
57 clk_wiz_cfg(CLK_WIZ_ID,vd_mode.freq);
58 //初始化Display controller
59 DisplayInitialize(&dispCtrl, DISP_VTC_ID);
60 //设置VideoMode
61 DisplaySetMode(&dispCtrl, &vd_mode);
62 DisplayStart(&dispCtrl);
63
64 //读取SD卡图片并显示
65 load_sd_bmp((u8*)frame_buffer_addr);
66
67 return 0;
68 }
69
70 //从SD卡中读取BMP图片
71 void load_sd_bmp(u8 *frame)
72 {
73 static FATFS fatfs;
74 FIL fil;
75 u8 bmp_head[54];
76 UINT *bmp_width,*bmp_height,*bmp_size;
77 UINT br;
78 int i;
79
80 //挂载文件系统
81 f_mount(&fatfs,"",1);
82
83 //打开文件
84 f_open(&fil,"fengjing.bmp",FA_READ);
85
86 //移动文件读写指针到文件开头
87 f_lseek(&fil,0);
88
89 //读取BMP文件头
90 f_read(&fil,bmp_head,54,&br);
91 xil_printf("fengjing.bmp head: \n\r");
92 for(i=0;i<54;i++)
93 xil_printf(" %x",bmp_head[i]);
94
95 //打印BMP图片分辨率和大小
96 bmp_width = (UINT *)(bmp_head + 0x12);
97 bmp_height = (UINT *)(bmp_head + 0x16);
98 bmp_size = (UINT *)(bmp_head + 0x22);
99 xil_printf("\n width = %d, height = %d, size = %d bytes \n\r",
100 *bmp_width,*bmp_height,*bmp_size);
101
102 //读出图片,写入DDR
103 for(i=*bmp_height-1;i>=0;i--){
104 f_read(&fil,frame+i*(*bmp_width)*3,(*bmp_width)*3,&br);
105 }
106
107 //关闭文件
108 f_close(&fil);
109
110 Xil_DCacheFlush(); //刷新Cache,数据更新至DDR4中
111 xil_printf("show bmp\n\r");
112 }

上面的程序要是通过调用FATFS库函数取SD卡中BMP图片文件章的简介部分详细介绍了BMP图片的数据格式,我们首先读取BMP文件前面54个字节的数据,如程序第90所示,其中BMP图片的分辨率等信息。然后在程序的95至100,我们根据BMP信息头中各数据的偏移地址,找到并打印出图像的分辨率和大小信息。

在读取BMP文件之前,我们先通过调用f_lseek(&fil,0)函数将文件的读写指针移动到文件开头。然后读取54个字节的数据之后,读写指针便移动BMP文件中图像数据的起始位置。接下来就可以通过f_read()函数继续读取下面的图像数据

我们需要注意的是程序的第102105行。由于BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序,因此如果我们直接将一整幅图片存入DDR显存,那么最终显示出来的将是一个上下颠倒的图片。在这里我们通过一个for循环来读取一整幅BMP图片,从上到下每次读取一行,然后把先读出来的数据放到了图片显存后面的位置。也就是说,读出来的第一行数据实际上是BMP图片的最后一行图像,因此要把它放在显存的最后一行。通过这个for循环,我们就可以将上下颠倒的图像给反过来。

最后通过调用Xil_DCacheFlush()函数将缓存在DataCache中的数据刷新到DDR4中,供PL中的模块读取并在LCD上显示

这里本次实验的软件设计就介绍完了,如果大家FATFS库函数的使用方法不熟悉的话,请参考“SD卡读写TXT文本实验”中的软件设计部分。

下载验证​

首先我将下载器与开发板上的JTAG接口连接,下载器另外一端与电脑连接然后使用USB连接线将USB UART接口(PS_PORT)与电脑连接,用于口通信。接下来使用FPC排线一端与RGB LCD液晶屏上的接口连接,另一端连接开发板上的RGB LCD接口。

然后章简介部分所给出示例图片重命名“fengjing.bmp”,并拷贝SD卡的根目录下最后Micro SD卡插入开发板背面的卡槽

需要注意的是,拷贝到Micro SD卡中的BMP图片分辨率应与开发板所连接的LCD屏幕分辨率保持一致。我们在工程目录下新建了一个名为“风景图片”的文件夹,里面有四种不同分辨率的图片,如下图所示:

《DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南》第二十四章 SD卡读BMP图片LCD显示实验​_数据_08


24.5.1 不同分辨率的BMP图片

比如本次实验使用的LCD屏幕分辨率为800*480,那么就将上图中名为“fengjing_800x480.bmp”的图片拷贝到Micro SD卡根目录下。在拷贝完成后,千万不要忘记将Micro SD中图片的名称修改为“fengjing.bmp”。

另外本次实验要求使用的Micro SD卡为FAT32格式,如果不是,那么在使用前需要将其格式化为FAT32格式。

准备工作完成之后,接下来连接开发板的电源。打开Vitis Terminal终端,设置连接串口。然后下载本次实验程序下载完成后,在下方的Terminal中可以看到应用程序打印的信息,如下图所示:

《DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南》第二十四章 SD卡读BMP图片LCD显示实验​_#include_09


24.5.2 串口打印信息

24.5.2打印出了BMP图片的文件和信息头等信息,24.1.4的数据保持一致从数据中计算出BMP图片的宽度为800,高度为480。另外根据读出的LCD屏幕ID看出,所使用的LCD屏幕分辨率为800*480,与Micro SD卡中的BMP图片分辨率一致。如果不一致,那么屏幕上就无法正常显示图片。

LCD屏幕上显示的图片如下图所示,说明本次实验在MPSOC开发板上面下载验证成功。

《DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南》第二十四章 SD卡读BMP图片LCD显示实验​_位图_10


24.5.3 下载验证


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月12日 0

暂无评论

95kVyaJuybju