字符显示
在OLED和LCD这两篇文章中,我们介绍了通过不同屏幕显示的方法,简单来说都是控制像素点的亮灭或颜色值,通过排列来显示相应的内容。其中显示字符所使用的像素点排列,被称为字模,也可以称为点阵数据。
在OLED一文的最后,我们介绍了字模的获取工具PCtoLCD,通过该软件可以获得对应字符(包括汉字)的字模,当然英文字符、数字以及特殊字符,一般都将其字模存储在.h文件中,即保存在微控制器内部存储器中,通过ASCII的顺序进行调用,但是,通过这种方法显示中文字符比较困难。
首先中文字符数量较多,仅常用字就已多达6000个左右,此外,还有众多生僻字和繁体字。其次,因为数量多,因此通过ASCII方式进行顺序排列并检索十分不方便。因此对中文字符的显示,一般通过GBK编码进行排列,下面介绍GB2312编码和GBK编码的使用。
关于GB2312编码和GBK编码的区别可参考彻底搞明白 GB2312、GBK 和 GB18030。
中文字符编码
GB2312
GB2312编码方式使用区位码来查找字符,通过将字符分为94个区,每个区含有94个字符,总共8836个编码,表达8836个字符(当然,实际上只有6763 个常用的汉字和字符对应的编码可用)。在使用过程中,字符编码的第一字节(高字节)为区号,第二字节(低字节)为位号,根据对应的区号和位号即可查找到对应的字符。区位码定位图如下图所示,其中16代表的是区号。以“啊”字为例,“啊”字对应的位为01,因此“啊”字对应的区位码为“1601”。由于GB2312编码向下兼容ASCII码,因此,在实际的GB2312编码中,将区号和位号同时加上“0xA0”来区别ASCII编码段。最终“啊”字的GB2312编码为0xB0A1。
GBK
GBK编码是从GB2312基础上产生的,可以保存20000多个汉字,包括生僻字和繁体字,并同时兼容GB2312和ASCII编码方式。
GBK编码使用1字节或2字节空间来保存字符,使用1字节时,保存的字符与ASCII码对应;使用2字节时,保存的字符为中文及其他字符,在其编码区中,第一字节(区号)范围为0x81~0xFE。由于0x7F不被使用,因此第二字节(位号)分为两部分,分别为0x40~0x7E和0x80~0xFE(以0x7F为界)。并且其中与GB2312编码区重合的部分,字符相同,因此可以说GB2312码是GBK码的子集。
0x7F不使用的原因:0x7F在ASCII中表示DEL,向后删除一个字符。
并且不止0x7F,0x08等控制字符同样需要避免使用。
中文字库
与显示英文需要提供所有的英文字符一样,显示中文时同样需要提供所有的中文字符,由于中文字符太多,因此其字模一般被统一存储在一个文件中,该文件被称为字库文件,当需要显示某个中文字符时,根据上述的区位码从该字库中取出相应字符并绘制即可。
注意,字库的大小不是固定的(这句话听起来很奇怪,但我是指同一种编码都不一样),字库的大小不仅由编码方式决定(如GBK一般都要大于GB2312),还有字体大小决定。若每个字通过100个像素点决定(也就是10 * 10),那么GB2312的6737个字符,需要 6737 * 100 / 8 / 1024 = 82 KB,这对大部分微控制器来说,都是难以承受的。
字库生成
中文字库的生成和英文字模的生成相同,可以通过软件快捷生成,当然这里的软件不再是PCtoLCD那样单个字符生成,而是直接生成整个字库。
字库的生成可以使用“高通字库”,免费,打开后如下图所示。
点击左边的第二项并根据要求设置字模格式即可,参考设置如下:
注意:左上角字号的大小需要与预览下面,点阵样式中的点阵宽度和高度相匹配,计算公式为:
字号大小 = 点阵大小 * 6/8 当然,如果不相匹配也不是不可以,但显示出来会奇怪一点,可以在上面的预览界面查看。
设置结束后点击右下角即可生成字库,生成.c文件的话大小约为8.31MB的数组,一般建议生成Bin文件后将其后缀修改为FON,形成字库文件。除了生成对应文件外,还会生成一个txt文件,里面存放着提示信息。
这里生成的FON文件大约是1.51MB,同样是微控制器难以存储的,因此需要将其存储在外部存储器中。
字模获取
这里的字模获取是指如何从代码中获取对应中文字符的字模,参考txt文件中的提示信息可得,字库被分为 6 段,以其中第一段“0xA1A1 - 0xA9FE”为例,该段中的某个字符的地址addr计算方法如下,其中94是由于每个区都有94个字符,72是区内每个字符字模的字节数。(24 * 24 / 8 = 72Bytes)
代码参考如下,其中,通过GD25Q16Read是由于字库被存储在GD25Q16芯片中。
void GetCNFont24x24(u32 code, u8* buf)
{
u8 gbkH, gbkL; //GBK码高位、低位
u32 addr; //点阵数据在SPI Flash中的地址
u32 i; //循环变量
//拆分GBK码高位、低位
gbkH = code >> 8;
gbkL = code & 0xFF;
//校验高位
if((gbkH < 0x81) || (gbkH > 0xFE))
{
for(i = 0; i < 72; i++)
{
buf[i] = 0;
}
return;
}
//低位处在0x40~0x7E范围
if((gbkL >= 0x40) && (gbkL <= 0x7E))
{
addr = ((gbkH - 0x81) * 190 + (gbkL - 0x40)) * 72;
GD25Q16Read(buf, 72, addr);
}
//低位处在0x80~0xFE范围
else if((gbkL >= 0x80) && (gbkL <= 0xFE))
{
addr = ((gbkH - 0x81) * 190 + (gbkL - 0x41)) * 72;
GD25Q16Read(buf, 72, addr);
}
//出错
else
{
for(i = 0; i < 72; i++)
{
buf[i] = 0;
}
}
}
至于这里的code是从哪里来的,GBK编码的文件,对应字符存储的就是code。