本文转自:http://www.rationmcu.com/elecjc/356.html
1.导言
和单片机搭配的显示器,有LCD12864、TFT、OLED等。在有些系统中,我们需要显示汉字,可以购买汉字芯片,或者购买带汉字显示的显示器,当然,也可以自己做字库。这里我们介绍一种自己做字库的方法。
2.汉字显示原理
看原理需要知道什么是像素:像素是液晶屏的最小单位,看你的手机屏幕或者电脑屏幕,他们实际上是由一个一个的正方形组成的,如果你的电脑或手机屏幕分辨率太高,不容易看到,需要借助放大镜,分辨率低的话,仔细一看就看见了,都是由一个一个的正方形组成的,这里面的一个正方形,就是1个像素。例如,12864屏幕就是128像素*64像素的屏幕,即横着有12864个正方形,竖着有64个正方形。
上图是12864屏幕的一角,请你仔细看看,是不是一个一个的正方形组成?
比128*64大的分辨率是320*240,还有800*600,1024*768,1280*720等,总之,分辨率越大,人的肉眼就越但不见它是由一个一个小正方形组成的。所以现在的手机屏幕是越来越高,当你用惯一个分辨率极高的手机,再去用你原来分辨率极低的手机,你就会发现原来的那个手机真是很模糊!
讲完了像素的概念,下面开始讲汉字显示的原理:
现在流行的汉字字库有两种,一种是GB2312、一种是GBK。这是两种不同的编码约定。
假如我们想在液晶显示器上显示一个16*16像素大小的汉字“瑞”,怎么办呢?先看一下“瑞”字在液晶显示器上被放大的效果:
上面的这个瑞即是16*16像素大小的字体,你可以数一下上面的像素点。你可以看一下,在上面“亮”的地方用“写点函数”写成和其它点不同的颜色,不就可以了吗!按照这个思想,我们就需要采用一种有效的办法了。从上面图像的右上角开始扫描,每行有16个点,即可以用两个字节来表示,“暗”的地方用0表示,亮的地方用1表示,那么第一行就可以写成十六进制的00 20,以此类推,可以得出第二行,第三行的字节码,如此一来,一个汉字就需要32个字节来表示。搞好了汉字的这些码,在程序中给TFT开个16*16大小的区域,然后开始扫描这些字节,遇到0,写背景色,遇到1,写定义的颜色。汉字显示就是如此了。
3.为什么显示英文一般不用字库芯片?
英文的单词都是由26个字母构成了,加上大小写的区别和一些字符,也不过才95个。假如要显示8*16像素大小的字符,每一个字符需要16个字节的字库空间,95个字符即是95*16=570个字节。即占用570个字节的RAM。对于LPC1114的8K字节RAM来说,搓搓有余了。
4.为什么显示中文需要字库芯片?
显示中文的话,必须需要每一个字的字模,16*16像素大小的中文,每一个中文都要32个字节。GBK收录了中文两万多个,如果要都能显示,需要700多K字节的空间。
所以,我们选择了把这些字库放在外部存储器当中,我们选择了2M的FLASH存储芯片W25Q16做为存储媒介。放个700多K的字库足够了,如果你愿意,同时放两种字体的字库都没问题。
5.如何把GBK字库做成字模?(此部分做字模参考阿莫论坛一帖子,非瑞生原创,特此感谢分享者)
5.1需要准备的文件和软件
点击下载字库制作软件
5.2开始制作
在工具栏处点“打开”按钮,打开gbk_ziku.txt文件,然后根据自己的需要,设置想要的取模方式,然后点工具栏上的“输出”按钮 并等待其完成,完成后会在取模软件所在路径生成了一个temp.txt文件,修改这个文件名,比如现在我是按照“宋体、点阵数为16、字重为4、取模为为16*16、对齐设置为左上、方向设置为横向取模,高位在左”的方式来取的字模,也就是我平时TFT常用的一种字模,改文件名为st16x16.txt 。
为了生成的方便,我们在D盘根目录下新建一个文件夹,名称就叫ziku吧,然后,把ziku.exe 和 st16x16.txt复制一份到这个文件夹里。
打开自己计算机的命令窗口:“开始”->“运行”->“cmd”,如下图所示:
然后用cd命令进入到我们刚刚建的ziku文件夹下,输入命令如下图所示:
输入命令,生成字库文件,如下图所示:
回车,然后程序会在ziku文件夹下生成一个名称固定为ziku.bin的字库文件
为了区分,把这个字库文件修改名称为st16x16.bin 。
到此,字库已经制作完成。
6.如何把做好的GBK字库字模存储到W25Q16中?
硬件:电脑串口—单片机—W25Q16
程序:单片机与电脑采用串口连接,单片机与W25Q16采用SPI方式连接。所以,我们只需要给单片机写一个接收串口数据再把数据通过SPI口传输到W25Q16中的程序即可。
7.单片机如何读取W25Q16中的字模并显示在液晶屏上?
假设我们的GBK字模是从W25Q16地址中的0x100开始存储的,那么,从0x100地址开始,单片机读取32个字节,就是GBK字库的第一个字“丂”的字模,再读32个字节,就是GBK字库第二个字“丄”的字模。(有人会问,你是怎么知道GBK字库第几个字是什么的?答:你把上文中5.1节提到的文件下载后,打开GBK字库,就看见了。)根据上文中第2步讲的原理,字模中,遇到0就给LCD的1个像素画一个颜色,遇到1就给LCD的下一个像素画不同的颜色,把32个字节都捋一遍,一个汉字就显示在LCD上了。
如果我们想要显示汉字“瑞”,我们该从W25Q16中的哪个字节开始提取它的字模呢?这时候,就需要知道字符存储编码的原理了。
字符编码原理:英文字母带各种符号等共有95个,都有自己的ascii码,一个字母或者符号就是一个ascii码。汉字以及汉字的符号是需要2个字节来存储,在keil等主流编译器中,一般都是以GBK编码方式存储。
GBK编码:每个GBK码由2个字节组成,第一个字节为0X81~0XFE,第二个字节分为两部分,一是0X40~0X7E,二是0X80~0XFE。例如汉字“瑞”的GBK编码为C8 F0,第一字节C8,位于0X81~0XFE之间,第二字节F0,位于0X40~0XFE之间。第一个字节代表的意义称为区,那么GBK里面总共有126个区(0XFE-0X81+1=126);第二个字节代表的意义就是每个区内有多少个汉字,算了一下,一共有190个(0XFE-0X80+0X7E-0X40+2=190)。那么,GBK一共存储了126X190=23940个汉字。
我们仔细看GBK编码第二个字节两部分,0X40~0X7E 0X80~0XFE,也就是说它是从0X40~到0XFF,中间的0x7F和最后的0xFF没有用到。但是为了能够线性查找,我们暂且认为这两个字节也存在,就是我们强制把每个区190个汉字当做每个区192个汉字,不过0X7F和0XFF上没有汉字。
定义GBKH代表第一个字节,GBKL代表第二个字节,字库的偏移量offset,放入W25Q16的字库从0x100地址开始存储,那么获取某个汉字的字模的函数就可以写成:
void Get_GBK_DZK(uint8_t *code, uint8_t *dz_data)
{
uint8_t GBKH,GBKL;
uint32_t offset;
GBKH=*code;
GBKL=*(code+1);
if(GBKH>0XFE||GBKH<0X81)return;
GBKH-=0x81;
GBKL-=0x40;
offset=((uint32_t)192*GBKH+GBKL)*32;
W25Q16_Read(dz_data,offset+0x100,32);
return;
}
函数中,参数*code带入汉字的两字节编码,*dz_data是一个32个字节的数据,带入函数中,就可以得到任何一个汉字的字模了。例如:
uint8_t buf[32];
Get_GBK_DZK("瑞", buf);