单片机 液晶屏 存储
液晶屏使用的汉字库的原理是什么?
单片机是如何实现液晶屏汉字库的显示呢?
回帖(1)
2022-1-19 11:10:34
液晶屏使用的汉字库的原理:
*每个汉字由多个点组成,即点阵,比如16号字体(实际上电脑对字号的叫法和点阵大小不一样,实际上这个电脑成为12号字体,原谅下面我也叫16号字体),就是16*16的点阵,即32个字节的数据,那么每一个字体的点阵由点阵取模软件可以得到
*同号大小字体所有汉字取模后的点阵数据顺序往后存放,那么我们怎么找到每一个汉字对应的点阵数据呢,然后进行液晶屏画点函数即可显示出该函数,这是通过把每一个汉字进行编码(就像人的身份证号),那就叫做汉字内码。
汉字在各种文件里面的存储不是以点阵数据的形式存储的(否则那占用的空间就太大了),而是以内码的形式存储的,就是
GB2312/GBK/BIG5 等这几种的一种,每个汉字对应着一个内码,在知道了内码之后再去字库里面查找这个汉字的点阵数据,然后在液晶上显示出来。这里我举个例,比如我们打开记事本,随便敲两个16号大小的字,然后保存,这个txt文件占的大小应该是4字节,而不是2*32=64字节,我们去看看这个文件属性,果然是占用4字节。
那这里的4个字节到底存了什么呢,刚刚说了每一个汉字都对应一个汉字内码(汉字的ID),以GBK这种汉字编码方式为例,每个 GBK 码由 2 个字节组成,第一个字节为大小范围0X81~0XFE(称为高字节GBKH),第二个字节(称为低字节GBKL),有两个汉字,所以就占4个字节了。有了一个汉字的内码和字体大小后通过一个计算公式就可以定位到这个汉字对应的点阵数据了(因为汉字取模时候每个汉字的点阵数据都是依次往后存放的,不需要文件系统的方式以文件形式存放,就是在外部flash芯片中顺序存放即可,这样单片机不需要上文件系统就能使用字库),然后在液晶屏上面绘制出来即可。对应的就是这个查找函数
//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
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;
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)
------------------------------------------------------------------------------------------------------------------------------------------------
上面说完了汉字库以及显示原理,接下来说一下单片机如何实现的:
单片机上电后,会检测外部flash芯片里是否有字库,原理:正点原子事先约定了在外部flash芯片(32MB的)的25MB的位置开始存放字库信息头结构体 ftinfo,然后依次往后紧接着存放UNIGBK转换码表(这个是干啥的,文章末尾我会进行单独介绍),GBK12,16,24,32四种字体。
//字体信息保存地址,占41个字节,第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 gbk24size; //gbk24的大小
u32 f32addr; //gbk32地址
u32 gbk32size; //gbk32的大小
}_font_info;
通过检测上面的字库存在标志就能知道字库存在与否,如果不存在,那么我们就得进行字库更新,即从插入的sd卡(提前在电脑上制作好的字库以文件形式放在sd卡里)中依次读取字库文件存放到flash中来即可。
接下来讲解一下更新字库的过程:
读取sd卡中事先做好的几个字库文件
//字库存放在磁盘(sd卡)中的路径
u8*const 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的存放位置
};
然后
res=f_open(fftemp,(const TCHAR*)fxpath,FA_READ); // fxpath就是字库的全路径
case 0: //更新UNIGBK.BIN
ftinfo.ugbkaddr=FONTINFOADDR+sizeof(ftinfo); //信息头之后,紧跟UNIGBK转换码表
ftinfo.ugbksize=fftemp->obj.objsize; //UNIGBK大小
flashaddr=ftinfo.ugbkaddr;
break;
case 1:
ftinfo.f12addr=ftinfo.ugbkaddr+ftinfo.ugbksize; //UNIGBK之后,紧跟GBK12字库
ftinfo.gbk12size=fftemp->obj.objsize; //GBK12字库大小
flashaddr=ftinfo.f12addr; //GBK12的起始地址
break;
case 2:
ftinfo.f16addr=ftinfo.f12addr+ftinfo.gbk12size; //GBK12之后,紧跟GBK16字库
ftinfo.gbk16size=fftemp->obj.objsize; //GBK16字库大小
flashaddr=ftinfo.f16addr; //GBK16的起始地址
break;
。。。。。。
上面是设置信息头结构体的每个字库起始地址信息等等,然后就要读取复制sd卡字库内容进入外部flash了
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个数据 ,读取复制sd卡字库内容进入外部flash
offx+=bread;
fupd_prog(x,y,size,fftemp->obj.objsize,offx); //进度显示
if(bread!=4096)break; //说明读完了,因为没有读到想要的4096个,就说明不够读了,就是完成了
}
最后保存上信息头结构体内容进入外部flash即可,同时置字库存在标志为0XAA
//全部更新好了
ftinfo.fontok=0XAA;
W25QXX_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo)); //保存字库信息
---------------------------------------------------------------------------------------------------------------------------------------------
关于 UNIGBK转换码表 的作用:
文件系统中要用 ffunicode.c,以支持长文件名,但是 ffunicode.c 文件里面中文转换
(中文的页面编码代号为:936)的两个数组太大了(172KB),直接刷在单片机里面,太占用
flash 了,所以我们必须把这两个数组存放在外部 flash
将 C 语言数组转换为.bin 文件,然后只需要将 UNIGBK.bin 保存到外部 FLASH
就实现了该数组的转移
ffunicode.c中的代码 W25QXX_Read((u8*)&t,ftinfo.ugbkaddr+i*4+gbk2uni_offset,4);//读出 4 个字节
就是实现从外部flash读取出那两个大数组。
液晶屏使用的汉字库的原理:
*每个汉字由多个点组成,即点阵,比如16号字体(实际上电脑对字号的叫法和点阵大小不一样,实际上这个电脑成为12号字体,原谅下面我也叫16号字体),就是16*16的点阵,即32个字节的数据,那么每一个字体的点阵由点阵取模软件可以得到
*同号大小字体所有汉字取模后的点阵数据顺序往后存放,那么我们怎么找到每一个汉字对应的点阵数据呢,然后进行液晶屏画点函数即可显示出该函数,这是通过把每一个汉字进行编码(就像人的身份证号),那就叫做汉字内码。
汉字在各种文件里面的存储不是以点阵数据的形式存储的(否则那占用的空间就太大了),而是以内码的形式存储的,就是
GB2312/GBK/BIG5 等这几种的一种,每个汉字对应着一个内码,在知道了内码之后再去字库里面查找这个汉字的点阵数据,然后在液晶上显示出来。这里我举个例,比如我们打开记事本,随便敲两个16号大小的字,然后保存,这个txt文件占的大小应该是4字节,而不是2*32=64字节,我们去看看这个文件属性,果然是占用4字节。
那这里的4个字节到底存了什么呢,刚刚说了每一个汉字都对应一个汉字内码(汉字的ID),以GBK这种汉字编码方式为例,每个 GBK 码由 2 个字节组成,第一个字节为大小范围0X81~0XFE(称为高字节GBKH),第二个字节(称为低字节GBKL),有两个汉字,所以就占4个字节了。有了一个汉字的内码和字体大小后通过一个计算公式就可以定位到这个汉字对应的点阵数据了(因为汉字取模时候每个汉字的点阵数据都是依次往后存放的,不需要文件系统的方式以文件形式存放,就是在外部flash芯片中顺序存放即可,这样单片机不需要上文件系统就能使用字库),然后在液晶屏上面绘制出来即可。对应的就是这个查找函数
//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
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;
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)
------------------------------------------------------------------------------------------------------------------------------------------------
上面说完了汉字库以及显示原理,接下来说一下单片机如何实现的:
单片机上电后,会检测外部flash芯片里是否有字库,原理:正点原子事先约定了在外部flash芯片(32MB的)的25MB的位置开始存放字库信息头结构体 ftinfo,然后依次往后紧接着存放UNIGBK转换码表(这个是干啥的,文章末尾我会进行单独介绍),GBK12,16,24,32四种字体。
//字体信息保存地址,占41个字节,第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 gbk24size; //gbk24的大小
u32 f32addr; //gbk32地址
u32 gbk32size; //gbk32的大小
}_font_info;
通过检测上面的字库存在标志就能知道字库存在与否,如果不存在,那么我们就得进行字库更新,即从插入的sd卡(提前在电脑上制作好的字库以文件形式放在sd卡里)中依次读取字库文件存放到flash中来即可。
接下来讲解一下更新字库的过程:
读取sd卡中事先做好的几个字库文件
//字库存放在磁盘(sd卡)中的路径
u8*const 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的存放位置
};
然后
res=f_open(fftemp,(const TCHAR*)fxpath,FA_READ); // fxpath就是字库的全路径
case 0: //更新UNIGBK.BIN
ftinfo.ugbkaddr=FONTINFOADDR+sizeof(ftinfo); //信息头之后,紧跟UNIGBK转换码表
ftinfo.ugbksize=fftemp->obj.objsize; //UNIGBK大小
flashaddr=ftinfo.ugbkaddr;
break;
case 1:
ftinfo.f12addr=ftinfo.ugbkaddr+ftinfo.ugbksize; //UNIGBK之后,紧跟GBK12字库
ftinfo.gbk12size=fftemp->obj.objsize; //GBK12字库大小
flashaddr=ftinfo.f12addr; //GBK12的起始地址
break;
case 2:
ftinfo.f16addr=ftinfo.f12addr+ftinfo.gbk12size; //GBK12之后,紧跟GBK16字库
ftinfo.gbk16size=fftemp->obj.objsize; //GBK16字库大小
flashaddr=ftinfo.f16addr; //GBK16的起始地址
break;
。。。。。。
上面是设置信息头结构体的每个字库起始地址信息等等,然后就要读取复制sd卡字库内容进入外部flash了
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个数据 ,读取复制sd卡字库内容进入外部flash
offx+=bread;
fupd_prog(x,y,size,fftemp->obj.objsize,offx); //进度显示
if(bread!=4096)break; //说明读完了,因为没有读到想要的4096个,就说明不够读了,就是完成了
}
最后保存上信息头结构体内容进入外部flash即可,同时置字库存在标志为0XAA
//全部更新好了
ftinfo.fontok=0XAA;
W25QXX_Write((u8*)&ftinfo,FONTINFOADDR,sizeof(ftinfo)); //保存字库信息
---------------------------------------------------------------------------------------------------------------------------------------------
关于 UNIGBK转换码表 的作用:
文件系统中要用 ffunicode.c,以支持长文件名,但是 ffunicode.c 文件里面中文转换
(中文的页面编码代号为:936)的两个数组太大了(172KB),直接刷在单片机里面,太占用
flash 了,所以我们必须把这两个数组存放在外部 flash
将 C 语言数组转换为.bin 文件,然后只需要将 UNIGBK.bin 保存到外部 FLASH
就实现了该数组的转移
ffunicode.c中的代码 W25QXX_Read((u8*)&t,ftinfo.ugbkaddr+i*4+gbk2uni_offset,4);//读出 4 个字节
就是实现从外部flash读取出那两个大数组。
举报
更多回帖