※点阵汉字显示原理及其在点阵 LCD&LED 中的应用※
发表时间:2007-3-18 10:03:48 天气状况:![]() ![]() 摘要: 本文主要论述汉字的显示原理,并详细阐述了如何创建点阵字库,如何在点阵 LCD&LED 进行扫描显示的原理,还阐述了如何根据不同的字符编码标准,来存取数据,使您的系统可以和计算机兼容,并象计算机一样能显示各种字符。 关键字: 位( bit )、字节( byte )、字模、计算机内码( ASCII )、 UNICODE 编码 提示: 如果没有特别提示,本文所提到的字库都是指点阵字库 问题引入 大千世界中,有很多物质都可以看作是由很小很小的点 (例如:分子、原子)组成的,当然,您所看到的字符也不例外。假设我们把一个字符分成若干个可视的点组成,换句话来说,就是一个个点组成了我们看到的字 符。假设您的电脑显示器是液晶的,您不妨仔细的看看,每一个字符或图形都是由一个个的点组成的,只是这些点很小,小得让您不容易发现而已(仔细看看还是不 难发现呀!);由此,我们引入点阵字符的概念,从微观的电子信号 0 或 1 ,到宏观可视的字符,足以让我们感叹这世界真是丰富多彩,奥妙无穷啊! 字模数据 首 先,从我们常用的计算机系统谈起,再扩展到我们要开发设计的点阵 LCD&LED 显示系统中去,其实单片机系统的显示原理和计算机是一样的。在计算机中,所有的数据(包括指令等)都是以 0 和 1 来表示的,这意味着,如果我们想要在显示器上显示字符,那么这些字符的信息将也会是以 0 、 1 来保存显示的。那么计算机是如何来存贮显示字符的呢?下面我们举例来说明点阵字符的数据存贮及显示原理(这里我们主要讨论的是点阵字符,故有关计算机矢量 字符的显示及其原理这里就不作说明,而且单片机的寻址和计算能力远不及 PC ,故显示矢量的字符还是有一定的困难)。假设我们把计算机液晶显示器上显示 16x16 点阵的“豪”字放大 10 倍,如下图所示: 放大 10 倍的“豪” 位信息 字模数据 ![]() ![]() ![]() 放 大之后,每一个小方格代表一个点,黑色的为 1 ,白色为 0 ;每一个点看作为一位( bit )。据此可以描绘出“豪”字的位( bit )信息。采用行扫描的方式,每八位( bit )为一个字节,这里采用十六进制表示,这样就得到了字模数据。由上述的示例,我们可以清晰的了解到可视字符、位信息与字模数据之间的关系。清楚了上面的关 系之后,我们就可以自己编写一个字模数据生成工具了。笔者自己就编写了两款很强劲的字模工具(汉字字模点阵数据批量生成工具[下载]和 Font Model Tool,前者是针对汉字编写的,后来一小日本想用这个工具,Font Model Tool就出生了,不过新增了很多功能,而且是一个中英文可以互相切换的双语版 ),这里介绍给大家,希望共同学习和使用哟。 [点阵异常处理] 假设字符的点阵不是 8 的倍数怎么办呢?通常情况下可以不计或在后面以 4 个 0 位补足 8 位都可,例如: 12x12 点阵的汉字,以本人编写的字模工具软件 为例,是这样处理的:先假设对 12x12的点阵字符进行扫描,第一行的前8位为一个字节,第一行的后面4位形成一个字节,以后的每行逐次类推,直到扫描到最后一行,行成一个完整可用的 字模数据。 点阵字库 把上述很多很多字符的字模数据按照一定的排列顺序存放在一起,就形成了点阵字库。这里所讲的字库是广义的,可以是文件,也可以是其他的东东,例如:数组、 DB 表等等所有可以存取数据的形式。有的点阵字库还带有索引表,用来方便程序的编写及查询。 在计算机中如何显示一个字符 在 计算机中是如何把点阵字符显示出来的呢?其实字符的显示过程是字模数据创建的逆过程。首先我们要明白字模数据的排列扫描方式,然后再把 16 进制的字模数据变成位( bit )信息,最后才能根据位信息按照字模数据给定的扫描方式逐个把点描绘出来。光说还是不行的,最好我们亲自动手来实验一下,先假定我们要用行扫描的显示方 式,在计算机中显示一个“豪”字,我们可以使用字模软件来创建一个字模数据,设定为行扫描、 16x16 点阵、宋体、 11 号字, 创建如下字模数据: unsigned char hao0[]={ 0x00,0x00, 0x00,0x80, 0x3F,0xFC, 0x00,0x00, 0x07,0xF0, 0x04,0x10, 0x3F,0xFE, 0x20,0x02, 0x4F,0xF4, 0x05,0x20, 0x1A,0xC0, 0x04,0xA0, 0x1B,0x58, 0x04,0x46, 0x19,0x80, 0x00,0x00 }; 则 C 语言全部显示代码描述如下: unsigned char cmp_w[8]={128,64,32,16,8,4,2,1}; // 用于取位 unsigned char hao0[]={ /* 字模数组 */ 0x00,0x00, 0x00,0x80, 0x3F,0xFC, 0x00,0x00, 0x07,0xF0, 0x04,0x10, 0x3F,0xFE, 0x20,0x02, 0x4F,0xF4, 0x05,0x20, 0x1A,0xC0, 0x04,0xA0, 0x1B,0x58, 0x04,0x46, 0x19,0x80, 0x00,0x00 }; void FontDisplay(int x, int y, unsigned char * FontModule) ; /*16x16 单个字符行扫描函数 */ void FontDisplay(int x, int y, unsigned char * FontModule) {/*x: 水平偏移坐标, y: 垂直偏移坐标 */ for(int row=0;row<16;row++) { for(int c=0;c<8;c++) if((FontModule[row*2]&cmp_w[c])!=0) putpixel(c+x,row+y,15);/* 画一个点 */ for(c=0;c<8;c++) if((FontModule[row*2+1]&cmp_w[c])!=0) putpixel(c+8+x,row+y,15); } } main() {/* 调用显示主程序 */ FontDisplay(5,5, hao0 ); } 上 述代码直接拷贝即可用,假设你的系统需要显示的字符不多,直接使用上面的代码或稍做修改,直接编译烧写到芯片里,即可满足一般点阵字符显示的需求,而不需 要制作大的文件字库,节省有限的 ROM 空间,编程又极其简单。假设我们要显示一串字符,把这一串字符拷贝到字模工具软件里,字模 软件会为每个字符产生一个字模数组,我们在程序里按字符显示的顺序依次调用 FontDisplay(int x,int y, unsigned char * FontModule) 即可连续把字符显示出来。记着把显示的偏移位置递增一个字符宽度呀( x 或 y )!否则就重叠看不清了。不要笑俺,俺就犯过如此愚钝的错误! 备注 : putpixel(int x,int y,int color) 函数 x,y为坐标,color为颜色值.该函数在(x,y)点设定象素的颜色.由于硬件的不同,也许提供的函数不同,用户可把此函数作为参考,必要时用硬件提供的函数取而代之。 前 面提到的是 16x16点阵的字符的显示方案和程序例程。但往往我们在开发产品的时候,可能因为产品的体积定位较小,从而需要使用更小的液晶来显示;也可能为了显示更 多的字符;还可能为了节约成本,使用较小的存贮器、计算能力一般的廉价单片机,或者压根不要存贮芯片,直接把字模数据和程序一起烧在单片机里边。上述的情 况都有可能,解决的最好办法就是减小点阵的大小,点阵小,自然字模数据就小,存贮、扫描、计算的开销相对也小。但太小的点阵又不容易识别,为此,在汉显方 面,本人根据经验推荐使用12x12点阵。并在此给出一个12x12点阵的示例,供大家学习和交流之用。当然,12x12点阵的汉字在LCD和LED显示 屏中是最常用的,其优点就不言而喻了。 unsigned char hao0[]={ 0x04,0x0, 0xFF,0xE, 0x1F,0x8, 0x10,0x8, 0xFF,0xE, 0x80,0x2, 0x3F,0xC, 0x54,0x8, 0x2B,0x0, 0x12,0x8, 0x6E,0x6, 0x00,0x0 }; unsigned char cmp_w[8]={128,64,32,16,8,4,2,1}; void FontDisplay(int x, int y, unsigned char *FontModule); void FontDisplay(int x, int y, unsigned char *FontModule) {/*12x12扫描显示函数*/ for(int row=0;row<12;row++) { for(int c=0;c<8;c++) if((FontModule[row*2]&cmp_w[c])!=0) putpixel(c+x,row+y,15); for(c=0;c<4;c++) if((FontModule[row*2+1]&cmp_w[c+4])!=0) putpixel (c+8+x,row+y,15); } } main() {/*调用显示主程序*/ FontDisplay(5,5, hao0 ); } 几种常用的字符动态编码显示方案分析 直接固化显示字模数据: 将 要显示的字符的字模数据通过字模软件提取出来,顺序烧在存贮器中,当程序要显示的时候,直接提取送显示屏。优点是易理解、实现程序简单、空间资源占用少; 缺点:组织字模数据及寻址比较麻烦,可维护性及灵活性差。针对其缺点并不是没有解决的办法,作者推荐前面提到的汉字字模工具,它可按汉字的拼音批量生成字 模数组或汇编 DB 表,直接拷贝到程序里即可用,这样的字模数据可以方便灵活的根据以拼音命名的方式进行寻址。用户在使用的时候,直接用汉字的拼音代替字符串中相应的汉字, 显示程序则直接调用该地址的字模数据进行显示。前面在计算机中如何显示一个字符中所提到的 C 语言示例就是本方案,灵活方便吧。 创建索引表和点阵字模库: 索 引表包括字符机内码和该字符在字库中的偏移地址。如果字符的机内码的排列顺序和字符的字模数据在字库里的排列顺序一致,偏移地址则可通过计算的方式给出( offset= 该字符机内码在索引中的位置 No. * 单个字符的字模数据所占的字节数 bytes ;由本人编写的 汉字字模点阵数据批量生成工具 V5.0以上版本可创建机内码索引表 )。在显示的时候,先得到字符的机内码,再得到该字符机内码在索引中的位置,最后计算出该字符在字库中的偏移地址并从字库中取出字模数据进行扫描显示即 可。此方法的优点是灵活方便,占用空间小;但需要复杂的查询、计算、寻址取模等过程。如果字稍多,单个字的显示时间就会很长,会使系统显得慢,效率低 创建连续的大字库: 根 据字符编码,利用字模软件创建连续的大字库( Font Model Tool可担此重任 ),然后再根据字符编码直接计算出该字符在字库中的位置,最后取模显示。这种方法非常灵活,但是需要计算寻址,因为字库较大,所以寻址的时间可能会较长, 显示速度较慢。如果你的系统用的是高速芯片(例如: ARM 、 DSP ),大容量的存贮器件,这些对你来说不算什么,这种方法最适用你,因为这种方法程序易维护、不需经常修改字库、而且兼容性很强。当然,电子信息发展到今 天,芯片的计算能力已不是什么大的问题,越来越多的存贮芯片不断推出,价格低廉,为我们的开发奠定了很好的基础。同志们,努力吧!展望一下明天,还是很美 好的呀! ......要阅读更多更详尽的内容,请 下载本文 点击这里 下载字模软件工具 符24x24点阵水平扫描C语言示例代码:
边框汉显 一维数组实现举例: unsigned short FontOrg1[]={ /* @中 [Font Model Tool] */ 0x00,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x08, 0x3F,0xFC, 0x21,0x08, 0x21,0x08, 0x21,0x08, 0x21,0x08, 0x3F,0xF8, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x00,0x00}; unsigned short FontOrg2[]={ /* @国 [Font Model Tool] */ 0x00,0x00, 0x00,0x04, 0x3F,0xFE, 0x20,0x04, 0x2F,0xF4, 0x21,0x04, 0x21,0x04, 0x27,0xE4, 0x21,0x44, 0x21,0x24, 0x21,0x04, 0x2F,0xFC, 0x20,0x04, 0x3F,0xFC, 0x20,0x04, 0x00,0x00}; unsigned short FontOrg3[]={ /* @移 [Font Model Tool] */ 0x00,0x00, 0x04,0x20, 0x0E,0x20, 0x38,0x7C, 0x08,0x88, 0x09,0x50, 0x7E,0x20, 0x09,0xC0, 0x1C,0x24, 0x2A,0x7E, 0x28,0xC4, 0x4B,0x28, 0x08,0x10, 0x08,0x60, 0x0B,0x80, 0x00,0x00}; unsigned short FontOrg4[]={ /* @动 [Font Model Tool] */ 0x00,0x00, 0x00,0x20, 0x00,0x20, 0x3E,0x20, 0x00,0x24, 0x00,0xFE, 0x7F,0x24, 0x08,0x24, 0x08,0x24, 0x12,0x24, 0x22,0x44, 0x7F,0x44, 0x20,0x84, 0x01,0x14, 0x02,0x08, 0x00,0x00}; unsigned short FontEdge1[]={ /* @中 [Font Model Tool] */ 0x03,0x80, 0x02,0x80, 0x02,0x9C, 0x7E,0xF6, 0x40,0x02, 0x5E,0xF6, 0x52,0x94, 0x52,0x94, 0x5E,0xF4, 0x40,0x04, 0x7E,0xFC, 0x02,0x80, 0x02,0x80, 0x02,0x80, 0x02,0x80, 0x03,0x80}; unsigned short FontEdge2[]={ /* @国 [Font Model Tool] */ 0x00,0x0E, 0x7F,0xFB, 0x40,0x01, 0x5F,0xFB, 0x50,0x0A, 0x5E,0xFA, 0x5E,0xFA, 0x58,0x1A, 0x5E,0xBA, 0x52,0xDA, 0x5E,0xFA, 0x50,0x02, 0x5F,0xFA, 0x40,0x02, 0x5F,0xFA, 0x70,0x0E}; unsigned short FontEdge3[]={ /* @移 [Font Model Tool] */ 0x0E,0x70, 0x1B,0x50, 0x71,0xDE, 0x47,0x82, 0x77,0x76, 0xF6,0xAC, 0x81,0xD8, 0xF6,0x3E, 0x63,0xDB, 0x55,0x81, 0xD7,0x3B, 0xB4,0xD6, 0xF7,0xEC, 0x17,0x98, 0x14,0x70, 0x1F,0xC0}; unsigned short FontEdge4[]={ /* @动 [Font Model Tool] */ 0x00,0x70, 0x00,0x50, 0x7F,0x50, 0x41,0x5E, 0x7F,0xDB, 0xFF,0x01, 0x80,0xDB, 0xF7,0xDA, 0x37,0x5A, 0x6D,0xDA, 0xDD,0xBA, 0x80,0xAA, 0xDF,0x7A, 0x76,0xEA, 0x05,0xB6, 0x07,0x1C}; const unsigned short FontProp[][]={ {2,1,12,14}, /* 中 [Font Model Tool]*/ {2,1,13,14}, /* 国 [Font Model Tool]*/ {1,1,14,14}, /* 移 [Font Model Tool]*/ {1,1,14,14} /* 动 [Font Model Tool]*/ } main() {/*调用显示主程序*/ FontDisplay(8,5, FontOrg1); FontDisplay(8,5, FontEdge1); FontDisplay(24,5, FontOrg2); FontDisplay(24,5, FontEdge2); FontDisplay(40,5, FontOrg3); FontDisplay(40,5, FontEdge3); FontDisplay(56,5, FontOrg4); FontDisplay(56,5, FontEdge4); } 边框汉显 二维数组实现举例: const unsigned short FontOrg[4][32]={ /* @中 [Font Model Tool] */ {0x00,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x08, 0x3F,0xFC, 0x21,0x08, 0x21,0x08, 0x21,0x08, 0x21,0x08, 0x3F,0xF8, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x01,0x00, 0x00,0x00}, /* @国 [Font Model Tool] */ {0x00,0x00, 0x00,0x04, 0x3F,0xFE, 0x20,0x04, 0x2F,0xF4, 0x21,0x04, 0x21,0x04, 0x27,0xE4, 0x21,0x44, 0x21,0x24, 0x21,0x04, 0x2F,0xFC, 0x20,0x04, 0x3F,0xFC, 0x20,0x04, 0x00,0x00}, /* @移 [Font Model Tool] */ {0x00,0x00, 0x04,0x20, 0x0E,0x20, 0x38,0x7C, 0x08,0x88, 0x09,0x50, 0x7E,0x20, 0x09,0xC0, 0x1C,0x24, 0x2A,0x7E, 0x28,0xC4, 0x4B,0x28, 0x08,0x10, 0x08,0x60, 0x0B,0x80, 0x00,0x00}, /* @动 [Font Model Tool] */ {0x00,0x00, 0x00,0x20, 0x00,0x20, 0x3E,0x20, 0x00,0x24, 0x00,0xFE, 0x7F,0x24, 0x08,0x24, 0x08,0x24, 0x12,0x24, 0x22,0x44, 0x7F,0x44, 0x20,0x84, 0x01,0x14, 0x02,0x08, 0x00,0x00} } const unsigned short FontEdge[4][32]={ /* @中 [Font Model Tool] */ {0x03,0x80, 0x02,0x80, 0x02,0x9C, 0x7E,0xF6, 0x40,0x02, 0x5E,0xF6, 0x52,0x94, 0x52,0x94, 0x5E,0xF4, 0x40,0x04, 0x7E,0xFC, 0x02,0x80, 0x02,0x80, 0x02,0x80, 0x02,0x80, 0x03,0x80}, /* @国 [Font Model Tool] */ {0x00,0x0E, 0x7F,0xFB, 0x40,0x01, 0x5F,0xFB, 0x50,0x0A, 0x5E,0xFA, 0x5E,0xFA, 0x58,0x1A, 0x5E,0xBA, 0x52,0xDA, 0x5E,0xFA, 0x50,0x02, 0x5F,0xFA, 0x40,0x02, 0x5F,0xFA, 0x70,0x0E}, /* @移 [Font Model Tool] */ {0x0E,0x70, 0x1B,0x50, 0x71,0xDE, 0x47,0x82, 0x77,0x76, 0xF6,0xAC, 0x81,0xD8, 0xF6,0x3E, 0x63,0xDB, 0x55,0x81, 0xD7,0x3B, 0xB4,0xD6, 0xF7,0xEC, 0x17,0x98, 0x14,0x70, 0x1F,0xC0}, /* @动 [Font Model Tool] */ {0x00,0x70, 0x00,0x50, 0x7F,0x50, 0x41,0x5E, 0x7F,0xDB, 0xFF,0x01, 0x80,0xDB, 0xF7,0xDA, 0x37,0x5A, 0x6D,0xDA, 0xDD,0xBA, 0x80,0xAA, 0xDF,0x7A, 0x76,0xEA, 0x05,0xB6, 0x07,0x1C} } unsigned char cmp_w[8]={128,64,32,16,8,4,2,1}; void DrawFontOrg(int x, int y, int dim); void DrawFontEdge(int x, int y, int dim); void DrawFontOrg(int x, int y, int dim) {/*16x16扫描显示函数*/ for(int row=0;row<16;row++) { for(int c=0;c<8;c++) if((FontOrg[dim][row*2]&cmp_w[c])!=0) putpixel(c+x,row+y,0);/* 画一个点 */ for(c=0;c<8;c++) if((FontModule[dim][row*2+1]&cmp_w[c])!=0) putpixel(c+8+x,row+y,0); } } void DrawFontEdge(int x, int y, int dim) {/*16x16扫描显示函数*/ for(int row=0;row<16;row++) { for(int c=0;c<8;c++) if((FontEdge[dim][row*2]&cmp_w[c])!=0) putpixel(c+x,row+y,15);/* 画一个点 */ for(c=0;c<8;c++) if((FontEdge[dim][row*2+1]&cmp_w[c])!=0) putpixel(c+8+x,row+y,15); } } main() {/*调用显示主程序*/ DrawFontOrg(8,5,0); DrawFontEdge(8,5,0); DrawFontOrg(24,5,1); DrawFontEdge(24,5,1); DrawFontOrg(40,5,2); DrawFontEdge(40,5,2); DrawFontOrg(56,5,3); DrawFontEdge(56,5,3); } 所有刊登本文的网站必须经作者同意并注明:本文由豪智软件工具自由职业者 秦文豪 提供 www. 字样 |
|