第四十五章 汉字显示实验
[mw_shl_code=c,true]1.硬件平台:正点原子探索者STM32F407开发板
2.软件平台:MDK5.1
3.固件库版本:V1.4.0
[/mw_shl_code]
汉字显示在很多单片机系统都需要用到,少则几个字,多则整个汉字库的支持,更有甚者还要支持多国字库,那就更麻烦了。本章,我们将向大家介绍,如何用STM32F4控制LCD显示汉字。在本章中,我们将使用外部FLASH来存储字库,并可以通过SD卡更新字库。STM32F4读取存在FLASH里面的字库,然后将汉字显示在LCD上面。本章分为如下几个部分:
45.1 汉字显示原理简介
45.2 硬件设计
45.3 软件设计
45.4 下载验证
45.1 汉字显示原理简介
常用的汉字内码系统有GB2312,GB13000,GBK,BIG5(繁体)等几种,其中GB2312支持的汉字仅有几千个,很多时候不够用,而GBK内码不仅完全兼容GB2312,还支持了繁体字,总汉字数有2万多个,完全能满足我们一般应用的要求。
本实例我们将制作三个GBK字库,制作好的字库放在SD卡里面,然后通过SD卡,将字库文件复制到外部FLASH芯片W25Q128里,这样,W25Q128就相当于一个汉字字库芯片了。
汉字在液晶上的显示原理与前面显示字符的是一样的。汉字在液晶上的显示其实就是一些点的显示与不显示,这就相当于我们的笔一样,有笔经过的地方就画出来,没经过的地方就不画。所以要显示汉字,我们首先要知道汉字的点阵数据,这些数据可以由专门的软件来生成。只要知道了一个汉字点阵的生成方法,那么我们在程序里面就可以把这个点阵数据解析成一个汉字。
知道显示了一个汉字,就可以推及整个汉字库了。汉字在各种文件里面的存储不是以点阵数据的形式存储的(否则那占用的空间就太大了),而是以内码的形式存储的,就是GB2312/GBK/BIG5等这几种的一种,每个汉字对应着一个内码,在知道了内码之后再去字库里面查找这个汉字的点阵数据,然后在液晶上显示出来。这个过程我们是看不到,但是计算机是要去执行的。
单片机要显示汉字也与此类似:汉字内码(GBK/GB2312)à查找点阵库à解析à显示。
所以只要我们有了整个汉字库的点阵,就可以把电脑上的文本信息在单片机上显示出来了。这里我们要解决的最大问题就是制作一个与汉字内码对得上号的汉字点阵库。而且要方便单片机的查找。每个GBK码由2个字节组成,第一个字节为0X81~0XFE,第二个字节分为两部分,一是0X40~0X7E,二是0X80~0XFE。其中与GB2312相同的区域,字完全相同。
我们把第一个字节代表的意义称为区,那么GBK里面总共有126个区(0XFE-0X81+1),每个区内有190个汉字(0XFE-0X80+0X7E-0X40+2),总共就有126*190=23940个汉字。我们的点阵库只要按照这个编码规则从0X8140开始,逐一建立,每个区的点阵大小为每个汉字所用的字节数*190。这样,我们就可以得到在这个字库里面定位汉字的方法:
当GBKL<0X7F时:Hp=((GBKH-0x81)*190+GBKL-0X40)*(size*2);
当GBKL>0X80时:Hp=((GBKH-0x81)*190+GBKL-0X41)*(size*2);
其中GBKH、GBKL分别代表GBK的第一个字节和第二个字节(也就是高位和低位),size代表汉字字体的大小(比如16字体,12字体等),Hp则为对应汉字点阵数据在字库里面的起始地址(假设是从0开始存放)。
这样我们只要得到了汉字的GBK码,就可以显示这个汉字了。从而实现汉字在液晶上的显示。
上一章,我们提到要用cc936.c,以支持长文件名,但是cc936.c文件里面的两个数组太大了(172KB),直接刷在单片机里面,太占用flash了,所以我们必须把这两个数组存放在外部flash。cc936里面包含的两个数组oem2uni和uni2oem存放unicode和gbk的互相转换对照表,这两个数组很大,这里我们利用ALIENTEK 提供的一个C语言数组转BIN(二进制)的软件:C2B转换助手V1.1.exe,将这两个数组转为BIN文件,我们将这两个数组拷贝出来存放为一个新的文本文件,假设为UNIGBK.TXT,然后用C2B转换助手打开这个文本文件,如图45.1.1所示:
图45.1.1 C2B转换助手
然后点击转换,就可以在当前目录下(文本文件所在目录下)得到一个UNIGBK.bin的文件。这样就完成将C语言数组转换为.bin文件,然后只需要将UNIGBK.bin保存到外部FLASH就实现了该数组的转移。
在cc936.c里面,主要是通过ff_convert调用这两个数组,实现UNICODE和GBK的互转,该函数原代码如下:
WCHAR ff_convert ( /*
Converted code, 0 means conversion error */
WCHAR src, /* Character code to be converted */
UINT dir /*
0: Unicode to OEMCP, 1: OEMCP to Unicode */
)
{
const
WCHAR *p;
WCHAR
c;
int i,
n, li, hi;
if
(src < 0x80) { /* ASCII */
c
= src;
} else
{
if
(dir) { /* OEMCP to unicode */
p
= oem2uni;
hi
= sizeof(oem2uni) / 4 - 1;
}
else { /* Unicode to OEMCP */
p
= uni2oem;
hi
= sizeof(uni2oem) / 4 - 1;
}
li
= 0;
for
(n = 16; n; n--) {
i
= li + (hi - li) / 2;
if
(src == p[i * 2]) break;
if
(src > p[i * 2]) li = i;
else
hi = i;
}
c
= n ? p[i * 2 + 1] : 0;
}
return
c;
}
此段代码,通过二分法(16阶)在数组里面查找UNICODE(或GBK)码对应的GBK(或UNICODE)码。当我们将数组存放在外部flash的时候,将该函数修改为:
WCHAR ff_convert ( /*
Converted code, 0 means conversion error */
WCHAR src, /* Character code to be converted */
UINT dir /*
0: Unicode to OEMCP, 1: OEMCP to Unicode */
)
{
WCHAR
t[2];
WCHAR
c;
u32 i,
li, hi;
u16 n;
u32
gbk2uni_offset=0;
if
(src < 0x80)c = src;//ASCII,直接不用转换.
else
{
if(dir) gbk2uni_offset=ftinfo.ugbksize/2; //GBK 2 UNICODE
else
gbk2uni_offset=0; //UNICODE 2 GBK
/*
Unicode to OEMCP */
hi=ftinfo.ugbksize/2;//对半开.
hi
=hi / 4 - 1;
li
= 0;
for
(n = 16; n; n--)
{
i
= li + (hi - li) / 2;
W25QXX_Read((u8*)&t,ftinfo.ugbkaddr+i*4+gbk2uni_offset,4);//读出4个字节
if
(src == t[0]) break;
if
(src > t[0])li = i;
else
hi = i;
}
c
= n ? t[1] : 0;
}
return
c;
}
代码中的ftinfo.ugbksize为我们刚刚生成的UNIGBK.bin的大小,而ftinfo.ugbkaddr是我们存放UNIGBK.bin文件的首地址。这里同样采用的是二分法查找,关于cc936.c的修改,我们就介绍到这。
字库的生成,我们要用到一款软件,由易木雨软件工作室设计的点阵字库生成器 V3.8。该软件可以在WINDOWS 系统下生成任意点阵大小的ASCII,GB2312(简体中文)、GBK(简体中文)、BIG5(繁体中文)、HANGUL(韩文)、SJIS(日文)、Unicode 以及泰文,越南文、俄文、乌克兰文,拉丁文,8859 系列等共二十几种编码的字库,不但支持生成二进制文件格式的文件,也可以生成BDF 文件,还支持生成图片功能,并支持横向,纵向等多种扫描方式,且扫描方式可以根据用户的需求进行增加。该软件的界面如图45.1.2所示:
图45.1.2 点阵字库生成器默认界面
要生成16*16的GBK字库,则选择:936中文PRC GBK,字宽和高均选择16,字体大小选择12,然后模式选择纵向取模方式二(字节高位在前,低位在后),最后点击创建,就可以开始生成我们需要的字库了(.DZK文件)。具体设置如图45.1.3所示:
图45.1.3 生成GBK16*16字库的设置方法
注意:电脑端的字体大小与我们生成点阵大小的关系为:
fsize=dsize*6/8
其中,fsize是电脑端字体大小,dsize是点阵大小(12、16、24等)。所以16*16点阵大小对应的是12字体。
生成完以后,我们把文件名和后缀改成:GBK16.FON。同样的方法,生成12*12的点阵库(GBK12.FON)和24*24的点阵库(GBK24.FON),总共制作3个字库。
另外,该软件还可以生成其他很多字库,字体也可选,大家可以根据自己的需要按照上面的方法生成即可。该软件的详细介绍请看软件自带的《点阵字库生成器说明书》,关于汉字显示原理,我们就介绍到这。
45.2 硬件设计
本章实验功能简介:开机的时候先检测W25Q128中是否已经存在字库,如果存在,则按次序显示汉字(三种字体都显示)。如果没有,则检测SD卡和文件系统,并查找SYSTEM文件夹下的FONT文件夹,在该文件夹内查找UNIGBK.BIN、GBK12.FON、GBK16.FON和GBK24.FON(这几个文件的由来,我们前面已经介绍了)。在检测到这些文件之后,就开始更新字库,更新完毕才开始显示汉字。通过按按键KEY0,可以强制更新字库。同样我们也是用DS0来指示程序正在运行。
所要用到的硬件资源如下:
1) 指示灯DS0
2) KEY0按键
3) 串口
4) TFTLCD模块
5) SD卡
6) SPI FLASH
这几部分分,在之前的实例中都介绍过了,我们在此就不介绍了。
45.3 软件设计
打开本章实验目录可以看到,首先在工程根目录文件夹下面新建了一个TEXT的文件夹。在TEXT文件夹下新建fontupd.c、fontupd.h、text.c、text.h这4个文件。同时,我们在实验工程中新建了TEXT分组,将新建的源文件加入到了分组之下,并将头文件包含路径加入到了工程的PATH中。
打开fontupd.c,代码如下:
//字库区域占用的总扇区数大小(字库信息+ unigbk表+3个字库=3238700字节,约占791
//个W25QXX扇区)
#define FONTSECSIZE 791
//字库存放起始地址
#define FONTINFOADDR 1024*1024*12
//探索者STM32F4开发板,是从12M地址以后开始存放字库,前面12M被fatfs占用了.
//12M以后紧跟3个字库+UNIGBK.BIN,总大小3.09M,被字库占用了,不能动!
//15.10M以后,用户可以自由使用.建议用最后的100K字节比较好.
//用来保存字库基本信息,地址,大小等
_font_info ftinfo;
//字库存放在磁盘中的路径
u8*const
GBK24_PATH="/SYSTEM/FONT/GBK24.FON"; //GBK24的存放位置
u8*const
GBK16_PATH="/SYSTEM/FONT/GBK16.FON"; //GBK16的存放位置
u8*const
GBK12_PATH="/SYSTEM/FONT/GBK12.FON"; //GBK12的存放位置
u8*const
UNIGBK_PATH="/SYSTEM/FONT/UNIGBK.BIN"; //UNIGBK.BIN的存放位置
//显示当前字体更新进度
//x,y:坐标
//size:字体大小
//fsize:整个文件大小
//pos:当前文件指针位置
u32 fupd_prog(u16 x,u16 y,u8 size,u32 fsize,u32
pos)
{
……//此处省略代码
}
//更新某一个
//x,y:坐标
//size:字体大小
//fxpath:路径
//fx:更新的内容 0,ungbk;1,gbk12;2,gbk16;3,gbk24;
//返回值:0,成功;其他,失败.
u8 updata_fontx(u16 x,u16 y,u8 size,u8 *fxpath,u8
fx)
{
u32
flashaddr=0;
FIL *
fftemp;
u8
*tempbuf; u8 res; u8 rval=0;
u16
bread; u32 offx=0;
fftemp=(FIL*)mymalloc(SRAMIN,sizeof(FIL)); //分配内存
if(fftemp==NULL)rval=1;
tempbuf=mymalloc(SRAMIN,4096); //分配4096个字节空间
if(tempbuf==NULL)rval=1;
res=f_open(fftemp,(const
TCHAR*)fxpath,FA_READ);
if(res)rval=2;//打开文件失败
if(rval==0)
{
switch(fx)
{
case
0: //更新UNIGBK.BIN
ftinfo.ugbkaddr=FONTINFOADDR+sizeof(ftinfo);
//信息头之后,紧跟UNIGBK转换码表
ftinfo.ugbksize=fftemp->fsize; //UNIGBK大小
flashaddr=ftinfo.ugbkaddr;
break;
case
1:
ftinfo.f12addr=ftinfo.ugbkaddr+ftinfo.ugbksize;
//UNIGBK之后,紧跟GBK12字库
ftinfo.gbk12size=fftemp->fsize; //GBK12字库大小
flashaddr=ftinfo.f12addr; //GBK12的起始地址
break;
case
2:
ftinfo.f16addr=ftinfo.f12addr+ftinfo.gbk12size;
//GBK12之后,紧跟GBK16字库
ftinfo.gbk16size=fftemp->fsize; //GBK16字库大小
flashaddr=ftinfo.f16addr; //GBK16的起始地址
break;
case
3:
ftinfo.f24addr=ftinfo.f16addr+ftinfo.gbk16size;
//GBK16之后,紧跟GBK24字库
ftinfo.gkb24size=fftemp->fsize; //GBK24字库大小
flashaddr=ftinfo.f24addr; //GBK24的起始地址
break;
}
while(res==FR_OK)//死循环执行
{
res=f_read(fftemp,tempbuf,4096,(UINT
*)&bread); //读取数据
if(res!=FR_OK)break; //执行错误
W25QXX_Write(tempbuf,offx+flashaddr,4096); //从0开始写入4096个数据
offx+=bread;
fupd_prog(x,y,size,fftemp->fsize,offx); //进度显示
if(bread!=4096)break; //读完了.
}
f_close(fftemp);
}
myfree(SRAMIN,fftemp); //释放内存
myfree(SRAMIN,tempbuf); //释放内存
return
res;
}
//更新字体文件,UNIGBK,GBK12,GBK16,GBK24一起更新
//x,y:提示信息的显示地址
//size:字体大小
//src:字库来源磁盘."0:",SD卡;"1:",FLASH盘,"2:",U盘.
//提示信息字体大小
//返回值:0,更新成功;
// 其他,错误代码.
u8 update_font(u16 x,u16 y,u8 size,u8* src)
{
u8
*pname; u8 res=0; u8 rval=0;
u32
*buf; u16 i,j;
FIL
*fftemp;
res=0XFF;
ftinfo.fontok=0XFF;
pname=mymalloc(SRAMIN,100); //申请100字节内存
buf=mymalloc(SRAMIN,4096); //申请4K字节内存
fftemp=(FIL*)mymalloc(SRAMIN,sizeof(FIL)); //分配内存
if(buf==NULL||pname==NULL||fftemp==NULL)
{
myfree(SRAMIN,fftemp);
myfree(SRAMIN,pname);
myfree(SRAMIN,buf);
return
5; //内存申请失败
}
//先查找文件是否正常
strcpy((char*)pname,(char*)src); //copy src内容到pname
strcat((char*)pname,(char*)UNIGBK_PATH);
res=f_open(fftemp,(const
TCHAR*)pname,FA_READ);
if(res)rval|=1<<4;//打开文件失败
strcpy((char*)pname,(char*)src); //copy src内容到pname
strcat((char*)pname,(char*)GBK12_PATH);
res=f_open(fftemp,(const
TCHAR*)pname,FA_READ);
if(res)rval|=1<<5;//打开文件失败
strcpy((char*)pname,(char*)src); //copy src内容到pname
strcat((char*)pname,(char*)GBK16_PATH);
res=f_open(fftemp,(const TCHAR*)pname,FA_READ);
if(res)rval|=1<<6;//打开文件失败
strcpy((char*)pname,(char*)src); //copy src内容到pname
strcat((char*)pname,(char*)GBK24_PATH);
res=f_open(fftemp,(const
TCHAR*)pname,FA_READ);
if(res)rval|=1<<7;//打开文件失败
myfree(SRAMIN,fftemp);//释放内存
if(rval==0)//字库文件都存在.
{
LCD_ShowString(x,y,240,320,size,"Erasing
sectors... ");//提示正在擦除扇区
for(i=0;i<FONTSECSIZE;i++) //先擦除字库区域,提高写入速度
{
fupd_prog(x+20*size/2,y,size,FONTSECSIZE,i);//进度显示
W25QXX_Read((u8*)buf,((FONTINFOADDR/4096)+i)*4096,4096);
//读出整个扇区的内容
for(j=0;j<1024;j++)
if(buf[j]!=0XFFFFFFFF)break;//校验数据,是否需要擦除
if(j!=1024)W25QXX_Erase_Sector((FONTINFOADDR/4096)+i);//擦除扇区
}
myfree(SRAMIN,buf);
LCD_ShowString(x,y,240,320,size,"Updating
UNIGBK.BIN");
strcpy((char*)pname,(char*)src); //copy src内容到pname
strcat((char*)pname,(char*)UNIGBK_PATH);
res=updata_fontx(x+20*size/2,y,size,pname,0); //更新UNIGBK.BIN
if(res){myfree(SRAMIN,pname);return
1;}
LCD_ShowString(x,y,240,320,size,"Updating
GBK12.BIN ");
strcpy((char*)pname,(char*)src); //copy src内容到pname
strcat((char*)pname,(char*)GBK12_PATH);
res=updata_fontx(x+20*size/2,y,size,pname,1); //更新GBK12.FON
if(res){myfree(SRAMIN,pname);return
2;}
LCD_ShowString(x,y,240,320,size,"Updating
GBK16.BIN ");
strcpy((char*)pname,(char*)src); //copy src内容到pname
strcat((char*)pname,(char*)GBK16_PATH);
res=updata_fontx(x+20*size/2,y,size,pname,2); //更新GBK16.FON
if(res){myfree(SRAMIN,pname);return
3;}
LCD_ShowString(x,y,240,320,size,"Updating
GBK24.BIN ");
strcpy((char*)pname,(char*)src); //copy src内容到pname
strcat((char*)pname,(char*)GBK24_PATH);
res=updata_fontx(x+20*size/2,y,size,pname,3); //更新GBK24.FON
if(res){myfree(SRAMIN,pname);return
4;}
//全部更新好了
ftinfo.fontok=0XAA;
W25QXX_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo)); //保存字库信息
}
myfree(SRAMIN,pname);//释放内存
myfree(SRAMIN,buf);
return
rval;//无错误.
}
//初始化字体
//返回值:0,字库完好.
// 其他,字库丢失
u8 font_init(void)
{
u8
t=0;
W25QXX_Init();
while(t<10)//连续读取10次,都是错误,说明确实是有问题,得更新字库了
{
t++;
W25QXX_Read((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));//读ftinfo结构体
if(ftinfo.fontok==0XAA)break;
delay_ms(20);
}
if(ftinfo.fontok!=0XAA)return
1;
return
0;
}
此部分代码主要用于字库的更新操作(包含UNIGBK的转换码表更新),其中ftinfo是我们在fontupd.h里面定义的一个结构体,用于记录字库首地址及字库大小等信息。因为我们将W25Q128的前12M字节给FATFS管理(用做本地磁盘),随后,紧跟字库结构体、UNIGBK.bin、和三个字库,这部分内容首地址是:(1024*12)*1024,大小约3.09M,最后W25Q128还剩下约0.9M给用户自己用。
接下来我们打开fontupd.h文件代码如下:
#ifndef __FONTUPD_H__
#define __FONTUPD_H__
#include <stm32f4xx.h>
//字体信息保存地址,占33个字节,第1个字节用于标记字库是否存在.后续每8个字节一组,
//分别保存起始地址和文件大小
extern u32 FONTINFOADDR;
//字库信息结构体定义
//用来保存字库基本信息,地址,大小等
__packed typedef struct
{
u8
fontok; //字库存在标志,0XAA,字库正常;其他,字库不存在
u32
ugbkaddr; //unigbk的地址
u32
ugbksize; //unigbk的大小
u32
f12addr; //gbk12地址
u32
gbk12size; //gbk12的大小
u32
f16addr; //gbk16地址
u32
gbk16size; //gbk16的大小
u32
f24addr; //gbk24地址
u32
gkb24size; //gbk24的大小
}_font_info;
extern _font_info ftinfo; //字库信息结构体
u32 fupd_prog(u16 x,u16 y,u8 size,u32 fsize,u32
pos); //显示更新进度
u8 updata_fontx(u16 x,u16 y,u8 size,u8 *fxpath,u8
fx); //更新指定字库
u8 update_font(u16 x,u16 y,u8 size,u8* src); //更新全部字库
u8 font_init(void); //初始化字库
#endif
这里,我们可以看到ftinfo的结构体定义,总共占用33个字节,第一个字节用来标识字库是否OK,其他的用来记录地址和文件大小。
接下来打开text.c文件,代码如下:
//code 字符指针开始
//从字库中查找出字模
//code 字符串的开始地址,GBK码
//mat 数据存放地址 (size/8+((size%8)?1:0))*(size) bytes大小
//size:字体大小
void Get_HzMat(unsigned char *code,unsigned char
*mat,u8 size)
{
unsigned
char qh,ql;
unsigned
char i;
unsigned
long foffset;
u8
csize=(size/8+((size%8)?1:0))*(size);//得到字体一个字符对应点阵集所占的字节数
qh=*code;
ql=*(++code);
if(qh<0x81||ql<0x40||ql==0xff||qh==0xff)//非 常用汉字
{
for(i=0;i<csize;i++)*mat++=0x00;//填充满格
return; //结束访问
}
if(ql<0x7f)ql-=0x40;//注意!
else
ql-=0x41;
qh-=0x81;
foffset=((unsigned
long)190*qh+ql)*csize; //得到字库中的字节偏移量
switch(size)
{
case
12:W25QXX_Read(mat,foffset+ftinfo.f12addr,csize); break;
case
16:W25QXX_Read(mat,foffset+ftinfo.f16addr,csize);break;
case
24:W25QXX_Read(mat,foffset+ftinfo.f24addr,csize);break;
}
}
//显示一个指定大小的汉字
//x,y :汉字的坐标
//font:汉字GBK码
//size:字体大小
//mode:0,正常显示,1,叠加显示
void Show_Font(u16 x,u16 y,u8 *font,u8 size,u8
mode)
{
u8
temp,t,t1;
u16
y0=y;
u8 dzk[72];
u8
csize=(size/8+((size%8)?1:0))*(size);//得到字体一个字符对应点阵集所占的字节数
if(size!=12&&size!=16&&size!=24)return; //不支持的size
Get_HzMat(font,dzk,size); //得到相应大小的点阵数据
for(t=0;t<csize;t++)
{
temp=dzk[t]; //得到点阵数据
for(t1=0;t1<8;t1++)
{
if(temp&0x80)LCD_Fast_DrawPoint(x,y,POINT_COLOR);
else
if(mode==0)LCD_Fast_DrawPoint(x,y,BACK_COLOR);
temp<<=1;y++;
if((y-y0)==size)
{ y=y0; x++; break;}
}
}
}
//在指定位置开始显示一个字符串
//支持自动换行
//(x,y):起始坐标
//width,height:区域
//str :字符串
//size :字体大小
//mode:0,非叠加方式;1,叠加方式
void Show_Str(u16 x,u16 y,u16 width,u16
height,u8*str,u8 size,u8 mode)
{
……//此处代码省略
}
//在指定宽度的中间显示字符串
//如果字符长度超过了len,则用Show_Str显示
//len:指定要显示的宽度
void
Show_Str_Mid(u16 x,u16 y,u8*str,u8 size,u8 len)
{
……//此处代码省略
}
此部分代码总共有4个函数,我们省略了两个函数(Show_Str_Mid和Show_Str)的代码,另外两个函数,Get_HzMat函数用于获取GBK码对应的汉字字库,通过我们45.1节介绍的办法,在外部flash查找字库,然后返回对应的字库点阵。Show_Font函数用于在指定地址显示一个指定大小的汉字,采用的方法和LCD_ShowChar所采用的方法一样,都是画点显示,这里就不细说了。
text.h头文件是一些函数申明,我们这里不细说了。
前面提到我们对cc936.c文件做了修改,我们将其命名为mycc936.c,并保存在exfuns文件夹下,将工程FATFS组下的cc936.c删除,然后重新添加mycc936.c到FATFS组下,mycc936.c的源码就不贴出来了,其实就是在cc936.c的基础上去掉了两个大数组,然后对ff_convert进行了修改,详见本例程源码。
最后,我们看看main函数如下:
int main(void)
{
u32
fontcnt; u8 i,j; u8 key,t;
u8
fontx[2];//gbk码
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //初始化延时函数
uart_init(115200); //初始化串口波特率为115200
LED_Init(); //初始化LED
LCD_Init(); //LCD初始化
KEY_Init(); //按键初始化
W25QXX_Init(); //初始化W25Q128
usmart_dev.init(168); //初始化USMART
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.
while(font_init())
//检查字库
{
UPD:
LCD_Clear(WHITE); //清屏
POINT_COLOR=RED; //设置字体为红色
LCD_ShowString(30,50,200,16,16,"Explorer
STM32F4");
while(SD_Init()) //检测SD卡
{
LCD_ShowString(30,70,200,16,16,"SD
Card Failed!"); delay_ms(200);
LCD_Fill(30,70,200+30,70+16,WHITE);
delay_ms(200);
}
LCD_ShowString(30,70,200,16,16,"SD
Card OK");
LCD_ShowString(30,90,200,16,16,"Font
Updating...");
key=update_font(20,110,16,"0:");//更新字库
while(key)//更新失败
{
LCD_ShowString(30,110,200,16,16,"Font
Update Failed!"); delay_ms(200);
LCD_Fill(20,110,200+20,110+16,WHITE);
delay_ms(200);
}
LCD_ShowString(30,110,200,16,16,"Font
Update Success! ");
delay_ms(1500);
LCD_Clear(WHITE);//清屏
}
POINT_COLOR=RED;
Show_Str(30,50,200,16,"探索者STM32F407开发板",16,0);
Show_Str(30,70,200,16,"GBK字库测试程序",16,0);
Show_Str(30,90,200,16,"正点原子@ALIENTEK",16,0);
Show_Str(30,110,200,16,"2014年5月15日",16,0);
Show_Str(30,130,200,16,"按KEY0,更新字库",16,0);
POINT_COLOR=BLUE;
Show_Str(30,150,200,16,"内码高字节:",16,0);
Show_Str(30,170,200,16,"内码低字节:",16,0);
Show_Str(30,190,200,16,"汉字计数器:",16,0);
Show_Str(30,220,200,24,"对应汉字为:",24,0);
Show_Str(30,244,200,16,"对应汉字(16*16)为:",16,0);
Show_Str(30,260,200,12,"对应汉字(12*12)为:",12,0);
while(1)
{
fontcnt=0;
for(i=0x81;i<0xff;i++)
{
fontx[0]=i;
LCD_ShowNum(118,150,i,3,16); //显示内码高字节
for(j=0x40;j<0xfe;j++)
{
if(j==0x7f)continue;
fontcnt++;
LCD_ShowNum(118,170,j,3,16); //显示内码低字节
LCD_ShowNum(118,190,fontcnt,5,16);//汉字计数显示
fontx[1]=j;
Show_Font(30+132,220,fontx,24,0);
Show_Font(30+144,244,fontx,16,0);
Show_Font(30+108,260,fontx,12,0);
t=200;
while(t--)//延时,同时扫描按键
{
delay_ms(1);
key=KEY_Scan(0);
if(key==KEY0_PRES)goto
UPD;
}
LED0=!LED0;
}
}
}
}
此部分代码就实现了我们在硬件描述部分所描述的功能,至此整个软件设计就完成了。这节有太多的代码,而且工程也增加了不少,我们来看看工程的截图吧,整个工程截图如图45.3.1所示:
图45.3.1 工程建成截图
45.4 下载验证
在代码编译成功之后,我们通过下载代码到ALIENTEK探索者STM32F4开发板上,可以看到LCD开始显示汉字及汉字内码,如图45.4.1所示:
图45.4.1 汉字显示实验显示效果
一开始就显示汉字,是因为ALIENTEK探索者STM32F4开发板在出厂的时候都是测试过的,里面刷了综合测试程序,已经把字库写入到了W25Q128里面,所以并不会提示更新字库。如果你想要更新字库,那么则必须先找一张SD卡,把:光盘\5,SD卡根目录文件 文件夹下面的SYSTEM文件夹拷贝到SD卡根目录下,插入开发板,并按复位,之后,在显示汉字的时候,按下KEY0,就可以开始更新字库了。
字库更新界面如图45.4.2所示:
图45.4.2 汉字字库更新界面
正点原子探索者STM32F407开发板购买地址:http://item.taobao.com/item.htm?id=41855882779
|