嵌入式汉字显示原理及GBK编码详解
~~~~~~~~ 关于各个编码的介绍和转换可以看我的另一篇博客:【C语言实现】十六进制面值转字符串、字符面值转十六进制、UNICODE与GBK互转,UTF-8与GBK互转
(1)显示原理
~~~~~~~~ 屏幕都是有一个一个像素点组成的,不管是中文还是英文显示,其实都是把字符通过字模软件或者字库制作软件转换成了一个点阵,一个像素点就代表一个比特,例如字体为8X16像素点大小,那么点阵大小也是8X16,然后在LCD上打点显示,这样就能显示出字符来了。
如果只显示ASCII,也就是只显示英文和符号,那么就只有下面这96个可显示字符,将这96个字符进行点阵取模,就组成了ASCII字库。
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
对于只显示英文的使用情况下,倘若字体大小为8X16像素,
那么以8X16点阵大小来对这96个ASCII字符取模,
一个字符的点阵所占用的空间就是8x16/8=16字节,总占用空间就是96x16=1536字节,
这么算下来的话也就不到2K的空间,对于单片机FLASH来说能够存下。
但是如果是想要能够显示全部中文,那中文汉字的个数就多了去了,GBK汉字库收录了21886个汉字,由于中文汉字是正方形的,所有取模的话就得是1:1的点阵,要不然不好看,字是扁的!
还是以16X16点阵大小来算,16X16/8X21886=700352字节!不到684K的大小,这对于单片机的FLASH来说无比巨大了,根本存不下!
所以常见的解决办法是存到SPI接口的FLASH中,例如W25QXX系列。
(2)字符寻址
问题:使用字模软件制作好点阵字库之后,如何使用字库?
对于这个问题,中文显示和英文显示要分开来讲。
2.1 英文(ASCII)字符寻址
对于ASCII字符显示,将8X16大小的ASCII字库生成一个大的数组之后,由于每个字符占用16字节大小,所以空格【 】的偏移地址就是0,叹号【!】的偏移地址就是16,以此类推;
由于ASCII字符顺序固定,那么就可以用要显示的字符减去空格就能得到偏移地址索引,然后偏移地址索引在乘该字符的点阵大小就能得到在字库中的实际偏移地址。
例如:要显示字符【A】这个字符,那么A字符的点阵数据的偏移地址就是:
int offset = 'A' - ' ';
从offset地址开始往后的16个字节,就是字符A的点阵数据,然后将这16字节在屏幕上打点就显示出来了。
2.1 中文(GBK)字符寻址
注:GBK是GB2312的扩展,容纳的字更多了。
~
- GBK完全兼容GB2312,这意味着所有GB2312中的字符在GBK中的编码是相同的,确保了向后的兼容。
- GBK不仅包含了GB2312的全部汉字和字符,还扩展了很多新的汉字和符号,使得字符集更加丰富,能够满足更多的需求。
- 除了中文字符,GBK还支持日文假名、希腊字母、俄文等多种语言的字符,提高了其使用的灵活性。
对于中文GBK汉字显示,要找到显示的汉字点阵数据在字库中的位置,就无法直接用减法了,那么是怎么找的呢?
介绍怎么找偏移之前,需要先了解一下GBK编码。
GBK 和GB2312一样亦采用双字节表示,总体编码范围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间,剔除 xx7F 一条线。总计 23940 个码位,共收入 21886 个汉字和图形符号,其中汉字(包括部首和构件)21003 个,图形符号 883 个。
GBK码详细说明可以查看这个网站:https://www.qqxiuzi.cn/zh/hanzi-gbk-bianma.php
GB2312码详细说明可以查看这个网站:https://www.qqxiuzi.cn/zh/hanzi-gb2312-bianma.php
上面介绍了GBK的编码及个数,猛一看可能会有点蒙,下面我用一个实例来解释一下。
首先记住下面几点:
- GBK码用两个字节来表示一个汉字;
- GBK码对汉字进行了分区,用高字节(首字节)代表区号,用低字节代表该汉字在本区内的位置,例如【啊】这个汉字,它的GBK码是0XB0A1,高字节是0XB0表示它的区号,低字节0XA1表示他在0XB0区内的第0XA1个;
- 首字节在 81-FE之间(包含FE),尾字节在 40-FE(包含FE)之间。也就是说一共0XFF-0X81=0X7D=126个区,每个区有0XFF-0X40=0XBF=191个字,由于每个区的0X7F这个位置是空的,所以每个区最大可表示的汉字个数是190个,所以总共可以表示126X190=23940个字;印证了上面说了一句【剔除 xx7F一条线】。
知道了GBK是怎么编码的,我们就可以计算偏移位置了。
这里还有一个需要注意的问题,有的中文字库制作软件它不会将每个区0X7F这个没有用到的位置算进去,这个时候每个区的大小就是190,那么这种方式制作的字库计算偏移位置方式就是:
//
// 参数说明:
// gbkl :GBK低字节
// gbkh :GBK高字节
// width:字体宽度
// hight:字体高度
// 返回值:偏移地址
//
uint32_t getoff(uint8_t gbkl, uint8_t gbkh, uint32_t width, uint32_t hight)
{
if(gbkl < 0X7F)
gbkl -= 0X40;
else
gbkl -= 0X41;
return ((gbkh - 0X81) * 190 + gbkl) * (width * hight / 8);
}
而有的字库制作软件他就会将0X7F也算进去,所以每个区的大小就是191,那么计算偏移地址就是:
uint32_t getoff(uint8_t gbkl, uint8_t gbkh, uint32_t width, uint32_t hight)
{
return ((gbkh - 0X81) * 191 + (gbkl - 0X40)) * (width * hight / 8);
}
而对于GB2312来说,计算方式则是:
//
// 参数说明:
// font:汉字
// fontsize: 中文汉字点阵大小的字节数
// 返回值:偏移地址
//
static uint32_t getoff(const uint8_t chinese[2], uint8_t fontsize)
{
uint8_t gbkl, gbkh;
gbkh = chinese[0];
gbkl = chinese[1];
gbkh -= 0xA1;
gbkl -= 0xA1;
return (94 * gbkh + gbkl) * fontsize;
}
我使用的字库制作软件叫FontMaker
,是这位博主的:https://blog.csdn.net/qq446252221/article/details/53188278
他制作的这个字库生成软件就是把0X7F也算进去的。
我个人很推荐他开发的这个软件,因为生成字库非常快!
附上0.96寸OLED字库取模设置:
ends…