第四十六章 图片显示实验
[mw_shl_code=c,true]1.硬件平台:正点原子探索者STM32F407开发板
2.软件平台:MDK5.1
3.固件库版本:V1.4.0[/mw_shl_code]
在开发产品的时候,很多时候,我们都会用到图片解码,在本章中,我们将向大家介绍如何通过
STM32F4来解码
BMP/JPG/JPEG/GIF等图片,并在
LCD上显示出来。本章分为如下几个部分:
46.1 图片格式简介
46.2 硬件设计
46.3 软件设计
46.4 下载验证
46.1 图片格式简介
我们常用的图片格式有很多,一般最常用的有三种:
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图像的宽、高、压缩方法,以及定义颜 {MOD}等信息
1, 调 {MOD}板,这个部分是可选的,有些位图需要调 {MOD}板,有些位图,比如真彩 {MOD}图(
24位的
BMP)就不需要调 {MOD}板;
2, 位图数据,这部分的内容根据
BMP位图使用的位数不同而不同,在
24位图中直接使用
RGB,而其他的小于
24位的使用调 {MOD}板中颜 {MOD}索引值。
关于
BMP的详细介绍,请参考光盘的《
BMP图片文件详解
.pdf》。接下来我们看看
JPEG文件格式。
JPEG是
Joint Photographic Experts Group(联合图像专家组
)的缩写,文件后辍名为“.
jpg”或“.
jpeg”,是最常用的图像文件格式,由一个软件开发联合会组织制定,同
BMP格式不同,
JPEG是一种有损压缩格式,能够将图像压缩在很小的储存空间,图像中重复或不重要的资料会被丢失,因此容易造成图像数据的损伤(
BMP不会,但是
BMP占用空间大)。尤其是使用过高的压缩比例,将使最终解压缩后恢复的图像质量明显降低,如果追求高品质图像,不宜采用过高压缩比例。但是
JPEG压缩技术十分先进,它用有损压缩方式去除冗余的图像数据,在获得极高的压缩率的同时能展现十分丰富生动的图像,换句话说,就是可以用最少的磁盘空间得到较好的图像品质。而且
JPEG是一种很灵活的格式,具有调节图像质量的功能,允许用不同的压缩比例对文件进行压缩,支持多种压缩级别,压缩比率通常在
10:
1到
40:
1之间,压缩比越大,品质就越低;相反地,压缩比越小,品质就越好。比如可以把
1.
37Mb的
BMP位图文件压缩至
20.
3KB。当然也可以在图像质量和文件尺寸之间找到平衡点。
JPEG格式压缩的主要是高频信息,对 {MOD}彩的信息保留较好,适合应用于互联网,可减少图像的传输时间,可以支持
24bit真彩 {MOD},也普遍应用于需要连续 {MOD}调的图像。
JPEG/JPG的解码过程可以简单的概述为如下几个部分:
1、从文件头读出文件的相关信息。
JPEG 文件数据分为文件头和图像数据两大部分,其中文件头记录了图像的版本、长宽、采样因子、量化表、哈夫曼表等重要信息。所以解码前必须将文件头信息读出,以备
图像数据解码过程之用。
2、从图像数据流读取一个最小编码单元(MCU) ,并提取出里边的各个颜 {MOD}分量单元。
3、将颜 {MOD}分量单元从数据流恢复成矩阵数据。
使用文件头给出的哈夫曼表,对分割出来的颜 {MOD}分量单元进行解码,把其恢复成
8×
8 的数据矩阵。
4、8×8 的数据矩阵进一步解码。
此部分解码工作以
8×
8 的数据矩阵为单位,
其中包括相邻矩阵的直流系数差分解码、使用文件头给出的量化表反量化数据、反
Zig- zag 编码、隔行正负纠正、反向离散余弦变换等
5 个步骤, 最终输出仍然是一个
8×
8 的数据矩阵。
5、颜 {MOD}系统YCrCb 向RGB 转换。
将一个
MCU的各个颜 {MOD}分量单元解码结果整合起来,将图像颜 {MOD}系统从
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图片格式。
GIF(Graphics
Interchange Format)是
CompuServe公司开发的图像文件存储格式,
1987年开发的
GIF文件格式版本号是
GIF87a,
1989年进行了扩充,扩充后的版本号定义为
GIF89a。
GIF图像文件以数据块
(block)为单位来存储图像的相关信息。一个
GIF文件由表示图形
/图像的数据块、数据子块以及显示图形
/图像的控制信息块组成,称为
GIF数据流
(Data Stream)。数据流中的所有控制信息块和数据块都必须在文件头
(Header)和文件结束块
(Trailer)之间。
GIF文件格式采用了
LZW(Lempel-Ziv Walch)压缩算法来存储图像数据,定义了允许用户为图像设置背景的透明
(transparency)属性。此外,
GIF文件格式可在一个文件中存放多幅彩 {MOD}图形
/图像。如果在
GIF文件中存放有多幅图,它们可以像演幻灯片那样显示或者像动画那样演示。
一个
GIF文件的结构可分为文件头
(File
Header)、
GIF数据流
(GIF Data Stream)和文件终结器
(Trailer)三个部分。文件头包含
GIF文件署名
(Signature)和版本号
(Version);
GIF数据流由控制标识符、图象块
(Image Block)和其他的一些扩展块组成;文件终结器只有一个值为
0x3B的字符(
';')表示文件结束。
关于
GIF的详细介绍,请参考光盘
GIF解码相关资料。图片格式简介,我们就介绍到这里。
46.2 硬件设计
本章实验功能简介:开机的时候先检测字库,然后检测SD卡是否存在,如果SD卡存在,则开始查找SD卡根目录下的PICTURE文件夹,如果找到则显示该文件夹下面的图片文件(支持bmp、jpg、jpeg或gif格式),循环显示,通过按KEY0和KEY2可以快速浏览下一张和上一张,KEY_UP按键用于暂停/继续播放,DS1用于指示当前是否处于暂停状态。如果未找到PICTURE文件夹/任何图片文件,则提示错误。同样我们也是用DS0来指示程序正在运行。
所要用到的硬件资源如下:
1) 指示灯
DS0和
DS1
2) KEY0、
KEY2和
KEY_UP三个按键
3) 串口
4) TFTLCD模块
5) SD卡
6) SPI FLASH
这几部分,在之前的实例中都介绍过了,我们在此就不介绍了。需要注意的是,我们在
SD卡根目录下要建一个
PICTURE的文件夹,用来存放
JPEG、
JPG、
BMP或
GIF等图片。
46.3 软件设计
打开本章实验工程目录可以看到,我们在工程根目录下面新建了一个
PICTURE文件夹。在该文件夹里面新建了
bmp.c、
bmp.h、
tjpgd.c、
tjpgd.h、
integer.h、
gif.c、
gif.h、
piclib.c和
piclib.h等
9个文件。打开实验工程可以看到,我们在工程中新建了
PICTURE分组,添加了相关源文件到工程,同时将
PICTURE文件夹加入头文件包含路径。
对于这些文件,其中
bmp.c和
bmp.h用于实现对
bmp文件的解码;
tjpgd.c和
tjpgd.h用于实现对
jpeg/jpg文件的解码;
gif.c和
gif.h用于实现对
gif文件的解码;这几个代码太长了,所以我们在这里不贴出来,请大家参考光盘本例程的源码,我们打开
piclib.c,代码如下:
_pic_info picinfo;
//图片信息
_pic_phy pic_phy; //图片显示物理接口
//lcd.h没有提供划横线函数
,需要自己实现
void piclib_draw_hline(u16 x0,u16 y0,u16 len,u16
color)
{
if((len==0)||(x0>lcddev.width)||(y0>lcddev.height))return;
LCD_Fill(x0,y0,x0+len-1,y0,color);
}
//填充颜 {MOD}
//x,y:起始坐标
//width,
height:宽度和高度。
//*color:颜 {MOD}数组
void piclib_fill_color(u16 x,u16 y,u16 width,u16
height,u16 *color)
{
LCD_Color_Fill(x,y,x+width-1,y+height-1,color);
}
//画图初始化
,在画图之前
,必须先调用此函数
//指定画点
/读点
void piclib_init(void)
{
pic_phy.read_point=LCD_ReadPoint; //读点函数实现
,仅
BMP需要
pic_phy.draw_point=LCD_Fast_DrawPoint; //画点函数实现
pic_phy.fill=LCD_Fill; //填充函数实现
,仅
GIF需要
pic_phy.draw_hline=piclib_draw_hline; //画线函数实现
,仅
GIF需要
pic_phy.fillcolor=piclib_fill_color; //颜 {MOD}填充函数实现
,仅
TJPGD需要
picinfo.lcdwidth=lcddev.width; //得到
LCD的宽度像素
picinfo.lcdheight=lcddev.height; //得到
LCD的高度像素
picinfo.ImgWidth=0; //初始化宽度为
0
picinfo.ImgHeight=0; //初始化高度为
0
picinfo.Div_Fac=0; //初始化缩放系数为
0
picinfo.S_Height=0; //初始化设定的高度为
0
picinfo.S_Width=0; //初始化设定的宽度为
0
picinfo.S_XOFF=0; //初始化
x轴的偏移量为
0
picinfo.S_YOFF=0; //初始化
y轴的偏移量为
0
picinfo.staticx=0; //初始化当前显示到的
x坐标为
0
picinfo.staticy=0; //初始化当前显示到的
y坐标为
0
}
//快速
ALPHA BLENDING算法
.
//src:源颜 {MOD}
//dst:目标颜 {MOD}
//alpha:透明程度
(0~32)
//返回值
:混合后的颜 {MOD}
.
u16 piclib_alpha_blend(u16 src,u16 dst,u8 alpha)
{
u32
src2;u32 dst2;
//Convert
to 32bit |-----GGGGGG-----RRRRR------BBBBB|
src2=((src<<16)|src)&0x07E0F81F;
dst2=((dst<<16)|dst)&0x07E0F81F;
//Perform
blending R:G:B with alpha in range 0..32
//Note
that the reason that alpha may not exceed 32 is that there are only
//5bits
of space between each R:G:B value, any higher value will overflow
//into
the next component and deliver ugly result.
dst2=((((dst2-src2)*alpha)>>5)+src2)&0x07E0F81F;
return
(dst2>>16)|dst2;
}
//初始化智能画点
//内部调用
void ai_draw_init(void)
{
float
temp,temp1;
temp=(float)picinfo.S_Width/picinfo.ImgWidth;
temp1=(float)picinfo.S_Height/picinfo.ImgHeight;
if(temp<temp1)temp1=temp;//取较小的那个
if(temp1>1)temp1=1;
//使图片处于所给区域的中间
picinfo.S_XOFF+=(picinfo.S_Width-temp1*picinfo.ImgWidth)/2;
picinfo.S_YOFF+=(picinfo.S_Height-temp1*picinfo.ImgHeight)/2;
temp1*=8192;//扩大
8192倍
picinfo.Div_Fac=temp1;
picinfo.staticx=0xffff;
picinfo.staticy=0xffff;//放到一个不可能的值上面
}
//判断这个像素是否可以显示
//(x,y) :像素原始坐标
//chg :功能变量
.
//返回值
:0,不需要显示
.1,需要显示
u8 is_element_ok(u16 x,u16 y,u8 chg)
{
if(x!=picinfo.staticx||y!=picinfo.staticy)
{
if(chg==1)
{ picinfo.staticx=x; picinfo.staticy=y; }
return
1;
}else return
0;
}
//智能画图
//FileName:要显示的图片文件
BMP/JPG/JPEG/GIF
//x,y,width,height:坐标及显示区域尺寸
//fast:使能
jpeg/jpg小图片
(图片尺寸小于等于液晶分辨率
)快速解码
,0,不使能
;1,使能
.
//图片在开始和结束的坐标点范围内显示
u8 ai_load_picfile(const u8 *filename,u16 x,u16
y,u16 width,u16 height,u8 fast)
{
u8 res;//返回值
u8
temp;
if((x+width)>picinfo.lcdwidth)return
PIC_WINDOW_ERR; //x坐标超范围了
.
if((y+height)>picinfo.lcdheight)return
PIC_WINDOW_ERR; //y坐标超范围了
.
//得到显示方框大小
if(width==0||height==0)return
PIC_WINDOW_ERR; //窗口设定错误
picinfo.S_Height=height;
picinfo.S_Width=width;
//显示区域无效
if(picinfo.S_Height==0||picinfo.S_Width==0)
{
picinfo.S_Height=lcddev.height;
picinfo.S_Width=lcddev.width;
return
FALSE;
}
if(pic_phy.fillcolor==NULL)fast=0;//颜 {MOD}填充函数未实现
,不能快速显示
//显示的开始坐标点
picinfo.S_YOFF=y;
picinfo.S_XOFF=x;
//文件名传递
temp=f_typetell((u8*)filename); //得到文件的类型
switch(temp)
{
case
T_BMP:
res=stdbmp_decode(filename);
//解码
bmp
break;
case
T_JPG:
case
T_JPEG:
res=jpg_decode(filename,fast); //解码
JPG/JPEG
break;
case
T_GIF:
res=gif_decode(filename,x,y,width,height); //解码
gif
break;
default:
res=PIC_FORMAT_ERR; //非图片格式
!!!
break;
}
return
res;
}
//动态分配内存
void *pic_memalloc (u32 size)
{
return
(void*)mymalloc(SRAMIN,size);
}
//释放内存
void pic_memfree (void* mf)
{
myfree(SRAMIN,mf);
}
此段代码总共
9个函数,其中,
piclib_draw_hline和
piclib_fill_color函数因为
LCD驱动代码没有提供,所以在这里单独实现,如果
LCD驱动代码有提供,则直接用
LCD提供的即可。
piclib_init 函数,该函数用于初始化图片解码的相关信息,其中
_pic_phy是我们在
piclib.h里面定义的一个结构体,用于管理底层
LCD接口函数,这些函数必须由用户在外部实现。
_pic_info则是另外一个结构体,用于图片缩放处理。
piclib_alpha_blend函数,该函数用于实现半透明效果,在小格式(图片分辨率小于
LCD分辨率)
bmp解码的时候,可能被用到。
ai_draw_init函数,该函数用于实现图片在显示区域的居中显示初始化,其实就是根据图片大小选择缩放比例和坐标偏移值。
is_element_ok函数,该函数用于判断一个点是不是应该显示出来,在图片缩放的时候该函数是必须用到的。
ai_load_picfile函数,该函数是整个图片显示的对外接口,外部程序,通过调用该函数,可以实现
bmp、
jpg/jpeg和
gif的显示,该函数根据输入文件的后缀名,判断文件格式,然后交给相应的解码程序(
bmp解码
/jpeg解码
/gif解码),执行解码,完成图片显示。注意,这里我们用到一个
f_typetell的函数,来判断文件的后缀名,
f_typetell函数在
exfuns.c里面实现,具体请参考光盘本例程源码。
最后,
pic_memalloc和
pic_memfree分别用于图片解码时需要用到的内存申请和释放,通过调用
mymalloc和
myfreee来实现。
接下来我们看看头文件
piclib.h代码如下:
#ifndef __PICLIB_H
#define __PICLIB_H
……
//圣罗头文件引入
#define PIC_FORMAT_ERR 0x27 //格式错误
#define PIC_SIZE_ERR 0x28 //图片尺寸错误
#define PIC_WINDOW_ERR 0x29 //窗口设定错误
#define PIC_MEM_ERR 0x11 //内存错误
#ifndef TRUE
#define TRUE
1
#endif
#ifndef FALSE
#define FALSE
0
#endif
//图片显示物理层接口
//在移植的时候
,必须由用户自己实现这几个函数
typedef struct
{
u16(*read_point)(u16,u16);
//u16 read_point(u16 x,u16 y) 读点函数
void(*draw_point)(u16,u16,u16);
//void draw_point(u16 x,u16 y,u16 color)画点函数
void(*fill)(u16,u16,u16,u16,u16);
///void fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color)单 {MOD}填充函数
void(*draw_hline)(u16,u16,u16,u16);
//void draw_hline(u16 x0,u16 y0,u16 len,u16 color) 画水平线函数
void(*fillcolor)(u16,u16,u16,u16,u16*);
//void piclib_fill_color(u16 x,u16 y,u16 width,u16 height,u16
*color) 颜 {MOD}填充
}_pic_phy;
extern _pic_phy pic_phy;
//图像信息
typedef struct
{
u16
lcdwidth; //LCD的宽度
u16
lcdheight; //LCD的高度
u32
ImgWidth; //图像的实际宽度和高度
u32
ImgHeight;
u32
Div_Fac; //缩放系数
(扩大了
8192倍的
)
u32
S_Height; //设定的高度和宽度
u32
S_Width;
u32 S_XOFF; //x轴和
y轴的偏移量
u32
S_YOFF;
u32
staticx; //当前显示到的xy坐标
u32
staticy;
}_pic_info;
extern _pic_info picinfo;//图像信息
void piclib_fill_color(u16 x,u16 y,u16 width,u16 height,u16
*color);
void piclib_init(void); //初始化画图
u16 piclib_alpha_blend(u16 src,u16 dst,u8 alpha); //alphablend处理
void ai_draw_init(void); //初始化智能画图
u8 is_element_ok(u16 x,u16 y,u8 chg); //判断像素是否有效
u8 ai_load_picfile(const u8 *filename,u16 x,u16
y,u16 width,u16 height,u8 fast);//智能画图
void *pic_memalloc (u32 size); //pic申请内存
void pic_memfree (void* mf); //pic释放内存
#endif
这里基本就是我们前面提到的两个结构体的定义以及一些函数的申明,相信大家很容易明白。最后我们看看
main.c文件内容如下:
//得到
path路径下
,目标文件的总个数
//path:路径
//返回值
:总有效文件数
u16 pic_get_tnum(u8 *path)
{
u8
res; u16 rval=0;
DIR tdir;
//临时目录
FILINFO
tfileinfo; //临时文件信息
u8
*fn;
res=f_opendir(&tdir,(const TCHAR*)path); //打开目录
tfileinfo.lfsize=_MAX_LFN*2+1; //长文件名最大长度
tfileinfo.lfname=mymalloc(SRAMIN,tfileinfo.lfsize);//为长文件缓存区分配内存
if(res==FR_OK&&tfileinfo.lfname!=NULL)
{
while(1)//查询总的有效文件数
{
res=f_readdir(&tdir,&tfileinfo); //读取目录下的一个文件
if(res!=FR_OK||tfileinfo.fname[0]==0)break; //错误了
/到末尾了
,退出
fn=(u8*)(*tfileinfo.lfname?tfileinfo.lfname:tfileinfo.fname);
res=f_typetell(fn);
if((res&0XF0)==0X50)
rval++;//取高四位
,是否图片文件?是则加
1
}
}
return
rval;
}
int main(void)
{
u8
res; u8 t; u16 temp;
DIR picdir;
//图片目录
FILINFO
picfileinfo;//文件信息
u8
*fn; //长文件名
u8
*pname; //带路径的文件名
u16
totpicnum; //图片文件总数
u16
curindex; //图片当前索引
u8
key; //键值
u8
pause=0; //暂停标记
u16
*picindextbl; //图片索引表
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组
2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为
115200
LED_Init(); //初始化
LED
usmart_dev.init(84); //初始化
USMART
LCD_Init(); //LCD初始化
KEY_Init(); //按键初始化
W25QXX_Init(); //初始化
W25Q128
my_mem_init(SRAMIN); //初始化内部内存池
my_mem_init(SRAMCCM); //初始化
CCM内存池
exfuns_init(); //为
fatfs相关变量申请内存
f_mount(fs[0],"0:",1); //挂载
SD卡
f_mount(fs[1],"1:",1); //挂载
FLASH.
POINT_COLOR=RED;
while(font_init())
//检查字库
{
LCD_ShowString(30,50,200,16,16,"Font
Error!"); delay_ms(200);
LCD_Fill(30,50,240,66,WHITE);
delay_ms(200);//清除显示
}
Show_Str(30,50,200,16,"Explorer
STM32F4开发板
",16,0);
Show_Str(30,70,200,16,"图片显示程序
",16,0);
Show_Str(30,90,200,16,"KEY0:NEXT
KEY2REV",16,0);
Show_Str(30,110,200,16,"WK_UPAUSE",16,0);
Show_Str(30,130,200,16,"正点原子
@ALIENTEK",16,0);
Show_Str(30,150,200,16,"2014年
5月
15日
",16,0);
while(f_opendir(&picdir,"0:/PICTURE"))//打开图片文件夹
{
Show_Str(30,170,240,16,"PICTURE文件夹错误
!",16,0); delay_ms(200);
LCD_Fill(30,170,240,186,WHITE);
delay_ms(200);//清除显示
}
totpicnum=pic_get_tnum("0:/PICTURE");
//得到总有效文件数
while(totpicnum==NULL)//图片文件为
0
{
Show_Str(30,170,240,16,"没有图片文件
!",16,0); delay_ms(200);
LCD_Fill(30,170,240,186,WHITE);
delay_ms(200);//清除显示
}
picfileinfo.lfsize=_MAX_LFN*2+1; //长文件名最大长度
picfileinfo.lfname=mymalloc(SRAMIN,picfileinfo.lfsize); //长文件缓存区分配内存
pname=mymalloc(SRAMIN,picfileinfo.lfsize); //为带路径的文件名分配内存
picindextbl=mymalloc(SRAMIN,2*totpicnum); //申请内存
,用于存放图片索引
while(picfileinfo.lfname==NULL||pname==NULL||picindextbl==NULL)//分配出错
{
Show_Str(30,170,240,16,"内存分配失败
!",16,0); delay_ms(200);
LCD_Fill(30,170,240,186,WHITE);
delay_ms(200);//清除显示
}
//记录索引
res=f_opendir(&picdir,"0:/PICTURE"); //打开目录
if(res==FR_OK)
{
curindex=0;//当前索引为
0
while(1)//全部查询一遍
{
temp=picdir.index; //记录当前
index
res=f_readdir(&picdir,&picfileinfo); //读取目录下的一个文件
if(res!=FR_OK||picfileinfo.fname[0]==0)break;//错误了
/到末尾了
,退出
fn=(u8*)(*picfileinfo.lfname?picfileinfo.lfname:picfileinfo.fname);
res=f_typetell(fn);
if((res&0XF0)==0X50)//取高四位
,看看是不是图片文件
{
picindextbl[curindex]=temp;//记录索引
curindex++;
}
}
}
Show_Str(30,170,240,16,"开始显示
...",16,0);
delay_ms(1500);
piclib_init(); //初始化画图
curindex=0; //从
0开始显示
res=f_opendir(&picdir,(const
TCHAR*)"0:/PICTURE"); //打开目录
while(res==FR_OK)//打开成功
{
dir_sdi(&picdir,picindextbl[curindex]); //改变当前目录索引
res=f_readdir(&picdir,&picfileinfo); //读取目录下的一个文件
if(res!=FR_OK||picfileinfo.fname[0]==0)break; //错误了
/到末尾了
,退出
fn=(u8*)(*picfileinfo.lfname?picfileinfo.lfname:picfileinfo.fname);
strcpy((char*)pname,"0:/PICTURE/"); //复制路径
(目录
)
strcat((char*)pname,(const
char*)fn); //将文件名接在后面
LCD_Clear(BLACK);
ai_load_picfile(pname,0,0,lcddev.width,lcddev.height,1);//显示图片
Show_Str(2,2,240,16,pname,16,1);
//显示图片名字
t=0;
while(1)
{
key=KEY_Scan(0); //扫描按键
if(t>250)key=1; //模拟一次按下
KEY0
if((t%20)==0)LED0=!LED0;//LED0闪烁
,提示程序正在运行
.
if(key==KEY2_PRES) //上一张
{
if(curindex)curindex--;
else
curindex=totpicnum-1;
break;
}else
if(key==KEY0_PRES)//下一张
{
curindex++;
if(curindex>=totpicnum)curindex=0;//到末尾的时候
,自动从头开始
break;
}else
if(key==WKUP_PRES) { pause=!pause; LED1=!pause;}//暂停?
if(pause==0)t++;
delay_ms(10);
}
res=0;
}
myfree(SRAMIN,picfileinfo.lfname);//释放内存
myfree(SRAMIN,pname); //释放内存
myfree(SRAMIN,picindextbl); //释放内存
}
此部分除了
mian函数,还有一个
pic_get_tnum的函数,用来得到
path路径下,所有有效文件(图片文件)的个数。在
mian函数里面我们通过索引(图片文件在
PICTURE文件夹下的编号),来查找上一个
/下一个图片文件,这里我们需要用到
FATFS自带的一个函数:
dir_sdi,来设置当前目录的索引(因为
f_readdir只能沿着索引一直往下找,不能往上找),方便定位到任何一个文件。
dir_sdi在
FATFS下面被定义为
static函数,所以我们必须在
ff.c里面将该函数的
static修饰词去掉,然后在
ff.h里面添加该函数的申明,以便
main函数使用。
其他部分就比较简单了,至此,整个图片显示实验的软件设计部分就结束了。该程序将实现浏览
PICTURE文件夹下的所有图片,并显示其名字,每隔
3s左右切换一幅图片。
46.4 下载验证
在代码编译成功之后,我们下载代码到
ALIENTEK探索者
STM32F4开发板上,可以看到
LCD开始显示图片(假设
SD卡及文件都准备好了),如图
46.4.1所示:
图
46.4.1 图片显示实验显示效果
按
KEY0和
KEY2可以快速切换到下一张或上一张,
KEY_UP按键可以暂停自动播放,同时
DS1亮,指示处于暂停状态,再按一次
KEY_UP则继续播放。同时,由于我们的代码支持
gif格式的图片显示(注意尺寸不能超过
LCD屏幕尺寸),所以可以放一些
gif图片到
PICTURE文件夹,来看动画了。
实验详细手册和源码下载地址:http://www.openedv.com/posts/list/41586.htm
正点原子探索者STM32F407开发板购买地址:http://item.taobao.com/item.htm?id=41855882779
一周热门 更多>