2)摘自《STM32F7 开拓指南(HAL 库版)》关注官方微旗子暗记"大众年夜众号,获取更多资料:正点原子
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html

第四十六章 汉字显示实验

汉字显示在很多单片机系统都须要用到,少则几个字,多则全体汉字库的支持,更有甚者
还要支持多国字库,那就更麻烦了。本章,我们将向大家先容,如何用 STM32F767 掌握 LCD
显示汉字。在本章中,我们将利用外部 SPI FLASH 来存储字库,并可以通过 SD 卡更新字库。
STM32F767 读取存在 SPI FLASH 里面的字库,然后将汉字显示在 LCD 上面。本章分为如下几
个部分:
46.1 汉字显示事理简介
46.2 硬件设计
46.3 软件设计
46.4 下载验证
46.1 汉字显示事理简介
常用的汉字内码系统有 GB2312,GB13000,GBK,BIG5(繁体)等几种,个中 GB2312
支持的汉字仅有几千个,很多时候不足用,而 GBK 内码不仅完备兼容 GB2312,还支持了繁体
字,总汉字数有 2 万多个,完备能知足我们一样平常运用的哀求。
本实例我们将制作三个 GBK 字库,制作好的字库放在 SD 卡里面,然后通过 SD 卡,将字
库文件复制到外部 FLASH 芯片 W25Q256 里,这样,W25Q256 就相称于一个汉字字库芯片了。
汉字在液晶上的显示事理与前面显示字符的是一样的。汉字在液晶上的显示实在便是一些
点的显示与不显示,这就相称于我们的笔一样,有笔经由的地方就画出来,没经由的地方就不
画,我们以 1212 的汉字为例,假设其取模方向为从上到下,从左到右的方向取模,且高位在
前,那么其取模事理如图 46.1.1 所示:
图 46.1.1 从上到下,从左到右取模事理
图中,我们取模的时候,从最左上方的点开始取(从上到下,从左到右),且高位在前(bit7
在表示第一个位),那么第一个字节便是:0X11(1,表示浅蓝色的点,即要画出来的点,0
则表示不要画出来),第二个字节是:0X10,第三个字节(到第二列了,每列 2 个字节)是:
0X1E……,依次类推,一个 1212 的汉字,统共有 12 列,每列 2 个字节,统共须要 24 个字节
来表示。
在显示的时候,我们只须要读取这个汉字的点阵数据(1212 字体,一个汉字的点阵数据
为 24 个字节),然后将这些数据,按取模办法,反向解析出来(坐标要处理好),每个字节,
是 1 的位,就画出来,不是 1 的位,就忽略,这样,就可以显示出这个汉字了。
以是要显示汉字,我们首先要知道汉字的点阵数据,这些数据可以由专门的软件来天生。
知道显示了一个汉字,就可以推及全体汉字库了。汉字在各种文件里面的存储不因此点阵数据
的形式存储的(否则那占用的空间就太大了),而因此内码的形式存储的,便是
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),统共就有 126190=23940 个汉字。我
们的点阵库只要按照这个编码规则从 0X8140 开始,逐一建立,每个区的点阵大小为每个汉字
所用的字节数190。这样,我们就可以得到在这个字库里面定位汉字的方法:
当 GBKL<0X7F 时:Hp=((GBKH-0x81)190+GBKL-0X40)(size2);
当 GBKL>0X80 时:Hp=((GBKH-0x81)190+GBKL-0X41)(size2);
个中 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 转换助手打开这个文本文件,如图 46.1.2
所示:
图 46.1.2 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 /UINTdir/ 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 /UINTdir/ 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 UNICODEelse 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+i4+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 文件,还支持天生图片功能,并支持横向,纵向等多种扫描办法,且扫描办法
可以根据用户的需求进行增加。该软件的界面如图 46.1.3 所示:
图 46.1.3 点阵字库天生器默认界面
要天生 1616 的 GBK 字库,则选择:936 中文 PRC GBK,字宽和高均选择 16,字体大小
选择 12,然后模式选择纵向取模办法二(从上到下,从左到右,且字节高位在前,低位在后),
末了点击创建,就可以开始天生我们须要的字库了(.DZK 文件,在天生完往后,我们手动修正
后缀为.fon)。详细设置如图 46.1.4 所示:
图 46.1.4 天生 GBK1616 字库的设置方法
把稳:电脑真个字体大小与我们天生点阵大小的关系为:
fsize=dsize6/8
个中,fsize 是电脑端字体大小,dsize 是点阵大小(12、16、24、32 等)。以是 1616 点阵
大小对应的是 12 字体。
天生完往后,我们把文件名和后缀改成:GBK16.FON(这里是手动修正后缀!
!
)。同样的
方法,天生 1212 的点阵库(GBK12.FON)、2424 的点阵库(GBK24.FON)和 3232 的点阵
库(GBK32.FON),统共制作 4 个字库。
其余,该软件还可以天生其他很多字库,字体也可选,大家可以根据自己的须要按照上面
的方法天生即可。该软件的详细先容请看软件自带的《点阵字库天生器解释书》,关于汉字显示
事理,我们就先容到这。
46.2 硬件设计
本章实验功能简介:开机的时候先检测 W25Q256 中是否已经存在字库,如果存在,则按
次序显示汉字(四种字体都显示)。如果没有,则检测 SD 卡和文件系统,并查找 SYSTEM 文件
夹下的 FONT 文件夹,在该文件夹内查找 UNIGBK.BIN、GBK12.FON、GBK16.FON、
GBK24.FON 和 GBK32.FON(这几个文件的由来,我们前面已经先容了)。在检测到这些文
件之后,就开始更新字库,更新完毕才开始显示汉字。通过按按键 KEY0,可以逼迫更新字库。
同样我们也是用 DS0 来指示程序正在运行。
所要用到的硬件资源如下:
1) 指示灯 DS0
2) KEY0 按键
3) 串口
4) LCD 模块
5) SD 卡
6) SPI FLASH
这几部分分,在之前的实例中都先容过了,我们在此就不先容了。
46.3 软件设计
打开本章实验目录可以看到,首先在工程根目录文件夹下面新建了一个 TEXT 的文件夹。
在 TEXT 文件夹下新建 fontupd.c、fontupd.h、text.c、text.h 这 4 个文件。同时,我们在实验工
程中新建了 TEXT 分组,将新建的源文件加入到了分组之下,并将头文件包含路径加入到了工
程的 PATH 中。
打开 fontupd.c,代码如下:
//字库区域占用的总扇区数大小(4 个字库+unigbk 表+字库信息=6302984 字节,约占 1539 个W25QXX 扇区,一个扇区 4K 字节)#define FONTSECSIZE1539//字库存放起始地址#define FONTINFOADDR 1024102425//开拓板是从 25M 地址往后开始存放字库//前面 25M 被 fatfs 占用了,25M 往后紧跟 4 个字库+UNIGBK.BIN,总大//小 6.01M,被字库占用了,不能动! 31.01M 往后,用户可以自由利用.//用来保存字库基本信息,地址,大小等_font_info ftinfo;//字库存放在磁盘中的路径u8const GBK_PATH[5]={"/SYSTEM/FONT/UNIGBK.BIN",//UNIGBK.BIN 的存放位置"/SYSTEM/FONT/GBK12.FON", //GBK12 的存放位置"/SYSTEM/FONT/GBK16.FON", //GBK16 的存放位置"/SYSTEM/FONT/GBK24.FON", //GBK24 的存放位置"/SYSTEM/FONT/GBK32.FON", //GBK32 的存放位置};//更新时的提示信息u8const UPDATE_REMIND_TBL[5]={"Updating UNIGBK.BIN", //提示正在更新 UNIGBK.bin"Updating GBK12.FON",//提示正在更新 GBK12"Updating GBK16.FON",//提示正在更新 GBK16"Updating GBK24.FON",//提示正在更新 GBK24"Updating GBK32.FON",//提示正在更新 GBK32};//显示当前字体更新进度//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;4,gbk32;//返回值:0,成功;其他,失落败.u8 updata_fontx(u16 x,u16 y,u8 size,u8 fxpath,u8 fx){u32 flashaddr=0;FIL fftemp;u8 tempbuf;u8 res;u16 bread;u32 offx=0;u8 rval=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.BINftinfo.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.gbk24size=fftemp->fsize;//GBK24 字库大小flashaddr=ftinfo.f24addr;//GBK24 的起始地址break;case 4:ftinfo.f32addr=ftinfo.f24addr+ftinfo.gbk24size;//GBK24 之后,紧跟 GBK32 字库ftinfo.gbk32size=fftemp->fsize;//GBK32 字库大小flashaddr=ftinfo.f32addr;//GBK32 的起始地址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,GBK32 一起更新//x,y:提示信息的显示地址//size:字体大小//src:字库来源磁盘."0:",SD 卡;"1:",FLASH 盘,"2:",U 盘.//提示信息字体大小//返回值:0,更新成功;//其他,缺点代码.u8 update_font(u16 x,u16 y,u8 size,u8 src){u8 pname;u32 buf;u8 res=0;u16 i,j;FIL fftemp;u8 rval=0;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;//内存申请失落败}for(i=0;i<5;i++)//先查找文件 UNIGBK,GBK12,GBK16,GBK24,GBK32 是否正常{strcpy((char)pname,(char)src);//copy src 内容到 pnamestrcat((char)pname,(char)GBK_PATH[i]);//追加详细文件路径res=f_open(fftemp,(const TCHAR)pname,FA_READ);//考试测验打开if(res){rval|=1<<7; //标记打开文件失落败break;//出错了,直接退出}}myfree(SRAMIN,fftemp);//开释内存if(rval==0)//字库文件都存在.{LCD_ShowString(x,y,240,320,size,"Erasing sectors... ");//提示正在擦除扇区for(i=0;i<FONTSECSIZE;i++)//先擦除字库区域,提高写入速率{fupd_prog(x+20size/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);//须要擦除的扇区}for(i=0;i<5;i++)//依次更新 UNIGBK,GBK12,GBK16,GBK24,GBK32{LCD_ShowString(x,y,240,320,size,UPDATE_REMIND_TBL[i]);strcpy((char)pname,(char)src);//copy src 内容到 pnamestrcat((char)pname,(char)GBK_PATH[i]);//追加详细文件路径res=updata_fontx(x+20size/2,y,size,pname,i); //更新字库if(res){myfree(SRAMIN,buf);myfree(SRAMIN,pname);return 1+i;}}//全部更新好了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 里面定义的一个构造体,用于记录字库首地址及字库大小等信息。由于我们将
W25Q256 的前 25M 字节给 FATFS 管理(用做本地磁盘),随后,紧跟字库构造体、UNIGBK.bin、
和三个字库,这部分内容首地址是:(102412)1024,大小约 6.01M,末了 W25Q256 还剩下约
0.99M 给用户自己用。
接下来我们打开 fontupd.h 文件代码如下:
extern u32 FONTINFOADDR; //字体信息保存地址,占 41 个字节,第 1 个字节用于标记字库
//是否存在.后续每 8 个字节一组,分别保存起始地址和文件大小//字库信息构造体定义//用来保存字库基本信息,地址,大小等__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 gbk24size;//gbk24 的大小u32 f32addr;//gbk32 地址u32 gbk32size;//gbk32 的大小}_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 的构造体定义,统共占用 41 个字节,第一个字节用来标识字库是否 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)190qh+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;case 32:W25QXX_Read(mat,foffset+ftinfo.f32addr,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[128];u8 csize=(size/8+((size%8)?1:0))(size);//得到字体一个字符对应点阵集所占的字节数if(size!=12&&size!=16&&size!=24&&size!=32)return;//不支持的 sizeGet_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,u8str,u8 size,u8 mode){…//此处省略部分代码}//在指定宽度的中间显示字符串//如果字符长度超过了 len,则用 Show_Str 显示//len:指定要显示的宽度void Show_Str_Mid(u16 x,u16 y,u8str,u8 size,u8 len){…//此处省略部分代码}
此部分代码统共有 4 个函数,我们省略了两个函数(Show_Str_Mid 和 Show_Str)的代码,
其余两个函数,Get_HzMat 函数用于获取 GBK 码对应的汉字字库,通过我们 47.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 fontx[2]; //gbk 码u8 key,t; Cache_Enable(); //打开 L1-Cache MPU_Memory_Protection(); //保护干系存储区域 HAL_Init(); //初始化 HAL 库 Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz delay_init(216); //延时初始化uart_init(115200); //串口初始化 LED_Init();//初始化 LED KEY_Init(); //初始化按键 SDRAM_Init(); //初始化 SDRAM LCD_Init();//初始化 LCDW25QXX_Init();//初始化 W25Q256 my_mem_init(SRAMIN);//初始化内部内存池 my_mem_init(SRAMEX); //初始化外部 SDRAM 内存池 my_mem_init(SRAMDTCM); //初始化内部 DTCM 内存池 exfuns_init(); //为 fatfs 干系变量申请内存 f_mount(fs[0],"0:",1); //挂载 SD 卡 f_mount(fs[1],"1:",1); //挂载 SPI FLASH. f_mount(fs[2],"2:",1); //挂在 NAND FLASHwhile(font_init())//检讨字库{ UPD:LCD_Clear(WHITE);//清屏POINT_COLOR=RED;//设置字体为赤色LCD_ShowString(30,50,200,16,16,"Apollo STM32F4/F7");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,30,200,16,"水星 STM32F4/F7 开拓板",16,0);Show_Str(30,50,200,16,"GBK 字库测试程序",16,0);Show_Str(30,70,200,16,"正点原子@ALIENTEK",16,0);Show_Str(30,90,200,16,"2016 年 7 月 15 日",16,0);Show_Str(30,110,200,16,"按 KEY0,更新字库",16,0);POINT_COLOR=BLUE;Show_Str(30,130,200,16,"内码高字节:",16,0);Show_Str(30,150,200,16,"内码低字节:",16,0);Show_Str(30,170,200,16,"汉字计数器:",16,0);Show_Str(30,200,200,32,"对应汉字为:",32,0);Show_Str(30,232,200,24,"对应汉字为:",24,0);Show_Str(30,256,200,16,"对应汉字(1616)为:",16,0);Show_Str(30,272,200,12,"对应汉字(1212)为:",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,150,j,3,16); //显示内码低字节LCD_ShowNum(118,170,fontcnt,5,16);//汉字计数显示fontx[1]=j;Show_Font(30+176,200,fontx,32,0);Show_Font(30+132,232,fontx,24,0);Show_Font(30+144,256,fontx,16,0);Show_Font(30+108,272,fontx,12,0);t=200;while(t--)//延时,同时扫描按键{delay_ms(1);key=KEY_Scan(0);if(key==KEY0_PRES)goto UPD;}LED0_Toggle;}}}}
此部分代码就实现了我们在硬件描述部分所描述的功能,至此全体软件设计就完成了。这
节有太多的代码,而且工程也增加了不少,我们来看看工程的截图吧,全体工程截图如图 46.3.1
所示:
图 46.3.1 工程建成截图
46.4 下载验证
在代码编译成功之后,我们通过下载代码到 ALIENTEK 水星 STM32 开拓板上,可以看到
LCD 开始显示汉字及汉字内码,如图 46.4.1 所示:
图 46.4.1 汉字显示实验显示效果
一开始就显示汉字,是由于 ALIENTEK 水星 STM32F767 开拓板在出厂的时候都是测试过
的,里面刷了综合测试程序,已经把字库写入到了 W25Q256 里面,以是并不会提示更新字库。
如果你想要更新字库,那么则必须先找一张 SD 卡,把:光盘\5,SD 卡根目录文件 文件夹下
面的 SYSTEM 文件夹拷贝到 SD 卡根目录下,插入开拓板,并按复位,之后,在显示汉字的时
候,按下 KEY0,就可以开始更新字库了。
字库更新界面如图 46.4.2 所示:
图 46.4.2 汉字字库更新界面
我们还可以通过 USMART 来测试该实验,将 Show_Str 函数加入 USMART 掌握(方法前
面已经讲了很多次了),就可以通过串口调用该函数,在屏幕上显示任何你想要显示的汉字了,
有兴趣的朋友可以测试一下。








