第28章 LTDC—液晶显示中英文
全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn
野火视频教程优酷观看网址:http://i.youku.com/firege
本章参考资料:《STM32F4xx 中文参考手册2》、《STM32F4xx规格书》、库帮助文档《stm32f4xx_dsp_stdperiph_lib_um.chm》。
关于开发板配套的液晶屏参数可查阅《5.0寸液晶屏数据手册》(或7.0寸数据手册)配套资料获知。本教程讲解时主要使用5寸屏来说明,对于我们配套的7寸屏,使用原理及配置参数完全一致(该7寸屏与5寸屏都是800x480的分辨率,仅尺寸不一样)。
在前面我们学习了如何使用LTDC外设控制液晶屏并用它显示各种图形,本章讲解如何控制液晶屏显示文字。使用液晶屏显示文字时,涉及到字符编码与字模的知识。
28.1 字符编码
由于计算机只能识别0和1,文字也只能以0和1的形式在计算机里存储,所以我们需要对文字进行编码才能让计算机处理,编码的过程就是规定特定的01数字串来表示特定的文字,最简单的字符编码例子是ASCII码。
28.1.1 ASCII编码
学习C语言时,我们知道在程序设计中使用ASCII编码表约定了一些控制字符、英文及数字。它们在存储器中,本质也是二进制数,只是我们约定这些二进制数可以表示某些特殊意义,如以ASCII编码解释数字"0x41"时,它表示英文字符"A"。ASCII码表分为两部分,第一部分是控制字符或通讯专用字符,它们的数字编码从0~31,见表 281,它们并没有特定的图形显示,但会根据不同的应用程序,而对文本显示有不同的影响。ASCII码的第二部分包括空格、阿拉伯数字、标点符号、大小写英文字母以及"DEL(删除控制)",这部分符号的数字编码从32~127,除最后一个DEL符号外,都能以图形的方式来表示,它们属于传统文字书写系统的一部分。
表 281 ASCII码中的控制字符或通讯专用字符
十进制 | 十六进制 | 缩写/字符 | 解释 |
0 | 0 | NUL(null) | 空字符 |
1 | 1 | SOH(start of headline) | 标题开始 |
2 | 2 | STX (start of text) | 正文开始 |
3 | 3 | ETX (end of text) | 正文结束 |
4 | 4 | EOT (end of transmission) | 传输结束 |
5 | 5 | ENQ (enquiry) | 请求 |
6 | 6 | ACK (acknowledge) | 收到通知 |
7 | 7 | BEL (bell) | 响铃 |
8 | 8 | BS (backspace) | 退格 |
9 | 9 | HT (horizontal tab) | 水平制表符 |
10 | 0A | LF (NL line feed, new line) | 换行键 |
11 | 0B | VT (vertical tab) | 垂直制表符 |
12 | 0C | FF (NP form feed, new page) | 换页键 |
13 | 0D | CR (carriage return) | 回车键 |
14 | 0E | SO (shift out) | 不用切换 |
15 | 0F | SI (shift in) | 启用切换 |
16 | 10 | DLE (data link escape) | 数据链路转义 |
17 | 11 | DC1 (device control 1) | 设备控制1 |
18 | 12 | DC2 (device control 2) | 设备控制2 |
19 | 13 | DC3 (device control 3) | 设备控制3 |
20 | 14 | DC4 (device control 4) | 设备控制4 |
21 | 15 | NAK (negative acknowledge) | 拒绝接收 |
22 | 16 | SYN (synchronous idle) | 同步空闲 |
23 | 17 | ETB (end of trans. block) | 传输块结束 |
24 | 18 | CAN (cancel) | 取消 |
25 | 19 | EM (end of medium) | 介质中断 |
26 | 1A | SUB (substitute) | 替补 |
27 | 1B | ESC (escape) | 换码(溢出) |
28 | 1C | FS (file separator) | 文件分割符 |
29 | 1D | GS (group separator) | 分组符 |
30 | 1E | RS (record separator) | 记录分离符 |
31 | 1F | US (unit separator) | 单元分隔符 |
表 282 ASCII码中的字符及数字
十进制 | 十六进制 | 缩写/字符 | 十进制 | 十六进制 | 缩写/字符 | |
32 | 20 | (space)空格 | 80 | 50 | P | |
33 | 21 | ! | 81 | 51 | Q | |
34 | 22 | " | 82 | 52 | R | |
35 | 23 | # | 83 | 53 | S | |
36 | 24 | $ | 84 | 54 | T | |
37 | 25 | % | 85 | 55 | U | |
38 | 26 | & | 86 | 56 | V | |
39 | 27 | ' | 87 | 57 | W | |
40 | 28 | ( | 88 | 58 | X | |
41 | 29 | ) | 89 | 59 | Y | |
42 | 2A | * | 90 | 5A | Z | |
43 | 2B | + | 91 | 5B | [ | |
44 | 2C | , | 92 | 5C | \ | |
45 | 2D | - | 93 | 5D | ] | |
46 | 2E | . | 94 | 5E | ^ | |
47 | 2F | / | 95 | 5F | _ | |
48 | 30 | 0 | 96 | 60 | ` | |
49 | 31 | 1 | 97 | 61 | a | |
50 | 32 | 2 | 98 | 62 | b | |
51 | 33 | 3 | 99 | 63 | c | |
52 | 34 | 4 | 100 | 64 | d | |
53 | 35 | 5 | 101 | 65 | e | |
54 | 36 | 6 | 102 | 66 | f | |
55 | 37 | 7 | 103 | 67 | g | |
56 | 38 | 8 | 104 | 68 | h | |
57 | 39 | 9 | 105 | 69 | i | |
58 | 3A | : | 106 | 6A | j | |
59 | 3B | ; | 107 | 6B | k | |
60 | 3C | < | 108 | 6C | l | |
61 | 3D | = | 109 | 6D | m | |
62 | 3E | > | 110 | 6E | n | |
63 | 3F | ? | 111 | 6F | o | |
64 | 40 | @ | 112 | 70 | p | |
65 | 41 | A | 113 | 71 | q | |
66 | 42 | B | 114 | 72 | r | |
67 | 43 | C | 115 | 73 | s | |
68 | 44 | D | 116 | 74 | t | |
69 | 45 | E | 117 | 75 | u | |
70 | 46 | F | 118 | 76 | v | |
71 | 47 | G | 119 | 77 | w | |
72 | 48 | H | 120 | 78 | x | |
73 | 49 | I | 121 | 79 | y | |
74 | 4A | J | 122 | 7A | z | |
75 | 4B | K | 123 | 7B | { | |
76 | 4C | L | 124 | 7C | | | |
77 | 4D | M | 125 | 7D | } | |
78 | 4E | N | 126 | 7E | ~ | |
79 | 4F | O | 127 | 7F | DEL (delete) 删除 |
后来,计算机引进到其它国家的时候,由于他们使用的不是英语,他们使用的字母在ASCII码表中没有定义,所以他们采用127号之后的位来表示这些新的字母,还加入了各种形状,一直编号到255。从128到255这些字符被称为ASCII扩展字符集。至此基本存储单位Byte(char)能表示的编号都被用完了。
28.1.2 中文编码
由于英文书写系统都是由26个基本字母组成,利用26个字母组可合出不同的单词,所以用ASCII码表就能表达整个英文书写系统。而中文书写系统中的汉字是独立的方块,若参考单词拆解成字母的表示方式,汉字可以拆解成部首、笔画来表示,但这样会非常复杂(可参考五笔输入法编码),所以中文编码直接对方块字进行编码,一个汉字使用一个号码。
由于汉字非常多,常用字就有6000多个,如果像ASCII编码表那样只使用1个字节最多只能表示256个汉字,所以我们使用2个字节来编码。
1. GB2312标准
我们首先定义的是GB2312标准。它把ASCII码表127号之后的扩展字符集直接取消掉,并规定小于127的编码按原来ASCII标准解释字符。当2个大于127的字符连在一起时,就表示1个汉字,第1个字节使用 (0xA1-0xFE) 编码,第2个字节使用(0xA1-0xFE)编码,这样的编码组合起来可以表示了7000多个符号,其中包含6763个汉字。在这些编码里,我们还把数学符号、罗马字母、日文假名等都编进表中,就连原来在ASCII里原本就有的数字、标点以及字母也重新编了2个字节长的编码,这就是平时在输入法里可切换的"全角"字符,而标准的ASCII码表中127号以下的就被称为"半角"字符。
表 283说明了GB2312是如何兼容ASCII码的,当我们设定系统使用GB2312标准的时候,它遇到一个字符串时,会按字节检测字符值的大小,若遇到连续两个字节的数值都大于127时就把这两个连续的字节合在一起,用GB2312解码,若遇到的数值小于127,就直接用ASCII把它解码。
表 283 GB2312兼容ASCII码的原理
第1字节 | 第2字节 | 表示的字符 | 说明 |
0x68 | 0x69 | (hi) | 两个字节的值都小于127(0x7F),使用ASCII解码 |
0xB0 | 0xA1 | (啊) | 两个字节的值都大于127(0x7F),使用GB2312解码 |
区位码
在GB2312编码的实际使用中,有时会用到区位码的概念,见图 281。GB2312编码对所收录字符进行了"分区"处理,共94个区,每区含有94个位,共8836个码位。而区位码实际是GB2312编码的内部形式,它规定对收录的每个字符采用两个字节表示,第一个字节为"高字节",对应94个区;第二个字节为"低字节",对应94个位。所以它的区位码范围是:0101-9494。为兼容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的码位表示的是"空格"符。
图 281 GB2312 的部分区位码
2. GBK编码
据统计,GB2312编码中表示的6763个汉字已经覆盖中国大陆99.75%的使用率,单看这个数字已经很令人满意了,但是我们不能因为那些文字不常用就不让它进入信息时代,而且生僻字在人名、文言文中的出现频率是非常高的。为此我们在GB2312标准的基础上又增加了14240个新汉字(包括所有后面介绍的Big5中的所有汉字)和符号,这个方案被称为GBK标准。增加这么多字符,按照GB2312原来的格式来编码,2个字节已经没有足够的编码,我们聪明的程序员修改了一下格式,不再要求第2个字节的编码值必须大于127,只要第1个字节大于127就表示这是一个汉字的开始,这样就做到了兼容ASCII和GB2312标准。
表 284说明了GBK是如何兼容ASCII和GB2312标准的,当我们设定系统使用GBK标准的时候,它按顺序遍历字符串,按字节检测字符值的大小,若遇到一个字符的值大于127时,就再读取它后面的一个字符,把这两个字符值合在一起,用GBK解码,解码完后,再读取第3个字符,重新开始以上过程,若该字符值小于127,则直接用ASCII解码。
表 284 GBK兼容ASCII和GB2312的原理
第1字节 | 第2字节 | 第3字节 | 表示的字符 | 说明 |
0x68(<7F) | 0xB0(>7F) | 0xA1(>7F) | (h啊) | 个字节小于127,使用ASCII解码,每2个字节大于127,直接使用GBK解码,兼容GB2312 |
0xB0(>7F) | 0xA1(>7F) | 0x68(<7F) | (啊h) | 个字节大于127,直接使用GBK码解释,第3个字节小于127,使用ASCII解码 |
0xB0(>7F) | 0x56(<7F) | 0x68(<7F) | (癡h) | 个字节大于127,第2个字节虽然小于127,直接使用GBK解码,第3个字节小于127,使用ASCII解码 |
3. GB18030
随着计算机技术的普及,我们后来又在GBK的标准上不断扩展字符,这些标准被称为GB18030,如GB18030-2000、GB18030-2005等("-"号后面的数字是制定标准时的年号),GB18030的编码使用4个字节,它利用前面标准中的第2个字节未使用的"0x30-0x39"编码表示扩充四字节的后缀,兼容GBK、GB2312及ASCII标准。
GB18030-2000主要在GBK基础上增加了"CJK(中日韩)统一汉字扩充A"的汉字。加上前面GBK的内容,GB18030-2000一共规定了27533个汉字(包括部首、部件等)的编码,还有一些常用非汉字符号。
GB18030-2005的主要特点是在GB18030-2000基础上增加了"CJK(中日韩)统一汉字扩充B"的汉字。增加了42711个汉字和多种我国少数民族文字的编码(如藏、蒙古、傣、彝、朝鲜、维吾尔文等)。加上前面GB18030-2000的内容,一共收录了70244个汉字。
GB2312、GBK及GB18030是汉字的国家标准编码,新版向下兼容旧版,各个标准简要说明见表 285,目前比较流行的是GBK编码,因为每个汉字只占用2个字节,而且它编码的字符已经能满足大部分的需求,但国家要求一些产品必须支持GB18030标准。
表 285 汉字国家标准
类别 | 编码范围 | 汉字编码范围 | 扩充汉字数 | 说明 |
GB2312 | 第一字节0xA1-0xFE 第二字节0xA1-0xFE | 第一字节0xB0-0xF7 第二字节0xA1-0xFE | 6763 | 除汉字外,还包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符 |
GBK | 第一字节0x81-0xFE 第二字节0x40-0xFE | 第一字节0x81-0xA0 第二字节0x40-0xFE | 6080 | 包括部首和构件,中日韩汉字,包含了BIG5编码中的所有汉字,加上GB2312的原内容,一共有21003个汉字 |
第一字节0xAA-0xFE 第二字节0x40-0xA0 | 8160 | |||
GB18030-2000 | 第一字节0x81-0xFE 第二字节0x30-0x39 第三字节0x81-0xFE 第四字节0x30-0x39 | 第一字节0x81-0x82 第二字节0x30-0x39 第三字节0x81-0xFE 第四字节0x30-0x39 | 6530 | 在GBK基础上增加了中日韩统一汉字扩充A的汉字,加上GB2312、GBK的内容,一共有27533个汉字 |
GB18030-2005 | 第一字节0x81-0xFE 第二字节0x30-0x39 第三字节0x81-0xFE 第四字节0x30-0x39 | 第一字节0x95-0x98 第二字节0x30-0x39 第三字节0x81-0xFE 第四字节0x30-0x39 | 42711 | 在GB18030-2000的基础上增加了42711中日韩统一汉字扩充B中的汉字和多种我国少数民族文字的编码(如藏、蒙古、傣、彝、朝鲜、维吾尔文等),加上前面GB2312、GBK、GB18030-2000的内容,一共70244个汉字 |
4. Big5编码
在台湾、香港等地区,使用较多的是Big5编码,它的主要特点是收录了繁体字。而从GBK编码开始,已经把Big5中的所有汉字收录进编码了。即对于汉字部分,GBK是Big5的超集,Big5能表示的汉字,在GBK都能找到那些字相应的编码,但他们的编码是不一样的,两个标准不兼容,如GBK中的"啊"字编码是"0xB0A1",而Big5标准中的编码为"0xB0DA"。
28.1.3 Unicode字符集和编码
由于各个国家或地区都根据使用自己的文字系统制定标准,同一个编码在不同的标准里表示不一样的字符,各个标准互不兼容,而又没有一个标准能够囊括所有的字符,即无法用一个标准表达所有字符。国际标准化组织(ISO)为解决这一问题,它舍弃了地区性的方案,重新给全球上所有文化使用的字母和符号进行编号,对每个字符指定一个唯一的编号(ASCII中原有的字符编号不变),这些字符的号码从0x000000到0x10FFFF,该编号集被称为Universal Multiple-Octet Coded Character Set,简称UCS,也被称为Unicode。最新版的Unicode标准还包含了表情符号(聊天软件中的部分emoji表情),可访问Unicode官网了解:http://www.unicode.org。
Unicode字符集只是对字符进行编号,但具体怎么对每个字符进行编码,Unicode并没指定,因此也衍生出了如下几种unicode编码方案(Unicode Transformation Format)。
28.1.4 UTF-32
对Unicode字符集编码,最自然的就是UTF-32方式了。编码时,它直接对Unicode字符集里的每个字符都用4字节来表示,转换方式很简单,直接将字符对应的编号数字转换为4字节的二进制数。如表 286,由于UTF-32把每个字符都用要4字节来存储,因此UTF-32不兼容ASCII编码,也就是说ASCII编码的文件用UTF-32标准来打开会成为乱码。
表 286 UTF-32编码示例
字符 | GBK编码 | Unicode编号 | UTF-32编码 |
A | 0x41 | 0x0000 0041 | 大端格式0x0000 0041 |
啊 | 0xB0A1 | 0x0000 554A | 大端格式0x0000 554A |
对UTF-32数据进行解码的时候,以4个字节为单位进行解析即可,根据编码可直接找到Unicode字符集中对应编号的字符。
UTF-32的优点是编码简单,解码也很方便,读取编码的时候每次都直接读4个字节,不需要加其它的判断。它的缺点是浪费存储空间,大量常用字符的编号只需要2个字节就能表示。其次,在存储的时候需要指定字节顺序,是高位字节存储在前(大端格式),还是低位字节存储在前(小端格式)。
28.1.5 UTF-16
针对UTF-32的缺点,人们改进出了UTF-16的编码方式,它采用2字节或4字节的变长编码方式(UTF-32定长为4字节)。对Unicode字符编号在0到65535的统一用2个字节来表示,将每个字符的编号转换为2字节的二进制数,即从0x0000到0xFFFF。而由于Unicode字符集在0xD800-0xDBFF这个区间是没有表示任何字符的,所以UTF-16就利用这段空间,对Unicode中编号超出0xFFFF的字符,利用它们的编号做某种运算与该空间建立映射关系,从而利用该空间表示4字节扩展,感兴趣的读者可查阅相关资料了解具体的映射过程。
表 287 UTF-16编码示例
字符 | GB18030编码 | Unicode编号 | UTF-16编码 |
A | 0x41 | 0x0000 0041 | 大端格式0x0041 |
啊 | 0xB0A1 | 0x0000 554A | 大端格式0x554A |
05-22 03:14 |