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:坐标及显示区域尺寸
//acolor :alphablend的颜色(仅对不大于320*240的32位bmp有效!)
//abdnum :alphablend的值(0~32有效,其余值表示不使用alphablend,
//仅对不大于320*240的32位bmp有效!)
//图片在开始和结束的坐标点范围内显示
u8 ai_load_picfile(const u8 *filename,u16 x,u16 y,u16 width,u16 height)
{
u8 res;//返回值
u8 temp;
if((x+width)>lcddev.width)return PIC_WINDOW_ERR; //x坐标超范围了.
if((y+height)>lcddev.height)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;
}
//显示的开始坐标点
picinfo.S_YOFF=y; picinfo.S_XOFF=x;
//文件名传递
temp=f_typetell((u8*)filename); //得到文件的类型
switch(temp)
{
case T_BMP:
res=stdbmp_decode(filename); break; //解码bmp
case T_JPG:
case T_JPEG:
res=jpg_decode(filename); break; //解码JPG/JPEG
case T_GIF:
res=gif_decode(filename,x,y,width,height); break;//解码gif
default:
res=PIC_FORMAT_ERR;break; //非图片格式!!!
}
return res;
}
此段代码总共6个函数,其中,piclib_draw_hline函数用于快速画横线,由gif解码程序使用。
piclib_init 函数,该函数用于初始化图片解码的相关信息,其中_pic_phy是我们在piclib.h里面定义的一个结构体,用于管理底层LCD接口函数,这些函数必须由用户在外部实现。_pic_info则是另外一个结构体,用于图片缩放处理。
piclib_alpha_blend函数,该函数用于实现半透明效果,在小格式(分辨率小于240*320)bmp解码的时候,可能被用到。
ai_draw_init函数,该函数用于实现图片在显示区域的居中显示初始化,其实就是根据图片大小选择缩放比例和坐标偏移值。
is_element_ok函数,该函数用于判断一个点是不是应该显示出来,在图片缩放的时候该函数是必须用到的。
ai_load_picfile函数,该函数是整个图片显示的对外接口,外部程序,通过调用该函数,可以实现bmp、jpg/jpeg和gif的显示,该函数根据输入文件的后缀名,判断文件格式,然后交给相应的解码程序(bmp解码/jpeg解码/gif解码),执行解码,完成图片显示。注意,这里我们用到一个f_typetell的函数,来判断文件的后缀名,f_typetell函数在exfuns.c里面实现,具体请参考光盘源码。
保存piclib.c,然后在工程里面新建一个PICTURE的分组,将bmp.c、gif.c、jpeg.c和piclib.c等4个c文件加入到PICTURE分组下。然后打开piclib.h,在该文件输入如下代码:
#ifndef __PICLIB_H
#define __PICLIB_H
#include \"sys.h\"
#include \"lcd.h\"
#include \"malloc.h\"
#include \"ff.h\"
#include \"exfuns.h\"
#include \"bmp.h\"
#include \"jpeg.h\"
#include \"gif.h\"
#define PIC_FORMAT_ERR 0x27 //格式错误
#define PIC_SIZE_ERR 0x28 //图片尺寸错误
#define PIC_WINDOW_ERR 0x29 //窗口设定错误
#define PIC_MEM_ERR 0x11 //内存错误
//图片显示物理层接口
//在移植的时候,必须由用户自己实现这几个函数
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) 单色填充函数
void(*draw_hline)(u16,u16,u16,u16);
//void draw_hline(u16 x0,u16 y0,u16 len,u16 color) 画水平线函数
}_pic_phy;
extern _pic_phy pic_phy;
//图像信息
typedef struct
{
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_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);//智能画图
#endif
这里基本就是我们前面提到的两个结构体的定义以及一些函数的申明,保存piclib.h。最后我们在test.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;
DIR picdir; //图片目录
FILINFO picfileinfo; //文件信息
u8 *fn; //长文件名
u8 *pname; //带路径的文件名
u16 totpicnum; //图片文件总数
u16 curindex; //图片当前索引
u8 key; //键值
u8 pause=0; //暂停标记
u8 t;
u16 temp;
u16 *picindextbl; //图片索引表
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
uart_init(72,9600); //串口1初始化
LCD_Init(); //初始化液晶
LED_Init(); //LED初始化
KEY_Init(); //按键初始化
usmart_dev.init(72) ; //usmart初始化
mem_init(SRAMIN); //初始化内部内存池
exfuns_init(); //为fatfs相关变量申请内存
f_mount(0,fs[0]); //挂载SD卡
f_mount(1,fs[1]); //挂载FLASH.
POINT_COLOR=RED;
while(font_init()) //检查字库
{
LCD_ShowString(60,50,200,16,16,\"Font Error!\");delay_ms(200);
LCD_Fill(60,50,240,66,WHITE);//清除显示
}
Show_Str(60,50,200,16,\"战舰 STM32开发板\",16,0);
Show_Str(60,70,200,16,\"图片显示程序\",16,0);
Show_Str(60,90,200,16,\"KEY0:NEXT KEY2:PREV\",16,0);
Show_Str(60,110,200,16,\"正点原子@ALIENTEK\",16,0);
Show_Str(60,130,200,16,\"2012年9月19日\",16,0);
while(f_opendir(&picdir,\"0:/PICTURE\"))//打开图片文件夹
{
Show_Str(60,150,240,16,\"PICTURE文件夹错误!\",16,0); delay_ms(200);
LCD_Fill(60,150,240,156,WHITE); delay_ms(200);//清除显示
}
totpicnum=pic_get_tnum(\"0:/PICTURE\"); //得到总有效文件数
while(totpicnum==NULL)//图片文件为0
{
Show_Str(60,150,240,16,\"没有图片文件!\",16,0); delay_ms(200);
LCD_Fill(60,150,240,156,WHITE); delay_ms(200);//清除显示
}
printf(\"图片总数为:%d\\r\\n\",totpicnum);
picfileinfo.lfsize=_MAX_LFN*2+1; //长文件名最大长度
picfileinfo.lfname=mymalloc(SRAMIN,picfileinfo.lfsize);//为长文件缓存区分配内存
pname=mymalloc(SRAMIN,picfileinfo.lfsize);//为带路径的文件名分配内存
picindextbl=mymalloc(SRAMIN,2*totpicnum);
//申请2*totpicnum个字节的内存,用于存放图片索引
while(picfileinfo.lfname==NULL||pname==NULL||picindextbl==NULL)
//内存分配出错
{
Show_Str(60,150,240,16,\"内存分配失败!\",16,0); delay_ms(200);
LCD_Fill(60,150,240,146,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(60,150,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);//显示图片
Show_Str(2,2,240,16,pname,16,1); //显示图片名字
t=0;
while(1)
{
key=KEY_Scan(0); //扫描按键
if(t>250)key=KEY_RIGHT; //模拟一次按下右键
if(key==KEY_LEFT) //上一张
{
if(curindex)curindex--;
else curindex=totpicnum-1;
break;
}else if(key==KEY_RIGHT)//下一张
{
curindex++;
if(curindex>=totpicnum)curindex=0;//到末尾的时候,自动从头开始
break;
}else if(key==KEY_UP) {pause=!pause; LED1=!pause;}
//暂停的时候LED1亮.
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左右切换一幅图片。
47.4 下载验证在代码编译成功之后,我们下载代码到ALIENTEK战舰STM32开发板上,可以看到LCD开始显示图片(假设SD卡及文件都准备好了),如图47.4.1所示:
图47.4.1 图片显示实验显示效果按KEY0和KEY2可以快速切换到下一张或上一张,WK_UP按键可以暂停自动播放,同时DS1亮,指示处于暂停状态,再按一次WK_UP则继续播放。同时,由于我们的代码支持gif格式的图片显示(注意尺寸不能超过LCD屏幕尺寸),所以可以放一些gif图片到PICTURE文件夹,来看动画了。
另外需要注意,不是所有的jpg格式图片都可以正常显示,只有JFIF格式的jpg文件才能正常显示,对于EXIF格式的jpg文件,则不能直接显示,大家可以将EXIF格式的jpg文件用XP的画图打开,然后再保存一下,就将EXIF格式转为JFIF格式了,这样就可以正常解码显示了。
本文含有来自论坛的附件或图片点击查看原帖附件。
本文来自论坛,点击查看完整帖子内容。