常用汉字编码
GB2312
GB13000
GBK
BIG5(繁体)
汉字显示原理
汉字在液晶上的显示其实就是一些点的显示与不显示,这就相当于我们的笔一样,有笔经过的地方就画出来,没经过的地方就不画。所以要显示汉字,我们首先要知道汉字的点阵数据,这些数据可以由专门的软件来生成。只要知道了一个汉字点阵的生成方法,那么我们在程序里面就可以把这个点阵数据解析成一个汉字。
汉字内码(GBK/GB2312)->查找点阵库->解析->显示。
GBK码
每个GBK码由2个字节组成,第一个字节为0X81~0XFE,第二个字节分为两部分,一是0X40~0X7E,二是0X80~0XFE。
我们把第一个字节代表的意义称为区,那么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>0X7F时:Hp=((GBKH-0x81)*190+GBKL-0X41)*(size*2);
其中GBKH、GBKL分别代表GBK的第一个字节和第二个字节(也就是高位和低位),size代表汉字字体的大小(比如16字体,12字体等),Hp则为对应汉字点阵数据在字库里面的起始地址(假设是从0开始存放)。
点阵字库的制作
打开ts3.exe,按图示设置。
电脑端的字体大小与我们生成点阵大小关系为: fsize=disize* 6/8;
其中fsize为电脑端字体大小,dsize是点阵大小(12,16,24)。所以16X16点阵大小对应的是12字体
汉字显示程序思路
存(更新)字库(fontupd.c):
做好字库。
将字库GBK12,GBK16,GBK24依次写入SPI FLASH连续地址:update_font();
字库写入完毕之后,做标记:ftinfo.fontok=0XAA;
显示汉字(text.c):
Show_Str ->Show_Font->Get_HzMat->解析显示。
源码讲解
在main.c中我们会先调用fontupd.c中的font_init()判断字库是否准备好
//初始化字体
//返回值:0,字库完好.
// 其他,字库丢失
u8 font_init(void)
{
u8 t=0;
W25QXX_Init();
while(t<10)//连续读取10次,都是错误,说明确实是有问题,得更新字库了
{
t++;
W25QXX_Read((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo));//读出ftinfo结构体数据
/*
结构体ftinfo中存放着字库的信息大小和位置,其中fontok用来判断字库是否准备好
FONTINFOADDR字库存放起始地址,Explorer STM32F4是从12M地址以后开始存放字库,前面12M被
fatfs占用了.
*/
if(ftinfo.fontok==0XAA)break;
delay_ms(20);
}
if(ftinfo.fontok!=0XAA)return 1;
return 0;
}
如果字库有问题,我们从SD卡加载字库并用updata_font更新字库,
//updata_font的部分代码
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
使用文件系统更新四个字库,更新每一个字库最终调用updata_fontx
//更新某一个字库
//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;
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.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;
}
根据字库的路径,把文件打开获取字库相关信息,然后将字库写入SPI FLASH,我们知道相关大小就可以用fupd_prog显示进度。
//显示当前字体更新进度
//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;
}
显示汉字要调用text.c中的Show_Str,Show_Str会将中文和字符区分,如果是字符就调用LCD_ShowChar,如果是中文就调用Show_Font.
Show_Font会根据汉字编码用Get_HzMat查找点阵数据,然后在lcd是一个一个位的显示。