[原创] 【连载】【ALIENTEK 战舰STM32开发板】STM32开发指南--第四十六章 汉字显示实验

正点原子   2013-4-6 23:06 楼主
第四十六章 汉字显示实验
汉字显示在很多单片机系统都需要用到,少则几个字,多则整个汉字库的支持,更有甚者还要支持多国字库,那就更麻烦了。本章,我们将向大家介绍,如何用STM32控制LCD显示汉字。在本章中,我们将使用外部FLASH来存储字库,并可以通过SD卡更新字库。STM32读取存在FLASH里面的字库,然后将汉字显示在LCD上面。本章分为如下几个部分:
46.1 汉字显示原理简介
46.2 硬件设计
46.3 软件设计
46.4 下载验证

46.1 汉字显示原理简介
常用的汉字内码系统有GB2312,GB13000,GBK,BIG5(繁体)等几种,其中GB2312支持的汉字仅有几千个,很多时候不够用,而GBK内码不仅完全兼容GB2312,还支持了繁体字,总汉字数有2万多个,完全能满足我们一般应用的要求。
本实例我们将制作一个GBK字库,制作好的字库放在SD卡里面,然后通过SD卡,将字库文件复制到外部FLASH芯片W25Q64里,这样,W25Q64就相当于一个汉字字库芯片了。
汉字在液晶上的显示原理与前面显示字符的是一样的。汉字在液晶上的显示其实就是一些点的显示与不显示,这就相当于我们的笔一样,有笔经过的地方就画出来,没经过的地方就不画。所以要显示汉字,我们首先要知道汉字的点阵数据,这些数据可以由专门的软件来生成。只要知道了一个汉字点阵的生成方法,那么我们在程序里面就可以把这个点阵数据解析成一个汉字。
知道显示了一个汉字,就可以推及整个汉字库了。汉字在各种文件里面的存储不是以点阵数据的形式存储的(否则那占用的空间就太大了),而是以内码的形式存储的,就是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转换助手打开这个文本文件,如图46.1.1所示:


46.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;
                     SPI_Flash_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 文件,还支持生成图片功能,并支持横向,纵向等多种扫描方式,且扫描方式可以根据用户的需求进行增加。该软件的界面如图46.1.1所示:


46.1.2 点阵字库生成器默认界面
比如我们要生成16*16的GBK字库,则选择:936中文PRC GBK,字宽和高均选择16,字体大小选择12(比较适合),然后模式选择纵向取模方式二(字节高位在前,低位在后),最后点击创建,就可以开始生成我们需要的字库了。具体设置如图46.1.3所示:


46.1.3 生成GBK16*16字库的设置方法
这里注意,软件里面的字体大小并不是我们生成点阵的大小,12字体是XP的叫法,我们字体的大小以宽和高的大小来决定!可以简单的这么认为:XP的12字体,基本上就等于16*16大小。该软件还可以生成其他很多字库,字体也可选,详细的介绍请看软件自带的《点阵字库生成器说明书》。
本章,我们生成两个字库文件GBK12.DZK和GBK16.DZK,并将后缀名改为.fon(方便识别^_^),备用。关于汉字显示原理,我们就介绍到这。



46.2 硬件设计
本章实验功能简介:开机的时候先检测W25Q64中是否已经存在字库,如果存在,则按次序显示汉字(两种字体都显示)。如果没有,则检测SD卡和文件系统,并查找SYSTEM文件夹下的FONT文件夹,在该文件夹内查找UNIGBK.BIN、GBK12.FON和GBK16.FON(这几个文件的的由来,我们前面已经介绍了)。在检测到这些文件之后,就开始更新字库,更新完毕才开始显示汉字。通过按按键KEY0,可以强制更新字库。同样我们也是用DS0来指示程序正在运行。
所要用到的硬件资源如下:
1)  指示灯DS0  
2)  KEY0按键
3)  串口
4)  TFTLCD模块
5)  SD卡
6)  SPI FLASH
这几部分分,在之前的实例中都介绍过了,我们在此就不介绍了。
46.3 软件设计
打开上一章的工程,首先在HARDWARE文件夹所在的文件夹下新建一个TEXT的文件夹。在TEXT文件夹下新建fontupd.c、fontupd.h、text.c、text.h这4个文件。并将该文件夹加入头文件包含路径。
打开fontupd.c,在该文件内输入如下代码:
#include "fontupd.h"
#include "ff.h"         
#include "flash.h"   
#include "lcd.h"  
#include "malloc.h"
u32 FONTINFOADDR=(1024*6+500)*1024;//默认是6M+500K后开始
//字库信息结构体.
//用来保存字库基本信息,地址,大小等
_font_info ftinfo;
//sd卡中的路径
const u8 *GBK16_SDPATH="0:/SYSTEM/FONT/GBK16.FON";     //GBK16存放位置
const u8 *GBK12_SDPATH="0:/SYSTEM/FONT/GBK12.FON";     //GBK12存放位置
const u8 *UNIGBK_SDPATH="0:/SYSTEM/FONT/UNIGBK.BIN"; //UNIGBK.BIN存放位置
//25Qxx中的路径
const u8 *GBK16_25QPATH="1:/SYSTEM/FONT/GBK16.FON";    //GBK12的存放位置
const u8 *GBK12_25QPATH="1:/SYSTEM/FONT/GBK12.FON";    //GBK12的存放位置
const u8 *UNIGBK_25QPATH="1:/SYSTEM/FONT/UNIGBK.BIN";//UNIGBK.BIN存放位置
//显示当前字体更新进度
//x,y:坐标
//size:字体大小
//fsize:整个文件大小
//pos:当前文件指针位置
u32 fupd_prog(u16 x,u16 y,u8 size,u32 fsize,u32 pos)
{
       float prog;
       u8 t=0XFF;
       prog=(float)pos/fsize;
       prog*=100;
       if(t!=prog)
       {
              LCD_ShowString(x+3*size/2,y,240,320,size,"%");            
              t=prog;
              if(t>100)t=100;
              LCD_ShowNum(x,y,t,3,size);//显示数值
       }
       return 0;                                 
}
//更新某一个
//x,y:坐标
//size:字体大小
//fxpath:路径
//fx:更新的内容 0,ungbk;1,gbk12;2,gbk16;
//返回值: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)     
       {
              if(fx==0)        //更新UNIGBK.BIN
              {
                     ftinfo.ugbkaddr=FONTINFOADDR+sizeof(ftinfo);
//信息头之后,紧跟UNIGBK转换码表
                    ftinfo.ugbksize=fftemp->fsize;                           //UNIGBK大小
                    flashaddr=ftinfo.ugbkaddr;
              }else if(fx==1)       //GBK12
              {                           
                     ftinfo.f12addr=ftinfo.ugbkaddr+ftinfo.ugbksize;  //UNIGBK后,跟GBK12字库
                     ftinfo.gbk12size=fftemp->fsize;                         //GBK12字库大小
                     flashaddr=ftinfo.f12addr;                                  //GBK12的起始地址
              }else                     //GBK16
              {
                     ftinfo.f16addr=ftinfo.f12addr+ftinfo.gbk12size;  //GBK12后,跟GBK16字库
                     ftinfo.gkb16size=fftemp->fsize;                         //GBK16字库大小
                     flashaddr=ftinfo.f16addr;                                  //GBK16的起始地址
              }        
              while(res==FR_OK)//死循环执行
              {
                    res=f_read(fftemp,tempbuf,4096,(UINT *)&bread);//读取数据
                     if(res!=FR_OK)break;                                      //执行错误
                     SPI_Flash_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一起更新
//x,y:提示信息的显示地址
//size:字体大小
//提示信息字体大小
//src:0,SD卡更新.
//       1,25QXX更新                                                                              
//返回值:0,更新成功;
//            其他,错误代码.     
u8 update_font(u16 x,u16 y,u8 size,u8 src)
{     
       u8 *gbk16_path;
       u8 *gbk12_path;
       u8 *unigbk_path;
       u8 res;              
       if(src)//25qxx更新
       {
              unigbk_path=(u8*)UNIGBK_25QPATH;
              gbk12_path=(u8*)GBK12_25QPATH;
              gbk16_path=(u8*)GBK16_25QPATH;
       }else//sd卡更新
       {
              unigbk_path=(u8*)UNIGBK_SDPATH;
              gbk12_path=(u8*)GBK12_SDPATH;
              gbk16_path=(u8*)GBK16_SDPATH;
       }   
      res=0XFF;            
       ftinfo.fontok=0XFF;
      SPI_Flash_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));   
//清除之前字库成功的标志.防止更新到一半重启,导致的字库部分数据丢失.
      SPI_Flash_Read((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));//读出ftinfo结构体数据
      LCD_ShowString(x,y,240,320,size,"Updating UNIGBK.BIN");        
       res=updata_fontx(x+20*size/2,y,size,unigbk_path,0);                //更新UNIGBK.BIN
       if(res)return 1;
      LCD_ShowString(x,y,240,320,size,"Updating GBK12.BIN  ");
       res=updata_fontx(x+20*size/2,y,size,gbk12_path,1);                 //更新GBK12.FON
       if(res)return 2;
       LCD_ShowString(x,y,240,320,size,"Updating GBK16.BIN  ");
       res=updata_fontx(x+20*size/2,y,size,gbk16_path,2);                 //更新GBK16.FON
       if(res)return 3;         
       //全部更新好了
       ftinfo.fontok=0XAA;
      SPI_Flash_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));    //保存字库信息
       return 0;//无错误.         
}
//初始化字体
//返回值:0,字库完好.
//            其他,字库丢失
u8 font_init(void)
{                                                                                                      
       SPI_Flash_Init();
       FONTINFOADDR=(1024*6+500)*1024;    //W25Q64,6M以后      
       ftinfo.ugbkaddr=FONTINFOADDR+25;     //UNICODEGBK 表存放首地址固定地址
       SPI_Flash_Read((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));//读出ftinfo结构体数据
       if(ftinfo.fontok!=0XAA)return 1;               //字库错误.
       return 0;            
}
此部分代码主要用于字库的更新操作(包含UNIGBK的转换码表更新),其中ftinfo是我们在fontupd.h里面定义的一个结构体,用于记录字库首地址及字库大小等信息。因为我们将W25Q64的前6M字节给FATFS管理(用做本地磁盘),然后又预留了500K字节给用户自己使用,最后的1.5M字节(W25Q64总共8M字节),才是UNIGBK码表和字库的存储空间,所以,我们的存储地址是从(1024*6+500)*1024处开始的。最开始的25个字节给ftinfo用,用于保存ftinfo结构体数据。之后开始的是UNIGBK.bin的存放地址,之后再是GBK12.fon的存放地址,最后存放GBK16.fon。
保存该部分代码,并在工程里面新建一个TEXT的组,把fontupd.c加入到这个组里面,然后打开fontupd.h在该文件里面输入如下代码:
#ifndef __FONTUPD_H__
#define __FONTUPD_H__   
#include
//前面6Mfatfs占用了.
//6M以后紧跟的500K字节,用户可以随便用.
//6M+500K字节以后的字节,被字库占用了,不能动!
//字体信息保存地址,25个字节,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 gkb16size;              //gbk16的大小      
}_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的结构体定义,总共占用25个字节,第一个字节用来标识字库是否OK,其他的用来记录地址和文件大小。保存此部分代码,然后打开text.c文件,在该文件里面输入如下代码:
#include "sys.h"
#include "fontupd.h"
#include "flash.h"
#include "lcd.h"
#include "text.h"   
#include "string.h"                                                      
//code 字符指针开始
//从字库中查找出字模
//code 字符串的开始地址, GBK
//mat  数据存放地址 size*2 bytes大小     
void Get_HzMat(unsigned char *code,unsigned char *mat,u8 size)
{               
       unsigned char qh,ql;
       unsigned char i;                                    
       unsigned long foffset;
       qh=*code;
       ql=*(++code);
       if(qh<0x81||ql<0x40||ql==0xff||qh==0xff)// 常用汉字
       {                     
           for(i=0;i<(size*2);i++)*mat++=0x00;//填充满格
           return; //结束访问
       }         
       if(ql<0x7f)ql-=0x40;//注意!
       else ql-=0x41;
       qh-=0x81;   
       foffset=((unsigned long)190*qh+ql)*(size*2);//得到字库中的字节偏移量  
       if(size==16)SPI_Flash_Read(mat,foffset+ftinfo.f16addr,32);
       else SPI_Flash_Read(mat,foffset+ftinfo.f12addr,24);                                             
}  
//显示一个指定大小的汉字
//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[32];
       u16 tempcolor;         
       if(size!=12&&size!=16)return;//不支持的size
       Get_HzMat(font,dzk,size);//得到相应大小的点阵数据
       if(mode==0)//正常显示
       {     
           for(t=0;t
           {                                                                                          
                  temp=dzk[t];//得到12数据                          
               for(t1=0;t1<8;t1++)
                     {
                            if(temp&0x80)LCD_DrawPoint(x,y);
                           else
                            {
                                   tempcolor=POINT_COLOR;
                                   POINT_COLOR=BACK_COLOR;
                                   LCD_DrawPoint(x,y);
                                   POINT_COLOR=tempcolor;//还原
                            }
                            temp<<=1; y++;
                            if((y-y0)==size) { y=y0; x++; break; }
                     }     
           }
       }else//叠加显示
       {
           for(t=0;t
           {                                                                                          
                  temp=dzk[t];//得到12数据                          
               for(t1=0;t1<8;t1++)
                     {
                            if(temp&0x80)LCD_DrawPoint(x,y);   
                            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码对应的汉字字库,通过我们46.1节介绍的办法,在外部flash查找字库,然后返回对应的字库点阵。Show_Font函数用于在指定地址显示一个指定大小的汉字,采用的方法和LCD_ShowChar所采用的方法一样,都是画点显示,这里就不细说了。保存此部分代码,并把text.c文件加入TEXT组下。text.h里面都是一些函数申明,这里我们就不贴出来了,详见光盘本例程源码。
前面提到我们队cc936.c文件做了修改,我们将其命名为mycc936.c,并保存在exfuns文件夹下,将工程FATFS组下的cc936.c删除,然后重新添加mycc936.c到FATFS组下,mycc936.c的源码就不贴出来了,其实就是在cc936.c的基础上去掉了两个大数组,然后对ff_convert进行了修改,详见光盘本例程源码。
   
最后,我们在test.c里面修改main函数如下:
int main(void)
{                  
       u32 fontcnt;            
       u8 i,j;
       u8 fontx[2];//gbk码
       u8 key,t;         
     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.
       while(font_init())          //检查字库
       {
UPD:   
              LCD_Clear(WHITE);                //清屏
             POINT_COLOR=RED;                //设置字体为红色              
              LCD_ShowString(60,50,200,16,16,"Warship STM32");
              while(SD_Initialize())           //检测SD卡
              {
                     LCD_ShowString(60,70,200,16,16,"SD Card Failed!"); delay_ms(200);
                     LCD_Fill(60,70,200+60,70+16,WHITE); delay_ms(200);                                 
              }                                                                                                  
              LCD_ShowString(60,70,200,16,16,"SD Card OK");
              LCD_ShowString(60,90,200,16,16,"Font Updating...");
              key=update_font(20,110,16,0);//从SD卡更新
              while(key)//更新失败           
              {                                 
                     LCD_ShowString(60,110,200,16,16,"Font Update Failed!"); delay_ms(200);   
                     LCD_Fill(20,110,200+20,110+16,WHITE); delay_ms(200);                           
              }              
              LCD_ShowString(60,110,200,16,16,"Font Update Success!");
              delay_ms(1500);   
              LCD_Clear(WHITE);//清屏         
       }   
       POINT_COLOR=RED;      
       Show_Str(60,50,200,16,"战舰 STM32开发板",16,0);                                       
       Show_Str(60,70,200,16,"GBK字库测试程序",16,0);                                 
       Show_Str(60,90,200,16,"正点原子@ALIENTEK",16,0);                                   
       Show_Str(60,110,200,16,"2012年9月18日",16,0);
       Show_Str(60,130,200,16,"按KEY0,更新字库",16,0);
      POINT_COLOR=BLUE;  
       Show_Str(60,150,200,16,"内码高字节:",16,0);                                   
       Show_Str(60,170,200,16,"内码低字节:",16,0);                                   
       Show_Str(60,190,200,16,"对应汉字(16*16)为:",16,0);                  
       Show_Str(60,212,200,12,"对应汉字(12*12)为:",12,0);                  
       Show_Str(60,230,200,16,"汉字计数器:",16,0);
       LCD_Fill(60,130,200+60,130+16,WHITE);                 
       while(1)
       {
              fontcnt=0;
              for(i=0x81;i<0xff;i++)
              {            
                     fontx[0]=i;
                     LCD_ShowNum(148,150,i,3,16);//显示内码高字节   
                     for(j=0x40;j<0xfe;j++)
                     {
                            if(j==0x7f)continue;
                            fontcnt++;
                            LCD_ShowNum(148,170,j,3,16);        //显示内码低字节  
                            LCD_ShowNum(148,230,fontcnt,5,16);//显示内码低字节
                           fontx[1]=j;
                            Show_Font(204,190,fontx,16,0);   
                            Show_Font(168,212,fontx,12,0);                             
                            t=200;
                            while(t--)//延时,同时扫描按键
                            {
                                   delay_ms(1);
                                   key=KEY_Scan(0);
                                   if(key==KEY_RIGHT)goto UPD;
                            }
                            LED0=!LED0;
                     }   
              }     
       }
}
此部分代码就实现了我们在硬件描述部分所描述的功能,至此整个软件设计就完成了。这节有太多的代码,而且工程也增加了不少,我们来看看工程的截图吧,整个工程截图如图46.3.1所示:


                                            图46.3.1 工程建成截图46.4 下载验证
在代码编译成功之后,我们通过下载代码到ALIENTEK战舰STM32开发板上,可以看到LCD开始显示汉字及汉字内码,如图46.4.1所示:


图46.4.1 汉字显示实验显示效果
一开始就显示汉字,是因为ALIENTEK战舰STM32开发板在出厂的时候都是测试过的,里面刷了综合测试程序,已经把字库写入到了W25Q64里面,所以并不会提示更新字库。如果你想要更新字库,那么则必须先找一张SD卡,把我们提供的SYSTEM文件夹COPY到SD卡根目录下,重启程序。然后,在显示汉字的时候,按下KEY0,就可以开始更新字库了。
字库更新界面如图46.4.2所示:


图46.4.2 汉字字库更新界面
我们还可以通过USMART来测试该实验,将Show_Str函数加入USMART控制(方法前面已经讲了很多次了),就可以通过串口调用该函数,在屏幕上显示任何你想要显示的汉字了,有兴趣的朋友可以测试一下。
《STM32开发指南》第四十六章 汉字显示实验.rar (723.53 KB)
(下载次数: 47, 2013-4-6 23:06 上传)


实验41 汉字显示实验.rar (776.37 KB)
(下载次数: 59, 2013-4-6 23:06 上传)



我的淘宝:http://shop62103354.taobao.com

回复评论 (4)

顶一个@!!!!!!
点赞  2013-4-7 11:23
顶一个
点赞  2013-5-29 09:53
这段代码可以显示16*16 12*12 的GBK点阵 但是我试了下不能显示24*24和32*32的GBK点阵 这是为什么呢
点赞  2015-4-14 15:12
找到原因了 如果字体大小大于16的话就不能用 SIZE*2 比如24的话就要用 SIZE*3 32就要用 SIZE*4 所以 字节数也要提取出来
点赞  2015-4-14 16:24
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复