OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 54|回复: 0

《STM32H7R7开发指南 V1.1 》第五十六章 图片显示实验

[复制链接]

1345

主题

1361

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5742
金钱
5742
注册时间
2019-5-8
在线时间
1557 小时
发表于 11 小时前 | 显示全部楼层 |阅读模式
第五十六章 图片显示实验

1)实验平台:正点原子STM32H7R7开发板

2)章节摘自【正点原子】STM32H7R7开发指南 V1.1

3)购买链接: https://detail.tmall.com/item.htm?id=820823382459

4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/stm32/zdyz_stm32h7rx.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子STM32开发板技术交流群:756580169


2.jpg

3.png

在开发产品的时候,很多时候,我们都会用到图片解码,在本章中,我们将向大家介绍如何通过STM32H7R7来解码BMP/JPG/JPEG/GIF等图片,并在LCD上显示出来。
本章分为如下几个部分:
56.1 图片格式简介
56.2 硬件设计
56.3 程序设计
56.4 下载验证


56.1 图片格式介绍
我们常用的图片格式有很多,一般最常用的有三种:JPEG(或JPG)、BMP和GIF。其中JPEG(或JPG)和BMP是静态图片,而GIF则是可以实现动态图片。下面,我们简单介绍一下这三种图片格式。

56.1.1 BMP编码简介
我们常用的图片格式有很多,一般最常用的有三种:JPEG(或JPG)、BMP和GIF。其中JPEG(或JPG)和BMP是静态图片,而GIF则是可以实现动态图片。下面,我们简单介绍一下这三种图片格式。
首先,我们来看看BMP图片格式。BMP(全称Bitmap)是Window操作系统中的标准图像文件格式,文件后缀名为“.bmp”,使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大,但是没有失真。BMP文件的图像深度可选lbit、4bit、8bit、16bit、24bit及32bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。
典型的BMP图像文件由四部分组成:
1、位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;
2、位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;
3、调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;
4、位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。
关于BMP的详细介绍,请参考光盘的《BMP图片文件详解.pdf》。


56.1.2 JPEG编码简介
JPEG是Joint Photographic Experts Group(联合图像专家组)的缩写,文件后辍名为“.jpg”或“.jpeg”,是最常用的图像文件格式,由一个软件开发联合会组织制定,同BMP格式不同,JPEG是一种有损压缩格式,能够将图像压缩在很小的储存空间,图像中重复或不重要的资料会被丢失,因此容易造成图像数据的损伤(BMP不会,但是BMP占用空间大)。尤其是使用过高的压缩比例,将使最终解压缩后恢复的图像质量明显降低,如果追求高品质图像,不宜采用过高压缩比例。但是JPEG压缩技术十分先进,它用有损压缩方式去除冗余的图像数据,在获得极高的压缩率的同时能展现十分丰富生动的图像,换句话说,就是可以用最少的磁盘空间得到较好的图像品质。而且JPEG是一种很灵活的格式,具有调节图像质量的功能,允许用不同的压缩比例对文件进行压缩,支持多种压缩级别,压缩比率通常在10:1到40:1之间,压缩比越大,品质就越低;相反地,压缩比越小,品质就越好。比如可以把1.37Mb的BMP位图文件压缩至20.3KB。当然也可以在图像质量和文件尺寸之间找到平衡点。JPEG格式压缩的主要是高频信息,对色彩的信息保留较好,适合应用于互联网,可减少图像的传输时间,可以支持24bit真彩色,也普遍应用于需要连续色调的图像。
JPEG/JPG的解码过程可以简单的概述为如下几个部分:
1、从文件头读出文件的相关信息。
JPEG 文件数据分为文件头和图像数据两大部分,其中文件头记录了图像的版本、长宽、采样因子、量化表、哈夫曼表等重要信息。所以解码前必须将文件头信息读出,以备图像数据解码过程之用。
2、从图像数据流读取一个最小编码单元(MCU) ,并提取出里边的各个颜色分量单元。
3、将颜色分量单元从数据流恢复成矩阵数据。
使用文件头给出的哈夫曼表,对分割出来的颜色分量单元进行解码,把其恢复成8×8 的数据矩阵。
4、8×8 的数据矩阵进一步解码。
此部分解码工作以8×8 的数据矩阵为单位, 其中包括相邻矩阵的直流系数差分解码、使用文件头给出的量化表反量化数据、反Zig- zag 编码、隔行正负纠正、反向离散余弦变换等5 个步骤, 最终输出仍然是一个8×8 的数据矩阵。
5、颜色系统YCrCb 向RGB 转换。
将一个MCU的各个颜色分量单元解码结果整合起来,将图像颜色系统从YCrCb 向RGB 转换。
6、排列整合各个MCU 的解码数据。
不断读取数据流中的MCU 并对其解码,直至读完所有MCU 为止,将各MCU 解码后的数据正确排列成完整的图像。
JPEG的解码本身是比较复杂的,这里FATFS的作用,提供了一个轻量级的JPG/JPEG解码库:TjpgDec,最少仅需3KB的RAM和3.5KB的FLASH即可实现JPG/JPEG解码,本例程采用TjpgDec作为JPG/JPEG的解码库,关于TjpgDec的详细使用,请参考光盘:6,软件资料\图片编解码\TjpgDec技术手册这个文档。
BMP和JPEG这两种图片格式均不支持动态效果,而GIF则是可以支持动态效果。最后,我们来看看GIF图片格式。


56.1.3 GIF编码简介
GIF(Graphics Interchange Format)是CompuServe公司开发的图像文件存储格式,1987年开发的GIF文件格式版本号是GIF87a,1989年进行了扩充,扩充后的版本号定义为GIF89a。GIF图像文件以数据块(block)为单位来存储图像的相关信息。一个GIF文件由表示图形/图像的数据块、数据子块以及显示图形/图像的控制信息块组成,称为GIF数据流(DataStream)。数据流中的所有控制信息块和数据块都必须在文件头(Header)和文件结束块(Trailer)之间。
GIF文件格式采用了LZW(Lempel-ZivWalch)压缩算法来存储图像数据,定义了允许用户为图像设置背景的透明(transparency)属性。此外,GIF文件格式可在一个文件中存放多幅彩色图形/图像。如果在GIF文件中存放有多幅图,它们可以像演幻灯片那样显示或者像动画那样演示。
一个GIF文件的结构可分为文件头(FileHeader)、GIF数据流(GIFDataStream)和文件终结器(Trailer)三个部分。文件头包含GIF文件署名(Signature)和版本号(Version);GIF数据流由控制标识符、图象块(ImageBlock)和其他的一些扩展块组成;文件终结器只有一个值为0x3B的字符(';')表示文件结束。
关于GIF的详细介绍,请参考光盘GIF解码相关资料。图片格式简介,我们就介绍到这里。


56.2 硬件设计

1. 例程功能
开机的时候先检测字库,然后检测SD卡是否存在,如果SD卡存在,则开始查找SD卡根目录下的PICTURE文件夹,如果找到则显示该文件夹下面的图片文件(支持bmp、jpg、jpeg或gif格式),循环显示,通过按WKUP和KEY0可以快速浏览上一张和下一张。如果未找到PICTURE文件夹/任何图片文件,则提示错误。
LED0闪烁,提示程序运行。

2. 硬件资源
1)LED灯
       LED0 :LED0 – PD14
       LED1 :LED1 – PC0
2)串口1(PB14/PB15连接在板载USB转串口芯片CH340上面)
3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(包括MCU屏和RGB屏,都支持)
4)独立按键:WK_UP – PC13
5)SD卡,通过SDMMC(SDMMC_D0~D4(PC8~PC11),SDMMC_SCK(PC12),
       SDMMC_CMD(PD2))连接
6)norflash


56.3 程序设计

56.3.1 程序解析

1. PICTURE代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。PICTURE驱动源码包括八个文件:bmp.c、bmp.h、tjpgd.c、tjpgd.h、gif.c、gif.h、piclib.c和piclib.h。
其中:
bmp.c和bmp.h用于实现对bmp文件的解码;
tjpgd.c和tjpgd.h用于实现对jpeg/jpg文件的解码;
gif.c和gif.h用于实现对gif文件的解码;
这几个代码太长了,而且也有规定的标准,需要结合各个图片编码的格式来编写,所以我们在这里不贴出来,大家查看光盘中的源码的实现过程即可。下面我们重点讲解这几个解码库对应到我们的LCD的显示部分。

1)解码库的控制句柄_pic_phy和_pic_info
我们使用这个接口,把解码后的图形数据与LCD的实际操作对应起来。为了方便去显示图片,我们需要将图片的信息与我们的LCD联系上。这里我们定义了_pic_phy和_pic_info分别用于定义图片解码库的LCD操作和存放解码后的图片尺寸颜色信息。它们的定义如下:

  1. /* 在移植的时候,必须由用户自己实现这几个函数 */
  2. typedef struct
  3. {
  4.     uint32_t(*read_point)(uint16_t, uint16_t);       /* 读点函数 */
  5.     void(*draw_point)(uint16_t, uint16_t, uint32_t); /* 画点函数 */
  6.     /* 单色填充函数 */
  7.     void(*fill)(uint16_t, uint16_t, uint16_t, uint16_t, uint32_t);
  8.     /* 画水平线函数 */
  9.     void(*draw_hline)(uint16_t, uint16_t, uint16_t, uint16_t);
  10.     /* 颜色填充 */
  11.     void(*fillcolor)(uint16_t, uint16_t, uint16_t, uint16_t, uint16_t *);
  12. } _pic_phy;

  13. /* 图像信息 */
  14. typedef struct
  15. {
  16.     uint16_t lcdwidth;              /* LCD的宽度 */
  17.     uint16_t lcdheight;             /* LCD的高度 */
  18.     uint32_t ImgWidth;              /* 图像的实际宽度和高度 */
  19.     uint32_t ImgHeight;
  20.     uint32_t Div_Fac;               /* 缩放系数 (扩大了8192倍的) */
  21.     uint32_t S_Height;              /* 设定的高度和宽度 */
  22.     uint32_t S_Width;
  23.     uint32_t S_XOFF;                /* x轴和y轴的偏移量 */
  24.     uint32_t S_YOFF;
  25.     uint32_t staticx;               /* 当前显示到的xy坐标 */
  26.     uint32_t staticy;
  27. } _pic_info;
复制代码
在piclib.c文件中,我们用上述类型定义了两个结构体,具体如下:
  1. _pic_info picinfo;      /* 图片信息 */
  2. _pic_phy pic_phy;       /* 图片显示物理接口 */
复制代码

2)piclib_init函数
piclib_init函数,该函数用于初始化图片解码的相关信息,用于定义解码后的LCD操作。 具体定义如下:

  1. /**
  2. * @brief       画图初始化
  3. * [url=home.php?mod=space&uid=60778]@note[/url]        在画图之前,必须先调用此函数, 指定相关函数
  4. * [url=home.php?mod=space&uid=271674]@param[/url]       无
  5. * @retval      无
  6. */
  7. void piclib_init(void)
  8. {
  9.     pic_phy.read_point = lcd_read_point;            /* 读点函数实现,仅BMP需要 */
  10.     pic_phy.draw_point = lcd_draw_point;            /* 画点函数实现 */
  11.     pic_phy.fill = lcd_fill;                          /* 填充函数实现,仅GIF需要 */
  12.     pic_phy.draw_hline = lcd_draw_hline;              /* 画线函数实现,仅GIF需要 */
  13.     pic_phy.fillcolor = piclib_fill_color;         /* 颜色填充函数实现,仅TJPGD需要 */
  14.     picinfo.lcdwidth = lcddev.width;                /* 得到LCD的宽度像素 */
  15.     picinfo.lcdheight = lcddev.height;             /* 得到LCD的高度像素 */
  16.     picinfo.ImgWidth = 0;                             /* 初始化宽度为0 */
  17.     picinfo.ImgHeight = 0;                            /* 初始化高度为0 */
  18.     picinfo.Div_Fac = 0;                              /* 初始化缩放系数为0 */
  19.     picinfo.S_Height = 0;                             /* 初始化设定的高度为0 */
  20.     picinfo.S_Width = 0;                              /* 初始化设定的宽度为0 */
  21.     picinfo.S_XOFF = 0;                               /* 初始化x轴的偏移量为0 */
  22.     picinfo.S_YOFF = 0;                               /* 初始化y轴的偏移量为0 */
  23.     picinfo.staticx = 0;                              /* 初始化当前显示到的x坐标为0 */
  24.     picinfo.staticy = 0;                              /* 初始化当前显示到的y坐标为0 */
  25. }
复制代码
函数描述:
初始化图片解码的相关信息,这些函数必须由用户在外部实现。我们使用之前LCD的操作函数对这个结构体中的绘制操作:画点、画线、画圆等定义与我们的LCD操作对应起来。具体这些操作可以查看TFT LCD一节的描述。
函数形参:
无。
函数返回值:
无。

3)piclib_alpha_blend函数
RGB色彩中,一个标准像素由32位组成:透明度(8bit)+R(8bit)+B(8bit)+B(8bit),8位的α通道(alpha channel)位表示该像素如何产生特技效果,即通常我们说的半透明。alpha的取值一般为0到255。为0时,表示是全透明的,即图片是看不见的。为255时,表示图片是显示原始图的。中间值即为半透明状态。计算alpha blending时,通常的方法是将源像素的RGB值,分别与目标像素(如背景)的RGB按比例混合,最后得到一个混合后的RGB值。函数定义如下:

  1. /**
  2. * @brief       快速ALPHA BLENDING算法
  3. * @param       src           : 颜色数
  4. * @param       dst           : 目标颜色
  5. * @param       alpha         : 透明程度(0~32)
  6. * @retval      混合后的颜色
  7. */
  8. uint16_t piclib_alpha_blend(uint16_t src, uint16_t dst, uint8_t alpha)
  9. {
  10.     uint32_t src2;
  11.     uint32_t dst2;
  12.     /* Convert to 32bit |-----GGGGGG-----RRRRR------BBBBB| */
  13.     src2 = ((src << 16) | src) & 0x07E0F81F;
  14.     dst2 = ((dst << 16) | dst) & 0x07E0F81F;
  15.     dst2 = ((((dst2 - src2) * alpha) >> 5) + src2) & 0x07E0F81F;
  16.     return (dst2 >> 16) | dst2;
  17. }
复制代码
函数描述:
piclib_alpha_blend函数,该函数用于实现半透明效果,在小格式(图片分辨率小于LCD分辨率)bmp解码的时候,可能被用到。
函数形参:
形参1是为RGB色彩编号,这里我们使用的是RGB565模式,故只有16位;
形参2是目标象素,使用时我们一般指背景颜色。
形参3是透明度:有效范围为0~255,0表示全透明,255表不透明。
函数返回值:
返回计算后的透明度颜色数值。

4)piclib_ai_draw_init函数
对于给定区域,为了显示更好看,一般会选择图片居中显示,此函数实现此功能,把图片在显示区域中居中。函数定义如下:

  1. /**
  2. * @brief       初始化智能画点
  3. * @param       无
  4. * @retval      无
  5. */
  6. void piclib_ai_draw_init(void)
  7. {
  8.     float temp, temp1;
  9.     temp = (float)picinfo.S_Width / picinfo.ImgWidth;
  10.     temp1 = (float)picinfo.S_Height / picinfo.ImgHeight;
  11.     if (temp < temp1)temp1 = temp;  /* 取较小的那个 */
  12.     if (temp1 > 1)temp1 = 1;
  13.     /* 使图片处于所给区域的中间 */
  14.     picinfo.S_XOFF += (picinfo.S_Width - temp1 * picinfo.ImgWidth) / 2;
  15.     picinfo.S_YOFF += (picinfo.S_Height - temp1 * picinfo.ImgHeight) / 2;
  16.     temp1 *= 8192;  /* 扩大8192倍 */
  17.     picinfo.Div_Fac = temp1;
  18.     picinfo.staticx = 0xffff;
  19.     picinfo.staticy = 0xffff;   /* 放到一个不可能的值上面 */
  20. }
复制代码
函数描述:
piclib_ai_draw_init函数,该函数使解码后的图片信息处于所给的区域的中间。
函数形参:
无。
函数返回值:
无。我们可以在显示实例中测试加与不加此函数的显示效果差异。

5)piclib_is_element_ok函数
对于给定区域,为了显示更好看,一般会选择图片居中显示,此函数实现此功能,把图片在显示区域中居中。函数定义如下:

  1. /**
  2. * @brief       判断这个像素是否可以显示
  3. * @param       x, y          : 像素原始坐标
  4. * @param       chg           : 功能变量
  5. * @param       无
  6. * @retval      操作结果
  7. *   @arg       0   , 不需要显示
  8. *   @arg       1   , 需要显示
  9. */
  10. __inline uint8_t piclib_is_element_ok(uint16_t x, uint16_t y, uint8_t chg)
  11. {
  12.     if (x != picinfo.staticx || y != picinfo.staticy)
  13.     {
  14.         if (chg == 1)
  15.         {
  16.             picinfo.staticx = x;
  17.             picinfo.staticy = y;
  18.         }
  19.         return 1;
  20.     }
  21.     else
  22.     {
  23.         return 0;
  24.     }
  25. }
复制代码
函数描述:
piclib_is_element_ok函数,该函数用于判断一个点是不是应该显示出来,在图片缩放的时候该函数是必须用到的。这里用__inline修饰,保证该部分的代码不被优化。
函数形参:
无。
函数返回值:
1:需要显示。
0:不需要显示。
其它函数使用到时,根据此返回值进行判定显示操作。

6)piclib_ai_load_picfile函数
piclib_ai_load_picfile帮助我们得到需要显示的图片信息并助于下一步的绘制。本函数需要结合文件系统来操作,图片根据后缀区分并且在文件夹在保存是我们在PC下的习分类,也是我们处理和分类图片的最方便的方式。

  1. /**
  2. * @brief       智能画图
  3. * @note        图片仅在x,y和width, height限定的区域内显示.
  4. *
  5. * @param       filename      : 包含路径的文件名(.bmp/.jpg/.jpeg/.gif等)
  6. * @param       x, y          : 起始坐标
  7. * @param       width, height : 显示区域
  8. * @param       fast          : 使能快速解码
  9. *   @arg                       0, 不使能
  10. *   @arg                       1, 使能
  11. *   @note                      图片尺寸小于等于液晶分辨率,才支持快速解码
  12. * @retval      res                           : 操作结果 0,成功 其他,错误码
  13. */
  14. uint8_t piclib_ai_load_picfile(const uint8_t *filename, uint16_t x, uint16_t y,
  15. uint16_t width, uint16_t height, uint8_t fast)
  16. {
  17.     uint8_t res;                                              /* 返回值 */
  18.     uint8_t temp;
  19.     if((x + width) > picinfo.lcdwidth)return PIC_WINDOW_ERR;  /* x坐标超范围了 */
  20.     if((y + height) > picinfo.lcdheight)return PIC_WINDOW_ERR;/* y坐标超范围了*/
  21.     /* 得到显示方框大小 */
  22.     if (width == 0 || height == 0)return PIC_WINDOW_ERR;      /* 窗口设定错误 */
  23.     picinfo.S_Height = height;
  24.     picinfo.S_Width = width;
  25.     /* 显示区域无效 */
  26.     if (picinfo.S_Height == 0 || picinfo.S_Width == 0)
  27.     {
  28.         picinfo.S_Height = lcddev.height;
  29.         picinfo.S_Width = lcddev.width;
  30.         return FALSE;
  31.     }
  32.     if (pic_phy.fillcolor == NULL)fast = 0;  /* 颜色填充函数未实现,不能快速显示 */
  33.     /* 显示的开始坐标点 */
  34.     picinfo.S_YOFF = y;
  35.     picinfo.S_XOFF = x;
  36.     /* 文件名传递 */
  37.     temp = exfuns_file_type((uint8_t *)filename); /* 得到文件的类型 */
  38.     switch (temp)
  39.     {
  40.         case T_BMP:
  41.             res = stdbmp_decode(filename);        /* 解码bmp */
  42.             break;
  43.         case T_JPG:
  44.         case T_JPEG:
  45.             res = jpg_decode(filename, fast);   /* 解码JPG/JPEG */
  46.             break;
  47.         case T_GIF:
  48.             res = gif_decode(filename, x, y, width, height);    /* 解码gif */
  49.             break;
  50.         default:
  51.             res = PIC_FORMAT_ERR;               /* 非图片格式!!! */
  52.             break;
  53.     }
  54.     return res;
  55. }
复制代码
函数描述:
piclib_ai_load_picfile函数,整个图片显示的对外接口,外部程序,通过调用该函数,可以实现bmp、jpg/jpeg和gif的显示,该函数根据输入文件的后缀名,判断文件格式,然后交给相应的解码程序(bmp解码/jpeg解码/gif解码),执行解码,完成图片显示。
函数形参:
形参1 filename是文件的路径名,具体可以参考FATFS一节的描述,为字符口,我们的例程采用的是SD卡存图片,故一般为”0:/PICTURE/*.GIF”等类似格式。
形参2为画图的起始x坐标;
形参3为画图的起始y坐标;
形参4的width和形参5的height形成了以x、y为起点的(x,y)~(x+width,y+height)的矩形显示区域,对屏幕坐标不理解的可能参考我们的TFT LCD一节的描述。
形参6根据我们的LCD进行适应的一个快速解的操作,仅jgp/jpeg模式下有效。
这里用到的exfuns_file_type()函数是我们前面FATFS一节提到的FATFS扩展应用,我们用这个函数来判断文件类型,方便我们进行程序设计。这部分参考文件系统下exfuns文件夹下的相关文件。
函数返回值:
0:成功                其他:错误码
由于图片显示需要用到大内存,我们使用动态内存分配来实现,我们仍使用我们自定的内存管理函数来管理程序内存。申请内存函数piclib_mem_malloc()和内存释放函数piclib_mem_free()的实现就比较简单了,大家参考光盘的源码即可。

2. main.c代码
main.c函数我们利用FATFS的接口来操作和查找图片文件,我们在SD卡的根目录下新建一个PICTURE文件夹,然后放置我们准备要显示的BMP、JPG、GIF图片,接下来按我们程序流程图设置的思路,先扫描图像文件的数量并切换显示,并加入按键支持图片翻页,主要的代码如下所示:

  1. int main(void)
  2. {
  3.     uint8_t t = 0;
  4.     uint8_t key;
  5.     uint8_t res;
  6.     DIR picdir;
  7.     uint16_t totpicnum;
  8.     FILINFO *picfileinfo;
  9.     char *pname;
  10.     uint32_t *picoffsettbl;
  11.     uint16_t curindex;
  12.     uint16_t temp;
  13.    
  14.     sys_mpu_config();                   /* 配置MPU */
  15.     sys_cache_enable();                 /* 使能Cache */
  16.     HAL_Init();                         /* 初始化HAL库 */
  17.     sys_stm32_clock_init(300, 6, 2);    /* 配置时钟,600MHz */
  18.     delay_init(600);                    /* 初始化延时 */
  19.     usart_init(115200);                 /* 初始化串口 */
  20.     led_init();                         /* 初始化LED */
  21.     key_init();                         /* 初始化按键 */
  22.     hyperram_init();                    /* 初始化HyperRAM */
  23.     lcd_init();                         /* 初始化LCD */
  24.     my_mem_init(SRAMIN);                /* 初始化AXI-SRAM1~4内存池 */
  25.     my_mem_init(SRAMEX);                /* 初始化XSPI2 HyperRAM内存池 */
  26.     my_mem_init(SRAM12);                /* 初始化AHB-SRAM1~2内存池 */
  27.     my_mem_init(SRAMDTCM);              /* 初始化DTCM内存池 */
  28.     my_mem_init(SRAMITCM);              /* 初始化ITCM内存池 */
  29.     exfuns_init();                      /* 为exfuns申请内存 */
  30.     f_mount(fs[0], "0:", 1);            /* 挂载SD卡 */
  31.     f_mount(fs[1], "1:", 1);            /* 挂载NOR Flash */
  32.     f_mount(fs[2], "2:", 1);            /* 挂载NAND Flash */
  33.    
  34.     /* 检查字库 */
  35.     while (fonts_init() != 0)
  36.     {
  37.         lcd_show_string(30, 30, 200, 16, 16, "STM32", RED);
  38.         
  39.         /* 初始化SD卡 */
  40.         while (sd_init() != 0)
  41.         {
  42.             lcd_show_string(30, 30, 200, 16, 16, "SD Card Error!", RED);
  43.             delay_ms(500);
  44.             lcd_show_string(30, 30, 200, 16, 16, "Please Check! ", RED);
  45.             delay_ms(500);
  46.             LED0_TOGGLE();
  47.         }
  48.         
  49.         lcd_show_string(30, 50, 200, 16, 16, "SD Card OK", RED);
  50.         lcd_show_string(30, 70, 200, 16, 16, "Font Updating...", RED);
  51.         
  52.         /* 更新字库 */
  53.         res = fonts_update_font(30, 90, 16, (uint8_t *)"0:", RED);
  54.         while (res != 0)
  55.         {
  56.             lcd_show_string(30, 90, 200, 16, 16, "Font Update Failed!", RED);
  57.             delay_ms(200);
  58.             lcd_show_string(30, 90, 200, 16, 16, "Please Check!      ", RED);
  59.             delay_ms(200);
  60.         }
  61.         
  62.         lcd_show_string(30, 90, 200, 16, 16, "Font Update Success!   ", RED);
  63.         delay_ms(1500);
  64.         lcd_clear(WHITE);
  65.     }
  66.    
  67.     text_show_string(30, 30, 200, 16, "正点原子STM32开发板", 16, 0, RED);
  68.     text_show_string(30, 50, 200, 16, "图片显示实验", 16, 0, RED);
  69.     text_show_string(30, 70, 200, 16, "ATOM@ALIENTEK", 16, 0, RED);
  70.     text_show_string(30, 90, 200, 16, "WKUP: PREV", 16, 0, RED);
  71.     text_show_string(30, 110, 200, 16, "KEY0: NEXT", 16, 0, RED);
  72.    
  73.     /* 打开图片文件夹 */
  74.     while (f_opendir(&picdir, "0:/PICTURE") != FR_OK)
  75.     {
  76.         text_show_string(30, 130, 200, 16, "PICTURE文件夹错误!", 16, 0, RED);
  77.         delay_ms(200);
  78.         lcd_fill(30, 130, 200, 16, WHITE);
  79.         delay_ms(200);
  80.     }
  81.    
  82.     /* 获取有效图片文件数量 */
  83.     totpicnum = pic_get_tnum("0:/PICTURE");
  84.     while (totpicnum == 0)
  85.     {
  86.         text_show_string(30, 130, 200, 16, "没有图片文件!", 16, 0, RED);
  87.         delay_ms(200);
  88.         lcd_fill(30, 130, 200, 16, WHITE);
  89.         delay_ms(200);
  90.     }
  91.    
  92.     /* 申请内存 */
  93.     picfileinfo = (FILINFO *)mymalloc(SRAMIN, sizeof(FILINFO));
  94.     pname = (char *)mymalloc(SRAMIN, FF_MAX_LFN * 2 + 1);
  95.     picoffsettbl = (uint32_t *)mymalloc(SRAMIN, 4 * totpicnum);
  96.     while ((picfileinfo == NULL) || (pname == NULL) || (picoffsettbl == NULL))
  97.     {
  98.         text_show_string(30, 130, 200, 16, "内存分配失败!", 16, 0, RED);
  99.         delay_ms(200);
  100.         lcd_fill(30, 130, 200, 16, WHITE);
  101.         delay_ms(200);
  102.     }
  103.    
  104.     /* 打开目录并记录图片索引 */
  105.     res = (uint8_t)f_opendir(&picdir, "0:/PICTURE");
  106.     if (res == 0)
  107.     {
  108.         curindex = 0;
  109.         while (1)
  110.         {
  111.             temp = picdir.dptr;
  112.             res = (uint8_t)f_readdir(&picdir, picfileinfo);
  113.             if ((res != 0) || (picfileinfo->fname[0] == 0))
  114.             {
  115.                 break;
  116.             }
  117.             
  118.             res = exfuns_file_type(picfileinfo->fname);
  119.             if ((res & 0xF0) == 0x50)
  120.             {
  121.                 picoffsettbl[curindex] = temp;
  122.                 curindex++;
  123.             }
  124.         }
  125.     }
  126.    
  127.     /* 开始显示图片 */
  128.     text_show_string(30, 130, 200, 16, "开始显示...", 16, 0, RED);
  129.     delay_ms(1500);
  130.     piclib_init();
  131.     curindex = 0;
  132.     res = (uint8_t)f_opendir(&picdir, (const TCHAR *)"0:/PICTURE");
  133.     while (res == 0)
  134.     {
  135.         /* 获取下一个图片文件信息 */
  136.         dir_sdi(&picdir, picoffsettbl[curindex]);
  137.         res = (uint8_t)f_readdir(&picdir, picfileinfo);
  138.         if ((res != 0) || (picfileinfo->fname[0] == 0))
  139.         {
  140.             break;
  141.         }
  142.         
  143.         /* 根据图片路径显示图片 */
  144.         strcpy((char *)pname, "0:/PICTURE/");
  145.         strcat((char *)pname, (const char *)picfileinfo->fname);
  146.         lcd_clear(BLACK);
  147.         piclib_ai_load_picfile(pname, 0, 0, lcddev.width, lcddev.height, 1);
  148.         text_show_string(2, 2, lcddev.width, 16, (char *)pname, 16, 1, RED);
  149.         
  150.         while (1)
  151.         {
  152.             key = key_scan(0);
  153.             if (key == KEY0_PRES)
  154.             {
  155.                 /* 切换上一张 */
  156.                 if (curindex != 0)
  157.                 {
  158.                     curindex--;
  159.                 }
  160.                 else
  161.                 {
  162.                     curindex = totpicnum - 1;
  163.                 }
  164.                 break;
  165.             }
  166.             else if (key == WKUP_PRES)
  167.             {
  168.                 /* 切换下一张 */
  169.                 curindex++;
  170.                 if (curindex >= totpicnum)
  171.                 {
  172.                     curindex = 0;
  173.                 }
  174.                 break;
  175.             }
  176.             
  177.             if (++t == 20)
  178.             {
  179.                 t = 0;
  180.                 LED0_TOGGLE();
  181.             }
  182.             
  183.             delay_ms(10);
  184.         }
  185.     }
  186.    
  187.     /* 释放内存 */
  188.     myfree(SRAMIN, picfileinfo);
  189.     myfree(SRAMIN, pname);
  190.     myfree(SRAMIN, picoffsettbl);
  191.    
  192.     while (1);
  193. }
复制代码
可以看到整个设计思路是跟据图片解码库来设计的,piclib_ai_load_picfile()是这套代码的核心,其它的交互是围绕它和图片解码后的图片信息作的显示。大家再仔细对照光盘中的源码进一步了解整个设置思路。另外,我们的程序中只分配了4个文件索引,故更多数量的图片无法直接在本程序下演示,大家根据自己的需要再进行修改即可。

56.4 下载验证
将程序下载到开发板后,可以看到LCD开始显示图片(假设SD卡及文件都准备好了,即:在SD卡根目录新建:PICTURE文件夹,并存放一些图片文件(.bmp/.jpg/.gif)在该文件夹内),如图56.4.1所示:

第五十六章 图片显示实验17711.png
图56.4.1图片显示实验显示效果

按WKUP和KEY0可以快速切换到上一张或下一张,同时,由于我们的代码支持gif格式的图片显示(注意尺寸不能超过LCD屏幕尺寸),所以可以放一些gif图片到PICTURE文件夹,来看动画了。
回复

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-6-4 21:25

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表