字符编码
由于计算机只能识别0和1,文字也只能以0和1的形式在计算机里存储,所以我们需要对文字进行编码才能让计算机处理,编码的过程就是规定特定的01数字字符串来表示特定的文字,最简单的字符编码就是ASCII编码。

ASCII编码
学习C语言的时候,我们知道在程序设计中使用的ASCII编码表约定了一些控制字符、英文及数字。他们在存储器中,本质也是二进制数,只是我们约定这些二进制数可以表示某些特殊意义,如以ASCII编码解释数字“0x41”时,它表示的是英文字符“A”。ASCII编码表可以分为两部分,第一部分是控制字符或通讯专用字符,它们的数字编码从0-31,它们并没有特定的图形显示,但会根据不同的应用程序,而对文本显示有不同的影响.ASCII表的第二部分包括空格、阿拉伯数字、标点符号、大小写英文字符以及“DEL”,这部分的数字编码从32-127,除了最后一个DEL符号外都能以图形的方式显示,具体的ASCII表这里不再赘述,读者可以网上查找。

后来,计算机推广之后,还加入了各种形状和符号,一直编号到255,从128-255的字符被称为ASCII的扩展字符集,至此,基本存储单位Byte能表示的编号已经全部用完了。

中文编码
由于汉字非常多,常用字就有6000多个,如果像ASCII编码表那样只使用1个字节最多只能表示256个汉字、所以我们使用2个字节来编码。

GB2312编码
我们首先定义的就是GB2312编码,它把 ASCII码表127之后的扩展字符集直接取消掉,并规定小于127的编码按原来的ASCII标准解释字符,当2个大于127的字符联结在一起时,就表示1个汉字。
第一个字节使用(0xA1-0xFE)编码,第2个字节使用(0xA1-0xFE)编码,这样的编码组合起来可以表示了7000多个符号,其中包括汉字6763个汉字。在这些编码里,我们还把数学符号、罗马字母、日文片假名等都编进表中,就连原来在ASCII表中原有的数字、标点以及字母也重新编了2个字节长的编码,这就是平时在输入法可以切换“全角”和“半角”符号的原因,半角就是原来的1个字节的ASCII编码。

下面的表格说明GB2312兼容ASCII码的原理,说明了GB2312是如何兼容ASCII的,当我们设定系统使用GB2312标准的时候,它遇到一个字符串时,会按字节检测值得大小,若遇到连续两个字节得数值都大于127时,就把这两个连续得字节合在一起,用GB2312解码,若遇到得数值小于127,就直接用ASCII解码。

区位码
在GB2312编码的实际使用中,有时会用到区位码的概念,见图GB2312的部分区位码。GB2312编码对所收录字符进行了“分区”处理,共94个区,每区含有94个位,共8836个码位。而区位码实际是GB2312编码的内部形式,它规定对收录的每个字符采用两个字节表示,第一个字节为“高字节”,对应94个区;第二个字节为低字节,对应94个位。所以它的区位码范围就是:0x0101-0x9494。为兼容ASCII码,区号和位号分别加上0xA0偏移就得到GB2312编码。在区位码上加上0xA0,可求得GB2312编码的范围:0xA1A1-0XFEFE,其中汉字的编码范围是0xB0A1-0XF7FE,第一字节0xB0-0xF7(对应位号16-87),第二字节0xA1-0XFE(对应位号01-94)。

例如,“啊”字是 GB2312 编码中的第一个汉字,它位于 16 区的 01 位,所以它的区位码就是 1601 , 加上 0xA0 偏移,其 GB2312 编码为 0xB0A1 。其中区位码为 0101 的码位表示的是“空格”符。

GBK编码
据统计, GB2312 编码中表示的 6763 个汉字已经覆盖中国大陆 99.75% 的使用率,单看这个数字
已经很令人满意了,但是我们不能因为那些文字不常用就不让它进入信息时代,而且生僻字在人
名、文言文中的出现频率是非常高的。为此我们在 GB2312 标准的基础上又增加了 14240 个新汉
字 ( 包括所有后面介绍的 Big5 中的所有汉字 ) 和符号,这个方案被称为 GBK 标准。增加这么多字
符,按照 GB2312 原来的格式来编码, 2 个字节已经没有足够的编码,我们聪明的程序员修改了
一下格式,不再要求第 2 个字节的编码值必须大于 127 ,只要第 1 个字节大于 127 就表示这是一
个汉字的开始,这样就做到了兼容 ASCII 和 GB2312 标准。
表格 GBK 兼容 ASCII 和 GB2312 的原理 说明了 GBK 是如何兼容 ASCII 和 GB2312 标准的,当我 们设定系统使用 GBK 标准的时候,它按顺序遍历字符串,按字节检测字符值的大小,若遇到一
个字符的值大于 127 时,就再读取它后面的一个字符,把这两个字符值合在一起,用 GBK 解码,
解码完后,再读取第 3 个字符,重新开始以上过程,若该字符值小于 127 ,则直接用 ASCII 解码。

后面还有GB18030编码,使用4字节编码,Big5繁体中文编码,由于篇幅原因,可以有兴趣的网上了解。

Unicode字符集和编码
由于各个国家或地区都根据使用自己的文字系统制定标准,同一个编码在不同的标准里表示不
一样的字符,各个标准互不兼容,而又没有一个标准能够囊括所有的字符,即无法用一个标准表
达所有字符。国际标准化组织 (ISO) 为解决这一问题,它舍弃了地区性的方案,重新给全球上所
有文化使用的字母和符号进行编号,对每个字符指定一个唯一的编号 (ASCII 中原有的字符编号
不变 ) ,这些字符的号码从 0x000000 到 0x10FFFF ,该编号集被称为 Universal Multiple-Octet Coded CharacterSet,简称 UCS ,也被称为 Unicode 。最新版的 Unicode 标准还包含了表情符号 ( 聊天软件 中的部分 emoji 表情 ) ,可访问 Unicode 官网了解: http://www.unicode.org
Unicode 字符集只是对字符进行编号,但具体怎么对每个字符进行编码, Unicode 并没指定,因此
也衍生出了如下几种 unicode 编码方案 (Unicode Transformation Format)。
UTF-32
对Unicode字符集编码,最自然的就是UTF-32方式。编码时,它直接对Unicode字符集里的每个字符都使用4字节来表示,转换方式很简单,直接将字符对应的编号数字转换为4字节的二进制数。如表格所示,由于UTF-32把每个字符都用4字节来存储,因此UTF-32不兼容ASCII码,也就是说ASCII编码使用的文件用UTF-32打开会乱码。

对UTF-32数据进行解码的时候,以4字节为单位进行解析即可,根据编码可直接找到Unicode字符集中对应编号的字符。

UTF-32的优点是编码简单,解码方便,读取编码的时候每次都直接读4字节,不需要加其他的判断。它的缺点是浪费存储空间,大量常用的字符的编号只需要两个字节就能表示。其次在存储的时候需要指定字节顺序,是高位字节存储在前(大端格式),还是低位字节存储在前(小端格式)。

UTF-16
针对UTF-32的缺点,人们改进了UTF-16的编码方式,如表格UTF-16编码示例,它采用2字节或4字节的变长编码方式(UTF-32为定长编码方式)。对Unicode字符编号在0-65535的统一使用2字节来表示,将每个字符的编号转换为2字节的二进制数,即从0x0000到0xFFFF.而由

于 Unicode 字符集在 0xD800-0xDBFF 这个区间是没有表示任何字符的,所以 UTF-16 就利用这段 空间,对 Unicode 中编号超出 0xFFFF 的字符,利用它们的编号做某种运算与该空间建立映射关 系,从而利用该空间表示 4 字节扩展,感兴趣的读者可查阅相关资料了解具体的映射过程。

UTF-8
UTF-8 也是一种变长的编码方式,它的编码有 1 、 2 、 3 、 4 字节长度的方式,每个 Unicode 字符根
据自己的编号范围去进行对应的编码,见表格 UTF-8 编码原理 _x 的位置用于填充 Unicode 编号
它的编码符合以下规律:
• 对于 UTF-8 单字节的编码,该字节的第 1 位设为 0( 从左边数起第 1 位,即最高位 ) ,剩余
的位用来写入字符的 Unicode 编号。即对于 Unicode 编号从 0x0000 0000 - 0x0000 007F 的字
符, UTF-8 编码只需要 1 个字节,因为这个范围 Unicode 编号的字符与 ASCII 码完全相同,
所以 UTF-8 兼容了 ASCII 码表。
• 对于 UTF-8 使用 N 个字节的编码 (N>1) ,第一个字节的前 N 位设为 1 ,第 N+1 位设为 0 ,
后面字节的前两位都设为 10 ,这 N 个字节的其余空位填充该字符的 Unicode 编号,高位用
0 补足。

注:实际上 utf-8 编码长度最大为四个字节,所以最多只能表示 Unicode 编码值的二进制数为 21
位的 Unicode 字符。但是已经能表示所有的 Unicode 字符,因为 Unicode 的最大码位 0x10FFFF 也 只有 21 位。
UTF-8 解码的时候以字节为单位去看,如果第一个字节的 bit 位以 0 开头,那就是 ASCII 字符,以
单字节进行解析。如果第一个字节的数据位以“ 110 ”开头,就按双字节进行解析, 3 、 4 字节的
解析方法类似。
字模
如果有字符编码、计算机还不知道如何表达该字符,因为字符实际上是一个个独特的图形,计算机必须把字符编码转化成对应的字符图形人类才能正常识别,因此我们要给计算机提供字符的图形数据,这些数据就是字模,多个字模数据组成的文件就是字库。

字模的构成
已知字模是图形数据,而图形在计算机中是由一个个像素点组成的,所以字模实质是一个个像素
点数据。为方便处理,我们把字模定义成方块形的像素点阵,且每个像素点只有 0 和 1 这两种状
态 ( 可以理解为单色图像数据 ) 。见图 字模 ,这是两个宽、高为 16x16 的像素点阵组成的两个汉字
图形,其中的黑色像素点即为文字的笔迹。计算机要表示这样的图形,只需使用 16x16 个二进制
数据位,每个数据位记录一个像素点的状态,把黑色像素点以“
1 ”表示,无色像素点以“0”表 示即可。这样的一个汉字图形,使用 16x16/8=32 个字节来就可以记录下来。

字模显示原理
如果使用 LCD 的画点函数,按位来扫描这些字模数据,把为 1 的位以黑色来显示 ( 也可以使用其
它颜色 ) ,为 0 的数据位以白色来显示,即可把整个点阵还原出来,显示在液晶屏上。

如何制作字模
以上只是某几个字符的字模,为方便使用,我们需要制作所有常用字符的字模,如程序只需要英
文显示,那就需要制作包含 ASCII 码表 ASCII 码中的字符和数字 中所有字符的字模,如程序只需
要使用一些常用汉字,我们可以选择制作 GB2312 编码里所有字符的字模,而且希望字模数据与
字符编码有固定的映射关系,以便在程序中使用字符编码作为索引,查找字模。在网上搜索可找
到一些制作字模的软件工具,可满足这些需求。在我们提供的《液晶显示中英文》的工程目录下
提供了一个取模软件“ PCtoLCD ”,这里以它为例讲解如何制作字模,其它字模软件也是类似的。
配置字模格式
1、生成GB2312字模
2、字模寻址公式
使用字模软件制作的字模数据一般会按照编码格式排列。如我们利用以上软件生成的字模文件
《 GB2312_H2424.FON 》中的数据,是根据 GB2312 的区位码表的顺序存储的,它存储了区位码
为 0101-9494 的字符,每个字模的大小为 16x16/8=32 字节。其中第一个字符“空格”的区位码为
0101 ,它是首个字符,所以文件的前 32 字节存储的是它的字模数据;同理, 32-64 字节存储的则
是 0102 字符“、”的字模数据。所以我们可以导出任意字符的寻址公式
Addr = (((Code H -0xA0-1)_94) +(Code L -0xA0-1))_16*16/8
其中 Code H 和 Code L 分别是 GB2312 编码的第一字节和第二字节; 94 是指一个区中有 94 个位 ( 即 94 个字符 ) 。公式的实质是根据字符的 GB2312 编码,求出区位码,然后区位码乘以每个字符占 据的字节数,求出地址偏移。
存储字模文件
上面生成的《 GB2312_H1616.FON 》文件的大小为 256KB ,比很多 STM32 芯片内部的所有 FLASH
空间都大,如果我们还是在程序中直接以 C 语言数组的方式存储字模数据, STM32 芯片的程序空
间会非常紧张,一般的做法是把字模数据存储到外部存储器,如 SD 卡或 SPI-FLASH 芯片,当需
要显示某个字符时,控制器根据字符的编码算好字模的存储地址,再从存储器中读取,而 FLASH
芯片在生产前就固化好字模内容,然后直接把 FLASH 芯片贴到电路板上,作为整个系统的一部

各种模式的液晶显示字符实验
显示ASCII编码的字符
编程要点

(1)获取字模数据

(2)根据字模格式,编写液晶显示函数

(3)编写测试程序,控制液晶

代码分析
ASCII字模数据

要显示的字符首先要有字库数据,在工程中fonts,c文件我们定义了一系列大小为24x32、16x24、16x16的ASCII码表的字模数据:如下

/*
*常用ASCII表,偏移量32,大小为8(宽度)*16(高度)
*/
const uint8_t ASCII8x16_Table[] = {       //@consolas字体,阴码点阵格式,逐行顺向取摸
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x08,0x00,0x08,0x18,0x00,0x00,0x00,
0x00,0x00,0x00,0x34,0x24,0x24,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x16,0x24,0x7f,0x24,0x24,0x24,0x7e,0x24,0x24,0x00,0x00,0x00,
0x00,0x00,0x00,0x08,0x3e,0x68,0x48,0x68,0x1c,0x16,0x12,0x12,0x7c,0x10,0x10,0x00,
0x00,0x00,0x00,0x61,0xd2,0x96,0x74,0x08,0x10,0x16,0x29,0x49,0xc6,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x3c,0x64,0x64,0x38,0x72,0x4a,0xce,0x46,0x7f,0x00,0x00,0x00,
0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x04,0x08,0x18,0x10,0x30,0x30,0x30,0x30,0x10,0x10,0x18,0x0c,0x04,
0x00,0x00,0x00,0x20,0x10,0x08,0x08,0x0c,0x04,0x04,0x04,0x0c,0x08,0x18,0x10,0x20,
0x00,0x00,0x00,0x08,0x0a,0x34,0x1c,0x6a,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x7f,0x18,0x18,0x18,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x08,0x30,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
0x00,0x00,0x00,0x02,0x06,0x04,0x0c,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x00,0x00,
0x00,0x00,0x00,0x00,0x3c,0x66,0x42,0x47,0x5b,0x73,0x42,0x66,0x3c,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x18,0x78,0x48,0x08,0x08,0x08,0x08,0x08,0x7e,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x3c,0x46,0x06,0x06,0x04,0x08,0x10,0x20,0x7e,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7c,0x06,0x06,0x04,0x3c,0x02,0x02,0x06,0x7c,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x0c,0x1c,0x14,0x24,0x64,0x44,0xff,0x04,0x04,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7e,0x60,0x60,0x60,0x7e,0x02,0x02,0x06,0x7c,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x1e,0x30,0x60,0x48,0x76,0x42,0x42,0x62,0x3c,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7e,0x02,0x06,0x04,0x0c,0x08,0x18,0x10,0x30,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x3c,0x62,0x42,0x36,0x1c,0x66,0x42,0x42,0x3c,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x3c,0x66,0x42,0x42,0x66,0x1a,0x02,0x04,0x78,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x08,0x30,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x18,0x30,0x60,0x10,0x0c,0x06,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x18,0x04,0x06,0x0c,0x10,0x20,0x00,0x00,0x00,
0x00,0x00,0x00,0x30,0x1c,0x06,0x06,0x06,0x18,0x10,0x00,0x10,0x10,0x00,0x00,0x00,
0x00,0x00,0x00,0x1c,0x22,0x41,0x41,0xdd,0xb5,0xa5,0xa5,0xaf,0x94,0xc0,0x40,0x3c,
0x00,0x00,0x00,0x00,0x18,0x1c,0x34,0x24,0x26,0x62,0x7e,0x43,0xc1,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7c,0x46,0x42,0x46,0x7c,0x42,0x42,0x42,0x7c,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x1e,0x20,0x40,0x40,0x40,0x40,0x40,0x60,0x3e,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7c,0x46,0x42,0x43,0x43,0x43,0x42,0x46,0x78,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7e,0x60,0x60,0x60,0x7e,0x60,0x60,0x60,0x7e,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7e,0x60,0x60,0x60,0x7e,0x60,0x60,0x60,0x60,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x1e,0x60,0x40,0x40,0xce,0x42,0x42,0x62,0x3e,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x42,0x42,0x42,0x42,0x7e,0x42,0x42,0x42,0x42,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7e,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7e,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7c,0x04,0x04,0x04,0x04,0x04,0x04,0x44,0x78,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x42,0x44,0x48,0x50,0x70,0x58,0x4c,0x44,0x42,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3e,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x62,0x66,0x67,0x5f,0x5b,0x5b,0xc1,0xc1,0xc1,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x62,0x62,0x72,0x52,0x5a,0x4a,0x4e,0x46,0x46,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x3c,0x62,0x43,0xc3,0xc3,0xc3,0x43,0x62,0x3c,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7c,0x46,0x42,0x42,0x46,0x78,0x40,0x40,0x40,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x3c,0x62,0x43,0xc3,0xc3,0xc3,0x43,0x62,0x3c,0x18,0x0f,0x00,
0x00,0x00,0x00,0x00,0x7c,0x66,0x62,0x66,0x7c,0x6c,0x64,0x66,0x62,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x3e,0x60,0x40,0x60,0x1c,0x06,0x02,0x02,0x7c,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x62,0x3c,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xc1,0x43,0x42,0x62,0x26,0x24,0x34,0x1c,0x18,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xc1,0xc1,0x41,0x49,0x5b,0x5b,0x76,0x66,0x66,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x43,0x66,0x34,0x18,0x18,0x1c,0x24,0x66,0xc3,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xc1,0x42,0x66,0x34,0x1c,0x18,0x18,0x18,0x18,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x7e,0x02,0x04,0x0c,0x18,0x10,0x20,0x60,0x7e,0x00,0x00,0x00,
0x00,0x00,0x00,0x1c,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x1c,
0x00,0x00,0x00,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x0c,0x04,0x06,0x02,0x00,0x00,
0x00,0x00,0x00,0x3c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3c,
0x00,0x00,0x00,0x00,0x18,0x1c,0x24,0x62,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,
0x00,0x00,0x00,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x3c,0x06,0x02,0x3e,0x42,0x46,0x7a,0x00,0x00,0x00,
0x00,0x00,0x00,0x40,0x40,0x40,0x5c,0x62,0x42,0x42,0x42,0x42,0x7c,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x1e,0x20,0x60,0x40,0x60,0x20,0x3e,0x00,0x00,0x00,
0x00,0x00,0x00,0x02,0x02,0x02,0x3e,0x62,0x42,0x42,0x42,0x66,0x3a,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x3c,0x62,0x42,0x7e,0x40,0x60,0x3e,0x00,0x00,0x00,
0x00,0x00,0x00,0x0f,0x18,0x10,0x10,0x7e,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x66,0x42,0x66,0x58,0x40,0x3e,0x43,0x42,0x3c,
0x00,0x00,0x00,0x40,0x40,0x40,0x5c,0x62,0x42,0x42,0x42,0x42,0x42,0x00,0x00,0x00,
0x00,0x00,0x00,0x18,0x18,0x00,0x78,0x08,0x08,0x08,0x08,0x08,0x7e,0x00,0x00,0x00,
0x00,0x00,0x00,0x04,0x0c,0x00,0x7c,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x0c,0x78,
0x00,0x00,0x00,0x60,0x60,0x60,0x62,0x6c,0x78,0x70,0x68,0x64,0x62,0x00,0x00,0x00,
0x00,0x00,0x00,0x78,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x7e,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x76,0x4b,0x4b,0x4b,0x4b,0x4b,0x4b,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x5c,0x62,0x42,0x42,0x42,0x42,0x42,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x3c,0x62,0x42,0x43,0x42,0x62,0x3c,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x5c,0x62,0x42,0x42,0x42,0x42,0x7c,0x40,0x40,0x40,
0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x62,0x42,0x42,0x42,0x66,0x3a,0x02,0x02,0x02,
0x00,0x00,0x00,0x00,0x00,0x00,0x6e,0x72,0x63,0x60,0x60,0x60,0x60,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x20,0x20,0x3c,0x06,0x02,0x7c,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x10,0x10,0xfe,0x10,0x10,0x10,0x10,0x10,0x1e,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x66,0x3a,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x43,0x42,0x66,0x24,0x34,0x18,0x18,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0xc1,0xc1,0x5b,0x5a,0x5e,0x66,0x66,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x62,0x26,0x1c,0x18,0x1c,0x26,0x62,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x43,0x42,0x66,0x24,0x34,0x1c,0x18,0x18,0x30,0xe0,
0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x06,0x0c,0x18,0x10,0x20,0x7e,0x00,0x00,0x00,
0x00,0x00,0x00,0x0e,0x18,0x10,0x10,0x10,0x30,0x70,0x10,0x10,0x10,0x10,0x18,0x0e,
0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
0x00,0x00,0x00,0x30,0x18,0x08,0x08,0x08,0x0c,0x0e,0x08,0x08,0x08,0x08,0x18,0x30,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x71,0x4b,0x06,0x00,0x00,0x00,0x00,0x00,
};

管理英文字模的结构体

为了方便使用各种不同的字体,工程中定义了一个sFONT的结构体类型,并利用它定义了存储不同字体信息的结构体变量。

typedef struct _tFont
{
    const uint8_t *table;
    uint16_t Width;
    uint16_t Height;

}sFONT;

sFONT ASCII_8x16 = {
                                            ASCII8x16_Table,
                                            8,
                                            16,
                                        };

sFONT ASCII_16x16 = {
                                            ASCII16x24_Table,
                                            16,
                                            24,
                                        };

sFONT ASCII_24x32 = {
                                            ASCII24x32_Table,
                                            24,
                                            32,
                    };

这个结构体类型定义了三个变量,第一个是指向字模数据的指针,即前面提到的 C 语言数
组,每二、三个变量存储了该字模单个字符的像素宽度和高度。利用这个类型定义了 Font8x16 、
Font16x24 之类的变量,方便显示时寻址。
ASCII字符和中文显示函数

/**************************以下是ASCII英文字符显示相关的函数**********************************/
/*
*@brief 显示字符
*@param usX:在特定扫描方向字符显示的X起始位置
                usY:在特定扫描方向字符显示的Y起始位置
                Chara:要显示的字符数据
*@revtal:None            
*/
void ILI9341_LCD_DisChar_EN(uint16_t usX,uint16_t usY,const char Chara)
{
    uint8_t byteCount,bitCount,fontLength;
    uint16_t usRelativePosition;
    uint8_t *Pfont;

    /*对ASCII表的偏移(注意:字模表不包含ASCII表的前32个非图形符号)*/
    usRelativePosition    =    Chara - ' ';

    /*一个字符的字模数据所占的字节数*/
    fontLength    =    ((*LCD_Currentfonts).Width*(*LCD_Currentfonts).Height)/8;

    /*字模的首地址,ASCII表偏移乘以每个字模的字节数,求出字模的偏移位置*/
    Pfont    =    (uint8_t *)&LCD_Currentfonts->table[usRelativePosition*fontLength];

    /*设置显示窗口*/
    ILI9341_Open_Window(usX,usY,LCD_Currentfonts->Width,LCD_Currentfonts->Height);

    /*按字节读取字模数据显示*/
    ILI9341_Write_Cmd(CMD_SetPixel);
    /*由于前面设置了显示窗口,显示数据会自动换行*/
    for(byteCount=0;byteCount<fontLength;byteCount++)
    {    /*一位一位处理要显示的颜色*/
        for(bitCount=0;bitCount<8;bitCount++)
        {
            if((*Pfont)&(0x80>>bitCount))
            {
                    ILI9341_Write_Data(CurrentTextColor);
            }
            else
            {
                    ILI9341_Write_Data(CurrentBackColor);
            }
        }
        Pfont++;
    }
}

/*
*@brief  显示一串英文字符串
*@param  usX:要显示的英文字符串的起始X位置
                 usY:要显示的英文字符串的起始Y位置
                 Str:要显示的英文字符串
*@retval:None
*/
void ILI9341_LCD_DisStr_EN(uint16_t usX,uint16_t usY, char *Str)
{
    /*先要根据字符串的字符找到对应的字模数据*/
    while(*Str != '\0')
    {
        if((usX + LCD_Currentfonts->Width)>LCD_X_LENGTH)
        {
            usX    =    ILI9341_DispWindow_X_Star;
            usY    +=    LCD_Currentfonts->Height ;
        }
        if((usY + LCD_Currentfonts->Height)>LCD_Y_LENGTH)
        {
            usX    =    ILI9341_DispWindow_X_Star;
            usY    =    ILI9341_DispWindow_Y_Star;

        }
        ILI9341_LCD_DisChar_EN(usX,usY,*Str);

        Str++;

        usX    += LCD_Currentfonts->Width;
    }
}

/*
*@brief 在显示屏上显示英文字符(沿着Y轴方向,前面默认是沿着X轴方向)
*@param    usX:在特定的扫描方向上要显示英文字符串的X位置
              usY:在特定的扫描方向下要显示英文字符串的Y位置

*@retval:None                
*/        
void ILI9341_LCD_DisStr_EN_Ydir(uint16_t usX,uint16_t usY,char *Str)
{
    while(*Str    != '\0')
    {
        if((usY    + LCD_Currentfonts->Height)>LCD_Y_LENGTH)
        {
            usX    += LCD_Currentfonts->Width;
            usY    =    ILI9341_DispWindow_Y_Star;

        }
        if((usX    + LCD_Currentfonts->Width)>LCD_X_LENGTH)
        {
            usX    =    ILI9341_DispWindow_X_Star;
            usY    =    ILI9341_DispWindow_Y_Star;
        }

        ILI9341_LCD_DisChar_EN(usX,usY,*Str);
        Str++;

        usY    += LCD_Currentfonts->Height;

    }
}    
/*
*@brief 在ILI9341显示器上显示英文字符串
*@param line:在特定扫描方向上英文字符串的起始Y坐标
*@note:本参数可以使用宏定义LINE(0)、LINE(1)等指定方式的文字坐标
                其中宏LINE会根据选择的当前字体来计算Y坐标值
                Str:要显示的英文字符串的首地址
*@retval:None
*/    
void ILI9341_LCD_DisStrLine_EN(uint16_t line,char *Str)
{
    uint16_t usX    =    0;

    while(*Str    != '\0')
    {
        if((usX    + LCD_Currentfonts->Width)>LCD_X_LENGTH)
        {
            usX    =    ILI9341_DispWindow_X_Star;
            line    += LCD_Currentfonts->Height;
        }
        if((line    + LCD_Currentfonts->Height)>LCD_Y_LENGTH)
        {
            usX    =    ILI9341_DispWindow_X_Star;
            line    =    ILI9341_DispWindow_Y_Star;
        }
        ILI9341_LCD_DisChar_EN(usX,line,*Str);
        Str++;

        usX    += LCD_Currentfonts->Width;

    }

}

/*
*@brief 设置当前要显示的字体大小
*@param    fonts:指定要选择的字体
*@note:参数可以为以下选择之一:
*@arg:ASCII_8x16
*@arg:ASCII_8x16
*@arg:ASCII_8x16
*@retval:None
*/
void LCD_SetFont(sFONT *fonts)
{
    LCD_Currentfonts    =    fonts;
}

/*
*brief    读取当前字体的类型
*param    None
*retval:返回当前字体的类型
*/
sFONT *LCD_GetFont(void)
{
    return LCD_Currentfonts;

}


/**************************以下是有关GB2312中文字符显示相关的函数********************************************/

/*
*@brief    显示GB2312中文字符
*@param    usX:在当前扫描方式下的要显示的中文字符的X起始位置
                usY:在当前扫描方式下的要显示的中文字符的Y起始位置
*@retval:None            
*/
void ILI9341_LCD_DisChar_CH ( uint16_t usX, uint16_t usY, uint16_t usChar )
{
    uint8_t rowCount, bitCount;
    uint8_t ucBuffer [ CH_WIDTH*CH_HEIGHT/8 ];    
  uint16_t usTemp;     

    //设置显示窗口
    ILI9341_Open_Window ( usX, usY, CH_WIDTH, CH_HEIGHT );

    ILI9341_Write_Cmd ( CMD_SetPixel );

    //取字模数据  
  GetGBKCode ( ucBuffer, usChar );    

    for ( rowCount = 0; rowCount < CH_HEIGHT; rowCount++ )
    {
    /* 取出两个字节的数据,在lcd上即是一个汉字的一行 */
        usTemp = ucBuffer [ rowCount * 2 ];
        usTemp = ( usTemp << 8 );
        usTemp |= ucBuffer [ rowCount * 2 + 1 ];

        for ( bitCount = 0; bitCount < CH_WIDTH; bitCount ++ )
        {            
            if ( usTemp & ( 0x8000 >> bitCount ) )  //高位在前 
              ILI9341_Write_Data ( CurrentTextColor );                
            else
                ILI9341_Write_Data ( CurrentBackColor );            
        }        
    }

}
/*
*@brief 在ILI9341显示器上显示中文字符串
*@param usX:在当前扫描方式下的显示中文字符串的X起始位置
              usY:在当前扫描方式下的显示中文字符串的Y起始位置
*@retval:None            
*/
void ILI9341_LCD_DisStr_CH(uint16_t usX,uint16_t usY,char *uStr)
{
    uint16_t usCh;
    while(*uStr!='\0')
    {
        if((usX + CH_WIDTH)>LCD_X_LENGTH)
        {
            usX    =    ILI9341_DispWindow_X_Star;
            usY    += CH_HEIGHT;
        }
        if((usY + CH_HEIGHT)>LCD_Y_LENGTH)
        {
            usX    =    ILI9341_DispWindow_X_Star;
            usY    =    ILI9341_DispWindow_Y_Star;
        }
        usCh    =    *(uint16_t *)uStr;
        usCh    =    (usCh<<8) + (usCh>>8);

        ILI9341_LCD_DisChar_CH(usX,usY,usCh);
        uStr+=2;

        usX    += CH_WIDTH;

    }

}

/**
 * @brief  在 ILI9341 显示器上显示中英文字符串
 * @param  usX :在特定扫描方向下字符的起始X坐标
 * @param  usY :在特定扫描方向下字符的起始Y坐标
 * @param  pStr :要显示的字符串的首地址
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_DispString_EN_CH (     uint16_t usX , uint16_t usY, char * pStr )
{
    uint16_t usCh;

    while( * pStr != '\0' )
    {
        if ( * pStr < 127 )                   //英文字符
        {
            if ( ( usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width ) > LCD_X_LENGTH )
            {
                usX = ILI9341_DispWindow_X_Star;
                usY += LCD_Currentfonts->Height;
            }

            if ( ( usY - ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height ) > LCD_Y_LENGTH )
            {
                usX = ILI9341_DispWindow_X_Star;
                usY = ILI9341_DispWindow_Y_Star;
            }            

          ILI9341_LCD_DisChar_EN ( usX, usY, * pStr );

            usX +=  LCD_Currentfonts->Width;

          pStr ++;

        }

        else                                //汉字字符
        {
            if ( ( usX - ILI9341_DispWindow_X_Star + CH_WIDTH ) > LCD_X_LENGTH )
            {
                usX = ILI9341_DispWindow_X_Star;
                usY += CH_HEIGHT;
            }

            if ( ( usY - ILI9341_DispWindow_Y_Star + CH_HEIGHT ) > LCD_Y_LENGTH )
            {
                usX = ILI9341_DispWindow_X_Star;
                usY = ILI9341_DispWindow_Y_Star;
            }    

            usCh = * ( uint16_t * ) pStr;    

            usCh = ( usCh << 8 ) + ( usCh >> 8 );        
            ILI9341_LCD_DisChar_CH ( usX, usY, usCh );

            usX += CH_WIDTH;

            pStr += 2;           //一个汉字两个字节 

    }

  }    
} 

/*
*@brief 
*@param 
*@retval
*/
void ILI9341_LCD_DisStr_CH_EN_Line(uint16_t line,char *pStr)
{    
    uint16_t usCh;
    uint16_t usX=0;
    while(*pStr!='\0')
    {
        if(*pStr<127)
        {
            if((usX+LCD_Currentfonts->Width)>LCD_X_LENGTH)
            {
                usX    =    ILI9341_DispWindow_X_Star;
                line    += LCD_Currentfonts->Height;

            }
            if((line+LCD_Currentfonts->Height)>LCD_Y_LENGTH)
            {
                usX    =    ILI9341_DispWindow_X_Star;
                line    =    ILI9341_DispWindow_Y_Star;
            }
            ILI9341_LCD_DisChar_EN(usX,line,*pStr);
            usX    +=    LCD_Currentfonts->Width;
            pStr++;
        }
        else
        {
            if((usX+CH_WIDTH)>LCD_X_LENGTH)
            {
                usX    =    ILI9341_DispWindow_X_Star;
                line    += CH_HEIGHT;

            }
            if((line+CH_HEIGHT)>LCD_Y_LENGTH)
            {
                usX    =    ILI9341_DispWindow_X_Star;
                line    =    ILI9341_DispWindow_Y_Star;
            }

            usCh    =    *(uint16_t *)pStr;

            usCh    =    (usCh<<8) + (usCh>>8);

            ILI9341_LCD_DisChar_CH(usX,line,usCh);

            usX    += CH_WIDTH;

            pStr    +=    2;

        }
    }

}

/**
 * @brief  在 ILI9341 显示器上显示中英文字符串(沿Y轴方向)
 * @param  usX :在特定扫描方向下字符的起始X坐标
 * @param  usY :在特定扫描方向下字符的起始Y坐标
 * @param  pStr :要显示的中英文字符串的首地址
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
 */
void ILI9341_DispString_EN_CH_YDir (  uint16_t usX,uint16_t usY , char * pStr )
{
    uint16_t usCh;

    while( * pStr != '\0' )
    {            
            //统一使用汉字的宽高来计算换行
            if ( ( usY - ILI9341_DispWindow_Y_Star + CH_HEIGHT ) >LCD_Y_LENGTH  )
            {
                usY = ILI9341_DispWindow_Y_Star;
                usX += CH_WIDTH;
            }            
            if ( ( usX - ILI9341_DispWindow_X_Star + CH_WIDTH ) >  LCD_X_LENGTH)
            {
                usX = ILI9341_DispWindow_X_Star;
                usY = ILI9341_DispWindow_Y_Star;
            }

        //显示    
        if ( * pStr <= 126 )                   //英文字符
        {            
            ILI9341_LCD_DisChar_EN ( usX, usY, * pStr);

            pStr ++;

            usY += CH_HEIGHT;        
        }
        else                                //汉字字符
        {            
            usCh = * ( uint16_t * ) pStr;    

            usCh = ( usCh << 8 ) + ( usCh >> 8 );        
            ILI9341_LCD_DisChar_CH ( usX,usY , usCh );

            usY += CH_HEIGHT;

            pStr += 2;           //一个汉字两个字节 

    }

  }    
} 

/**
  * @brief  清除某行文字
  * @param  Line: 指定要删除的行
  *   本参数可使用宏LINE(0)、LINE(1)等方式指定要删除的行,
  *   宏LINE(x)会根据当前选择的字体来计算Y坐标值,并删除当前字体高度的第x行。
  * @retval None
  */
void LCD_ClearLine(uint16_t Line)
{
  ILI9341_Clear(0,Line,LCD_X_LENGTH,((sFONT *)LCD_GetFont())->Height);    /* 清屏,显示全黑 */

}
/*********************end of file*************************/