24. 汉字显示实验
扫描二维码
随时随地手机看文章
一。汉字显示原理
1. 常用汉字编码
这里我们采用GBK编码
2. 汉字显示原理
假如显示一个16*8的汉字,高16,宽8,则高需要2个字节,显示这个汉字需要2*8=16个字节,这16个字节的数据就是汉字的点阵数据。
3. 字符/汉字显示方法
利用软件取模
取模方向设置:
扫描方向:纵向为先显示第一个字节,然后显示第二个字节,从低位到高位,然后换到第二列。
数据前缀改为“0x”,数据后缀改为“,”,行后缀为“;”。
对于汉字显示采用16*16。
例如:开,为16*16,得到下面的字模数据。
得到字模数据后,程序中就可以从中取得相应的字节进行显示。
4. 汉字显示的过程
如何显示字库中所有的汉字?利用专用的软件实现。
根据内码,查找相应的字库点阵,然后解析,显示。
对于汉字的内码常用GBK码。每一个汉字都对应一个GBK的编码。
认识GBK码:
GBK码共有126个区,每个区里有190个汉字。
如何根据GBK编码找到汉字:
1.首先找到GBK编码的高位,得到这个汉字在哪个区。(高位-0x81)确定在哪个区。
(GBKH-0x81)*190表示这个汉字所在区的前面几个区已经有这么多个汉字。
当GBKL<0x7F时:汉字所在区的偏移为GBKL-0x40。
当GBKL>0x7F时:汉字所在区的偏移为GBKL-0x41,因为前面空掉一个0x7F。
因为每个汉字占用的字节数为size*2,比如16*16的汉字,那么每个汉字占用的空间是16*2=32个字节。
当 GBKL<0X7F 时:Hp=((GBKH-0x81)*190+GBKL-0X40)*(size*2);
当 GBKL>0X80 时:Hp=((GBKH-0x81)*190+GBKL-0X41)*(size*2);
根据GBK编码得到这个汉字所在汉字库的起始地址。
从以上的方法找到了汉字所在的位置,下面开始制作点阵字库。
采用字库制作软件制作汉字库。
制作点阵字库
生成字库软件设置:
注意字体大小选12,这个是电脑端字体的大小。
注意电脑端的字体大小与生成点阵的字体大小有区别
模式选择纵向取模方式二。
生成点阵字库后,要用文件系统读取字库,然后存到SPI-Flash中。
在SPI FLASH中的某个地方写个标志0xAA,表示已经更新过字库。
Show_Str函数既能显示字符,也能显示汉字,如果是显示汉字就调用Show_Font函数。
Get_HzMat函数根据编码取得字库中汉字的字模。然后根据字模数据解析,显示。
二。实验讲解
生成的字库文件名为:16.DZK,字库大小为766,080字节,然后把名字改成:GBK16.FON。
然后把这个文件存进SD卡,程序会从SD卡中把这个文件写入SPI FLASH。
fontupd.c 更新字库
text.c 显示汉字
1. 程序主函数中开始的时候会检查字库,调用fontinit()函数。
//初始化字体
//返回值:0,字库完好.
//其他,字库丢失
u8 font_init(void)
{
SPI_Flash_Init(); //初始化25Q**系列。
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;
}
字体信息保存地址,占25个字节,第1个字节用于标记字库是否存在.后续每8个字节一组,分别保存起始地址和文件大小。
//字库信息结构体定义
//用来保存字库基本信息,地址,大小等
__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;
__packed是字节对齐的意思。
比如说int float double char它的总大小是4 + 4 + 8 + 1 = 17
但如果不用__packed的话,系统将以默认的方式对齐(假设是4字节),那么它占4 + 4 + 8 + 4 = 20;(不足4字节以4字节补齐)。
ftinfo 是我们在 fontupd.h 里面定义的一个结构体,用于记录字库首地址及字库大小等信息。
因为我们将W25Q64 的前 6M 字节给 FATFS 管理(用做本地磁盘),然后又预留了 500K 字节给用户自己使用,最后的 1.5M 字节(W25Q64 总共 8M 字节),才是 UNIGBK 码表和字库的存储空间。
finfo结构体的存放地址为:
FONTINFOADDR=(1024*6+500)*1024;
GBK字库存放的首地址为:
ftinfo.ugbkaddr=FONTINFOADDR+25;
2. 主函数如果没有检测到字库
a. 初始化SD卡
while(SD_Initialize()) //检测SD卡
没检测到SD卡报错
LCD_ShowString(60,70,200,16,16,"SD Card Failed!");
b. 调用 update_font(20,110,16,0); 从SD卡更新字库
自己做的字库要放在SD卡SYSTEM/FONT目录下
文件名为GBK16.FON或GBK24.FON
//更新字体文件,UNIGBK,GBK12,GBK16一起更新
//x,y:提示信息的显示地址
//size:字体大小
//提示信息字体大小
//src: 0, 从SD卡更新.
//src: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;
//全部更新好了,写入标志位0xAA。
ftinfo.fontok=0XAA;
SPI_Flash_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo)); //保存字库信息
return 0;//无错误.
}
调用updata_fontx(u16 x,u16 y,u8 size,u8 *fxpath,u8 fx);
//更新某一个
//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; //分配失败,rval = 1;
tempbuf=mymalloc(SRAMIN,4096); //为数据缓存区分配4096个字节空间
if (tempbuf==NULL) rval=1; //分配失败,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; //UNIGBK字库的地址
}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; //,如果读出来的数据不是4092个,说明数据读完了,跳出死循环.
}
f_close(fftemp); //关闭文件
}
myfree(SRAMIN,fftemp); //释放内存
myfree(SRAMIN,tempbuf); //释放内存
return res;
}
f_open函数用法:
The f_open function creates afile objectto be used to access the file.
FRESULTf_open(
FIL*fp,
constTCHAR*path,
BYTEmode
);
f_read函数的用法:
Thef_readfunctionreadsdatafromafile.
FRESULTf_read(
FIL*fp,
void*buff,
UINTbtr,
UINT*br
);
text.c中的函数:
1.根据GBK码找到字模函数Get_HzMat(unsignedchar*code,unsignedchar*mat,u8size)
//code字符指针开始
//从字库中查找出字模
//code字符串的开始地址,GBK码
//mat字模数据存放地址size*2bytes大小
voidGet_HzMat(unsignedchar*code,unsignedchar*mat,u8size)//汉字取模
{
unsignedcharqh,ql;
unsignedchari;
unsignedlongf