常见的液晶显示器有段式液晶、字符液晶和图形液晶等。其中,段式液晶**省电,但对于通用显示使用起来不很方便,只能显示固定式数字或符号,而且需要专用驱动电路或特殊的单片机。
字符液晶(如1602)用得比较多,容易和单片机配合,但是一般都需要5V工作电压,虽然现在也有3V就可以工作的模块,但是体积还是较大,而且只能显示数字和西文字符,无法显示图形和汉字。
点阵液晶模块既可以显示ASCII字符,又可以显示汉字和图形,相对于前面几种,具有更大的灵活性,所以使用得越来越多。不过常用的图形液晶因为显示面积增加,体积比字符液晶(如1602)更大,价格也更贵。初学者要注意的是,12864图形点阵液晶随着厂家设计使用的驱动芯片不同,驱动程序有所区别,不像1602那样基本通用。
几种常见的12864图形点阵模块
12864点阵液晶模块分为带汉字库和不带汉字库两大类,目前带汉字库的通常是ST7920驱动,它可以工作在汉字字符方式和图形点阵方式,很多制作都用它,如果需要显示较多汉字,用它**为方便。
在显示汉字数量很少的场合,我们可以使用更加廉价的、不带字库的点阵液晶模块,这正是本文重点介绍的。它们的控制电路有KS0108和ST7565两种:KS0108很简单,一共只有7条指令,可是它没有串行接口;ST7565有20多条指令(**常用的也就几条),有串行接口,可选串行或并行工作。KS0108和ST7565的指令和上述带字库的ST7920区别较大,所以初学者买液晶时一定要搞清楚是哪种驱动电路。即使同样的驱动电路,不同厂家或者不同型号的产品,具体细节仍可能不同。例如有的片选信号是高电平有效,有的却是低电平有效,有的把显示区分为左右两半分别选取,有的却不加区分。所以使用前要仔细看厂家说明,如果没有,就要看液晶模块背面给出的具体型号,根据这个型号去查找使用手册。
笔者**近在淘宝网上搜寻到一款12864的图形点阵液晶,只有4cm宽、3.5cm高,显示面积为3.2cm宽、1.95cm高,非常小巧。更加难能可贵的是它可以在3V低电压工作,很适合我们制作小型便携装置。该液晶模块型号是SP12864FPD-12CSBE,由北京集粹电子设备公司出品,它的外形见图1。
图1 12864图形点阵液晶模块 图2 小小日历钟(文字界面) 图3 小小日历钟(图形界面)
图2、图3所示为笔者用它制作的一个小小日历钟,它的特点是具有可以随意转换的文字和图形界面。文字界面除了显示年月日时分秒,在右上角还有一个小鸡啄米的小动画,图形界面用指针在刻度上指示出时分秒,是不是有点新意呢?图4所示是调频收音机的显示屏,用进度条指示音量,用刻度尺显示信号强度,比1602只能显示数字和字母要生动多了。没有字库用起来是否很麻烦?其实搞清了图形点阵的基本工作原理,用起来并不麻烦却更灵活,不带字库我们就按需要打造字符!
图4 调频收音机 图5 液晶模块采用“COG”封装 图6 点阵液晶显示原理
下面就谈谈这个液晶的驱动方法,以后将给出一些制作实例,以帮助初学者用它做出具有个性的东西。
液晶模块SO12864-12C简介
此型号小液晶包括一系列子型号,详见
◆ 逻辑或电源电压2.8~5.5V;
◆ 蓝色背光,背光电压3V;
◆ 串行接口,用 8个焊盘引出包括电源、背光、地和控制线数据线;
◆ 不带字库,需要自己编辑外部字模数组;
◆ 速度较快,用时钟1MHz的AVR单片机驱动,编程时无须附加脉冲额外延时;
◆ 使用ST7565电路,命令代码一共23条。
显然,它十分适合低压小尺寸场合应用,串行接口**大限度减少了液晶和单片机的连线,虽说比并行慢一点,实际上如果不是频繁刷屏影响并不大。要自己编制字库确实比较麻烦,但是只要显示的文字量少,制作小字模也不困难,反而可以自己打造个性化的字体,使得显示具有特色。
把液晶模块翻过来,如图5所示,发现电路板上没有通常液晶模块的黑胶封装集成电路,原来这个液晶采用的是“COG”封装,就是把集成电路直接绑定在液晶玻璃板上。
它的8个接口焊盘位于模块上方,定义如下:
1. 片选CS:它为低电平才能进行操作,在加载数据后至少维持40ns低电平。
2. 复位RES:启动时至少维持1μs低电平以使液晶内部复位,然后升高,再过1μs完成复位,以后才能对液晶进行操作。
3. 命令数据选择A0:高电平为数据,低电平为命令。
4. 串行时钟SCL:顶底宽度至少25ns,低时A0和SI至少稳定20ns,然后在上升沿加载数据或命令。
5. 串行数据SI:同上,在SCL上升沿加载后至少还要保持10ns稳定。
6. 电源正VCC:**低2.8V,标准3V,**大5.5V。
7. 地VSS。
8. 背光LED+:蓝色背光**低2.8V,标准3V,**大3.2V,使用时要注意不要**过。
在串行模式时,一个命令或数据字节要分为8次加载,从**高位开始。
图形点阵的显示原理
12864点阵液晶的图形显示原理都差不多。液晶屏x方向(水平)具有128列像素从左到右为第0列……第127列,y方向(垂直)具有64行像素。每8行组成1页,从上到下就是第0页……第7页。这样以列号和页号为坐标,就可以**交叉位置的8个像素。例如第0、1、2、3列第1页的8个像素,如图6所示。在液晶内部有一块显示缓存区,按照列号和页号就可以对显缓区的某个字节写数,该字节的8位二进制数就对应了液晶屏同样位置的像素的亮灭,如对第1列第1页的那个缓存单元写入0X80即0b10000000,那么液晶对应位置的**下面一点7亮(低位在上高位在下)其余都灭,如果第2列第1页写入0X0F即0b00001111,则该位置上方4个点0123亮,其余像素不亮,第3列第1页写0X33,则该处间隔2点亮。这样就可以通过程序控制液晶屏的任意像素了。不同的液晶屏指令代码可能不同(例如这个屏和常见的7920驱动不一样),屏幕划分也可能不同,例如有的是分为左右两半,每半边64列,有的是分为上下两半等。
液晶SO12864-12C的编程要点
控制液晶**基本的工作就是往液晶的控制器写入命令码或往显缓区写入数据码,是命令还是数据由加到上述A0的电平高低决定:高,数据;低,命令。
因为现在是串行传送,所以只能由高到低一位位写。
串行只能写不能读。以前要靠读来判定液晶是否忙,这个液晶速度较高,实际使用证明串行传输不用读忙。
编程就是用单片机的几根口线按照所需的时序发出高低电平,再往液晶里送入代码或数据。控制脉冲和代码脉冲的时序关系如图7所示。
图7 控制脉冲和代码脉冲的时序关系
**常用的几个命令
1. 显示开/关:代码:0XAF(开),0XAE(关)启动复位后为“关”,**在液晶初始化时置“开”。
2. 页地址定位:代码:0XB0……0XB7,对应第0页到第7页,复位后自动安置为第0页。
3. 列地址定位:列地址是0X00……0X7F,但不是直接用列地址而要转换为双字节代码。方法是0X10加原高4位和0X00加原低4位。例如第33列,本是0X21,现在应该转换为0X12和0X01,分2次写入。
4. 写显示数据:代码就是显示数据,控制脉冲A0为高。
5. 复位:代码0XE2,通过程序使得液晶恢复各种起始默认状态。
有了这几条命令就已经可以使液晶画出以像素点为基础的图形或字符了。有些其他命令(如对比控制、亮度、偏压等)就取默认值,无需修改(初始化要用一下),还有些很有用的命令(如反向列页扫描、起始行、反白显示等),需要时再去查资料不迟。有了上述命令代码,我们就可以通过汇编或C语言,按照控制时序编出子程序或函数,以便在程序中使用。C语言因为易读性好、通用性好、移植性好所以用得较多,下面就给出一些C的函数,由它们就构成了液晶的驱动。
驱动液晶的基本C函数
我们用C语言编程,在C语言里,用“函数”把单片机的一系列具体操作包装起来起个函数名,需要时直接按名调用即可,非常方便。
首先**解决怎样对液晶串行写数据的问题,然后按照A0线的高低,我们就可以自行编出写数据函数和写命令函数。
一下只列出函数说明和用法举例,函数的具体内容可到《无线电》网站上下载C程序代码。
1. 串行传送1字节数据函数,参数为待传字节。
void LcdWriByte(unsigned char nn) //nn就是待写字节
这个是**基本的函数,不过我们不直接用它而是把它放在其他写数据函数里调用。
2. 写命令。参数为命令码。
void LcdWriCommand(unsigned char command);
例如,打开显示:LcdWriCommand(0XAF);
3. 写数据。参数为待写的显示数据。
void LcdWriData(unsigned char data);
例如,写显示一个点的数据: LcdWriData(0X01);
4. **列、页地址。参数为列地址x和页地址y。
void LcdSetxy(unsigned char x,unsigned chary);
例如:LcdSetxy(68,2);//设置显示地址为第68列、第2页。
通常和上一个函数连用,见下一节函数应用举例。
在以上基本函数基础上我们还可以根据命令码推演出几个便于使用的功能函数。
5. 开显示。
void LcdOn(void );
例:LcdOn( );
6. 关显示。
void LcdOff(void );
例:LcdOff( );
7. 软复位。用它可以在任何时刻使液晶屏回到起始状态而显示缓冲区内容不变。
void LcdReset(void );
例:LcdReset( );
8. 刷屏。
void LcdCls(unsigned char data );
就是用数据data写满显示缓冲区,data如果为0,那全屏刷白(无像素显示),如果data为0XFF则全屏刷黑(像素全部亮)。如果为0XAA呢?感兴趣的读者不妨实验一下。
例:LcdCls(0 );
**后给出该液晶的初始化函数,在系统启动后,MCU初始化以后就进行液晶初始化。
9. LCD初始化。
Void Lcd_ini();
通常在程序开始阶段进行,对各种参数进行设置,具体初始化项目请参看函数内容和注解。
函数应用举例
1. 在**位置画1点
先给定页和列坐标,就**了1列8个像素点,1个点的位置从上到下对应数值为0X01、0X02、0X04、0X08、0X10、0X20、0X40、0X80。
例如在第9列、第3页**低位画点:
LcdSetxy(9,3);
LcdWriData(0X80);
2. 在**位置画连续8个点构成的短竖线
例如位置同上,则
LcdSetxy(9,3);
LcdWriData(0XFF);
3. 画水平线
例如从第10列起在第3页底部画1根长度50像素点的连续直线:
unsigned char i;
LcdSetxy(10,3);
for(i=0;i<50;i++)
{
LcdWriData(0X80); //循环中列地址自动递增
}
明白了以上基础,我们就可以进一步画出长短竖线、更粗的水平线、水平双线矩形方框、黑块等,这样就可以画简单图形了。
怎样显示字符或汉字
字符和汉字实质是在一个矩形区域内由一系列像素点构成的图形,也就是点阵图。例如在一个8×8点阵区域内,字母“L”点阵如图8左所示。如果要在第0页第0……第7列显示这个字符,那么就应该在相应的显示缓冲区装入相应的数据如右边图所示,这里用1表示显示像素,不显示的像素都是0,为清楚起见图中就不标出,上方表示列号0……7,那么从0列开始,各列的二进数和16进数分别表示为:
0B00000000 : 0X00
0B01000010 : 0X42
0B01111110 : 0X7E
0B01000010 : 0X82
0B01000000 : 0X40
0B01000000 : 0X40
0B01100000 : 0X60
0B00000000 : 0X00
所以我们就用一个8元素的数组来表示这个“L”:
const unsigned char L[8]={0X00,0X42,0X7E,0X82,0X40,0X40,0X60,0X00};
要在液晶上的第3页第6列显示这个“L”,就使用如下函数:
LcdShow88(unsigned x,unsigned y, const char *p);
其中p指向8点阵字符数组,那么具体代码为:
LcdShow88(6,3, L);
打造字模就是按照所需显示的文字符号一一编制各自的数组,上述8×8点阵就可以在1页高度内显示,实际上其上下左右都要留空,这样有效的点阵只有6×6,要显示数字和ASCII字符尚可,简单笔画的汉字也行,可是笔画稍多的汉字就不行了。对于汉字显示我们一般使用16×16点阵的字模。这样的汉字高度占2页,宽度是16列,12864的液晶可以显示4行,每行8个字。所有的12864液晶不论尺寸大小都是如此。
一个16点阵字模数组具有32个元素,每一页有16个元素。因此显示这样的汉字时,就要给同一列的某一页和下一页分别写入各自的16个元素。例如汉字“钟”的字模数组为:
const unsigned char ZHONG[ ]={0x80,0x40,0x70,0xCF,0x48,0x48,0x00,0xF0,0x10,0x10,0xFF,0x10,0x10,0xF8,0x10,0x00, 0x02,0x02,0x02,0x7F,0x22,0x12,0x00,0x07,0x02,0x02,0xFF,0x02,0x02,0x07,0x00,0x00};
要在第2页、第6列开始显示“钟”,我们用一个函数:
LcdShow1616(unsigned x,unsigned y, const char *p);
其中参数x 是列,y是页,p指向字模数组,具体的代码为:
LcdShow1616(6,2, ZHONG);
这样,汉字就显出来了。
**后,交代一下取得点阵汉字字模的方法。
编制字模数组的好帮手——字模软件的使用
因为我们现在需要的汉字量很少,所以可以使用一些字模提取软件自己制作所需的字模。以一个很好用的绿色免费“畔畔字模提取软件”为例加以说明。
此软件在各大程序网站都有下载,下载解压后就可使用,开启界面如图9所示。选取Shape :方形,Mode:C51,取模顺序选第4种,即点阵图右上为A,右下为B,左上为C,左下为D,这是按照先右半从上到下,后左半从上到下的顺序取模。
图9
图10
图11
图12
然后在下面输入框里输入汉字回车后,左边显示点阵字,见图10。对点阵自行修饰改动,用鼠标点选即可,例如竖笔加粗等,这里没改。
点右旋90度按钮,点阵汉字右旋90度,其目的是由上往下取码,相当于显示时从左往右显示。点“提取字模”按钮,字模代码呈现在点阵下面,见图11。注意字模码有2行,上行对应旋转后的右半区域从上往下取码,下行对应左半区域从上往下取码,这就等同于旋转前的汉字从左往右取码,而上行代码对应的是汉字的上半代码,下行则对应的是汉字的下半代码。正好符合上述汉字显示程序先显示上半部后显示下半部的要求。这样就取得了例子中的字模代码。
可以把所需的汉字逐一取码,然后存在二维表中,就形成了16×16点阵字模数组。
因为单片机FLASH ROM程序存储空间较大而RAM较小,而一个16点阵汉字占据32字节,10个字就320字节,对于RAM通常在几百到1K字节以下的单片机嫌太大,所以字库应该存放在FLASH区比较合理。但是读出FLASH中的数据,程序还要变一下,以使用GCC编程为例,前述字模常量和程序修改如下:
const unsigned char_[ ]PROGMEM{0x80,0x40,0x70,0xCF,0x48,0x48,0x00,0xF0,0x10,0x10,0xFF,0x10,0x10,0xF8,0x10,0x00,0x02,0x02,0x02,0x7F,0x22,0x12,0x00,0x07,0x02,0x02,0xFF,0x02,0x02,0x07,0x00,0x00,};
要在第2页第6列显示“钟”,用一个稍稍不同的函数:
LcdShowFlash1616(unsigned x,unsigned y, const char *p);
即:LcdShowFlash1616(6,2, ZHONG_);
就行了。
以上各有关函数具体内容可到《无线电》网站上下载。
制作非标准的字模
有时我们需要一些“非标准的”字符,例如12宽×16高或者8×8字符,同样也可以使用上述工具,不过这时字符就要用鼠标在选定区域内逐点点出,因为他提取的时候还是针对16×16点阵,所以**后还要去除多余的0字节,如图12表示一个数字9的取模。得到的代码上下排都去掉**后4个0字节如图中下面画线的那8个。**后每个字就24字节,程序中循环此书作相应改变。
好了 喋喋不休一大堆,基本方法都交代了,说得口干看得头大,这次就到这里,下次结合一个具体的小日历钟,看看该怎么整。
大大小小的液晶模块在电子市场里有很多种,读者朋友不一定限于笔者所用的型号,如果有兴趣,在淘宝网上搜索小12864液晶即可。