热度 20
2013-4-6 23:19
2733 次阅读|
1 个评论
第四十六章 汉字显示实验 汉字显示在很多单片机系统都需要用到,少则几个字,多则整个汉字库的支持,更有甚者还要支持多国字库,那就更麻烦了。本章,我们将向大家介绍,如何用 STM32 控制 LCD 显示汉字。在本章中,我们将使用外部 FLASH 来存储字库,并可以通过 SD 卡更新字库。 STM32 读取存在 FLASH 里面的字库,然后将汉字显示在 LCD 上面。本章分为如下几个部分: 46.1 汉字显示原理简介 46.2 硬件设计 46.3 软件设计 46.4 下载验证 46.1 汉字显示原理简介 常用的汉字内码系统有 GB2312 , GB13000 , GBK , BIG5 (繁体)等几种,其中 GB2312 支持的汉字仅有几千个,很多时候不够用,而 GBK 内码不仅完全兼容 GB2312 ,还支持了繁体字,总汉字数有 2 万多个,完全能满足我们一般应用的要求。 本实例我们将制作一个 GBK 字库,制作好的字库放在 SD 卡里面,然后通过 SD 卡,将字库文件复制到外部 FLASH 芯片 W25Q64 里,这样, W25Q64 就相当于一个汉字字库芯片了。 汉字在液晶上的显示原理与前面显示字符的是一样的。汉字在液晶上的显示其实就是一些点的显示与不显示,这就相当于我们的笔一样,有笔经过的地方就画出来,没经过的地方就不画。所以要显示汉字,我们首先要知道汉字的点阵数据,这些数据可以由专门的软件来生成。只要知道了一个汉字点阵的生成方法,那么我们在程序里面就可以把这个点阵数据解析成一个汉字。 知道显示了一个汉字,就可以推及整个汉字库了。汉字在各种文件里面的存储不是以点阵数据的形式存储的(否则那占用的空间就太大了),而是以内码的形式存储的,就是 GB2312/GBK/BIG5 等这几种的一种,每个汉字对应着一个内码,在知道了内码之后再去字库里面查找这个汉字的点阵数据,然后在液晶上显示出来。这个过程我们是看不到,但是计算机是要去执行的。 单片机要显示汉字也与此类似:汉字内码( GBK/GB2312 ) à 查找点阵库 à 解析 à 显示。 所以只要我们有了整个汉字库的点阵,就可以把电脑上的文本信息在单片机上显示出来了。这里我们要解决的最大问题就是制作一个与汉字内码对的上号的汉字点阵库。而且要方便单片机的查找。每个 GBK 码由 2 个字节组成,第一个字节为 0X81~0XFE ,第二个字节分为两部分,一是 0X40~0X7E ,二是 0X80~0XFE 。其中与 GB2312 相同的区域,字完全相同。 我们把第一个字节代表的意义称为区,那么 GBK 里面总共有 126 个区( 0XFE-0X81+1 ),每个区内有 190 个汉字( 0XFE-0X80+0X7E-0X40+2 ),总共就有 126*190=23940 个汉字。我们的点阵库只要按照这个编码规则从 0X8140 开始,逐一建立,每个区的点阵大小为每个汉字所用的字节数 *190 。这样,我们就可以得到在这个字库里面定位汉字的方法: 当 GBKL0X7F 时: Hp=((GBKH-0x81)*190+GBKL-0X40)*(size*2) ; 当 GBKL0X80 时: Hp=((GBKH-0x81)*190+GBKL-0X41)*(size*2) ; 其中 GBKH 、 GBKL 分别代表 GBK 的第一个字节和第二个字节 ( 也就是高位和低位 ) , size 代表汉字字体的大小(比如 16 字体, 12 字体等), Hp 则为对应汉字点阵数据在字库里面的起始地址 ( 假设是从 0 开始存放 ) 。 这样我们只要得到了汉字的 GBK 码,就可以显示这个汉字了。从而实现汉字在液晶上的显示。 上一章,我们提到要用 cc936.c ,以支持长文件名,但是 cc936.c 文件里面的两个数组太大了( 172KB ),直接刷在单片机里面,太占用 flash 了,所以我们必须把这两个数组存放在外部 flash 。 cc936 里面包含的两个数组 oem2uni 和 uni2oem 存放 unicode 和 gbk 的互相转换对照表,这两个数组很大,这里我们利用 ALIENTEK 提供的一个 C 语言数组转 BIN (二进制)的软件: C2B 转换助手 V1.1.exe ,将这两个数组转为 BIN 文件,我们将这两个数组拷贝出来存放为一个新的文本文件,假设为 UNIGBK.TXT ,然后用 C2B 转换助手打开这个文本文件,如图 46.1.1 所示: 图 46.1.1 C2B 转换助手 然后点击转换,就可以在当前目录下(文本文件所在目录下)得到一个 UNIGBK.bin 的文件。这样就完成将 C 语言数组转换为 .bin 文件,然后只需要将 UNIGBK.bin 保存到外部 FLASH 就实现了该数组的转移。 在 cc936.c 里面,主要是通过 ff_convert 调用这两个数组,实现 UNICODE 和 GBK 的互转,该函数原代码如下: WCHAR ff_convert ( /* Converted code, 0 means conversion error */ WCHAR src, /* Character code to be converted */ UINT dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */ ) { const WCHAR *p; WCHAR c; int i, n, li, hi; if (src 0x80) { /* ASCII */ c = src; } else { if (dir) { /* OEMCP to unicode */ p = oem2uni; hi = sizeof(oem2uni) / 4 - 1; } else { /* Unicode to OEMCP */ p = uni2oem; hi = sizeof(uni2oem) / 4 - 1; } li = 0; for (n = 16; n; n--) { i = li + (hi - li) / 2; if (src == p ) break; if (src p ) li = i; else hi = i; } c = n ? p : 0; } return c; } 此段代码,通过二分法( 16 阶)在数组里面查找 UNICODE (或 GBK )码对应的 GBK (或 UNICODE )码。当我们将数组存放在外部 flash 的时候,将该函数修改为: WCHAR ff_convert ( /* Converted code, 0 means conversion error */ WCHAR src, /* Character code to be converted */ UINT dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */ ) { WCHAR t ; WCHAR c; u32 i, li, hi; u16 n; u32 gbk2uni_offset=0; if (src 0x80)c = src;//ASCII, 直接不用转换 . else { if(dir) gbk2uni_offset=ftinfo.ugbksize/2; //GBK 2 UNICODE else gbk2uni_offset=0; //UNICODE 2 GBK /* Unicode to OEMCP */ hi=ftinfo.ugbksize/2;// 对半开 . hi =hi / 4 - 1; li = 0; for (n = 16; n; n--) { i = li + (hi - li) / 2; SPI_Flash_Read((u8*)t,ftinfo.ugbkaddr+i*4+gbk2uni_offset,4);// 读出 4 个字节 if (src == t ) break; if (src t )li = i; else hi = i; } c = n ? t : 0; } return c; } 代码中的 ftinfo.ugbksize 为我们刚刚生成的 UNIGBK.bin 的大小,而 ftinfo.ugbkaddr 是我们存放 UNIGBK.bin 文件的首地址。这里同样采用的是二分法查找,关于 cc936.c 的修改,我们就介绍到这。 非常抱歉,由于编辑器篇幅所限,剩下内容,请看附件