接 口
lcd1602是很多单片机爱好者较早接触的字符型液晶显示器,它的主控芯片是hd44780或者其它兼容芯片。刚开始接触它的大多是单片机的初学者。由于对它的不了解,不能随心所欲地对它进行驱动。经过一段时间的学习,我对它的驱动有了一点点心得,今天把它记录在这里,以备以后查阅。
与此相仿的是lcd12864液晶显示器,它是一种图形点阵显示器,能显示的内容比lcd1602要丰富得多,除了普通字符外,还可以显示点阵图案,带有汉字库的还可以显示汉字,它的并行驱动方式与lcd1602相差无几,所以,在这里花点时间是值得的。
一般来说,lcd1602有16条引脚,据说还有14条引脚的,与16脚的相比缺少了背光电源a(15脚)和地线k(16脚)。我手里这块lcd1602的型号是hj1602a,它有16条引脚:
图2
它的16条引脚定义如下:
表:引脚说明
对表格的说明:
(1)vss接电源地。
(2)vdd接+5v。
(3)vo是液晶显示的偏压信号,可接10k的3296精密电位器,或同样阻值的rm065/rm063蓝白可调电阻。见图3。
图3
(4)rs是命令/数据选择引脚,接单片机的一个i/o,当rs为低电平时,选择命令;当rs为高电平时,选择数据。
(5)rw是读/写选择引脚,接单片机的一个i/o,当rw为低电平时,向lcd1602写入命令或数据;当rw为高电平时,从lcd1602读取状态或数据。如果不需要进行读取操作,可以直接将其接vss。
(6)e,执行命令的使能引脚,接单片机的一个i/o。
(7)d0—d7,并行数据输入/输出引脚,可接单片机的p0—p3任意的8个i/o口。如果接p0口,p0口应该接4.7k—10k的上拉电阻。如果是4线并行驱动,只须接4个i/o口。
(8)a背光正极,可接一个10—47欧的限流电阻到vdd。
(9)k背光负极,接vss。见图4所示。
图4
基本操作
lcd1602的基本操作分为四种:
读状态。输入rs=0,rw=1,e=高脉冲。输出:d0—d7为状态字。
读数据。输入rs=1,rw=1,e=高脉冲。输出:d0—d7为数据。
写命令。输入rs=0,rw=0,e=高脉冲。输出:无。
写数据。输入rs=1,rw=0,e=高脉冲。输出:无。
读操作时序图,如图5所示:
图5
写操作时序图,如图6所示:
图6
时序时间参数,如图7所示:
图7
ddram、cgrom、cgram
ddram(display data ram)就是显示数据ram,用来寄存待显示的字符代码。共80个字节,其地址和屏幕的对应关系如下(如图8):
图8
ddram相当于计算机的显存,我们为了在屏幕上显示字符,就把字符代码送入显存,这样该字符就可以显示在屏幕上了。同样lcd1602共有80个字节的显存,即ddram。但lcd1602的显示屏幕只有16×2大小,因此,并不是所有写入ddram的字符代码都能在屏幕上显示出来,只有写在上图所示范围内的字符才可以显示出来,写在范围外的字符不能显示出来。这样,我们在程序中可以利用下面的“光标或显示移动指令”使字符慢慢移动到可见的显示范围内,看到字符的移动效果。
前面说了,为了在液晶屏幕上显示字符,就把字符代码送入ddram。例如,如果想在屏幕左上角显示字符‘a’,那么就把字符‘a’的字符代码41h写入ddram的00h地址处即可。至于怎么写入,后面会有说明。
那么为什么把字符代码写入ddram,就可以在相应位置显示这个代码的字符呢?我们知道,lcd1602是一种字符点阵显示器,为了显示一种字符的字形,必须要有这个字符的字模数据,什么叫字符的字模数据,看看下面的这个图就明白了(如图9)。
图9
上图的左边就是字符‘a’的字模数据,右边就是将左边数据用“○”代表0,用“■”代表1。从而显示出‘a’这个字形。从图12可以看出,字符‘a’的高4位是0100,低4位是0001,合在一起就是01000001b,即41h。它恰好与该字符的ascii码一致,这样就给了我们很大的方便,我们可以在pc上使用p2=‘a’这样的语法。编译后,正好是这个字符的字符代码。
在lcd1602模块上固化了字模存储器,就是cgrom和cgram,hd44780内置了192个常用字符的字模,存于字符产生器cgrom(character generator rom)中,另外还有8个允许用户自定义的字符产生ram,称为cgram(character generator ram)。图12说明了cgrom和cgram与字符的对应关系。
从rom和ram的名字我们也可以知道,rom是早已固化在lcd1602模块中的,只能读取;而ram是可读写的。也就是说,如果只需要在屏幕上显示已存在于cgrom中的字符,那么只须在ddram中写入它的字符代码就可以了;但如果要显示cgrom中没有的字符,比如摄氏温标的符号,那么就只有先在cgram中定义,然后再在ddram中写入这个自定义字符的字符代码即可。和cgrom中固化的字符不同,cgram中本身没有字符,所以要在ddram中写入某个cgrom不存在的字符,必须在cgram中先定义后使用。程序退出后cgram中定义的字符也不复存在,下次使用时,必须重新定义。
图10
图10说明的是5×8点阵和5×10点阵字符的字形和光标的位置。先来说5×8点阵,它有8行5列。那么定义这样一个字符需要8个字节,每个字节的前3个位没有被使用。例如,定义摄氏温标的符号{0x10,0x06,0x09,0x08,0x08,0x09,0x06,0x00}。
图11
图11说明的是设置cgram地址指令。从这个指令的格式中我们可以看出,它共有aaaaaa这6位,一共可以表示64个地址,即64个字节。一个5×8点阵字符共占用8个字节,那么这64个字节一共可以自定义8个字符。
也就是说,图11的6位地址中的db5db4db3用来表示8个自定义的字符,db2db1db0用来表示每个字符的8个字节。这db5db4db3所表示的8个自定义字符(0--7)就是要写入ddram中的字符代码。我们知道,在cgram中只能定义8个自定义字符,也就是只有0—7这8个字符代码,但在下面的这个表(如图12)中一共有16个字符代码(××××0000b--××××1111b)。
实际上,如图11所示,它只能表示8个自定义字符 (××××0000b=××××1000b, ××××0001b=××××1001b……依次类推)。也就是说,写入ddram中的字符代码0和字符代码8是同一个自定义字符。5×10点阵每个字符共占用16个字节的空间,所以cgram中只能定义4个这样的自定义字符。
那么如何在cgram中自定义字符呢?在上面的介绍中,我们知道有一个设置cgram地址指令,同写ddram指令相似,只须设置好某个自定义字符的字模数据,然后按照上面介绍的方法,设置好cgram地址,依次写入这个字模数据即可。我们在后面的例子中再进行说明。
图12
lcd1602指令
1、工作方式设置指令(图13)
图13
×:不关心,也就是说这个位是0或1都可以,一般取0。
dl:设置数据接口位数。
dl=1:8位数据接口(d7—d0)。
dl=0:4位数据接口(d7—d4)。
n=0:一行显示。
n=1:两行显示。
f=0:5×8点阵字符。
f=1:5×10点阵字符。
因为是写指令字,所以rs和rw都是0。lcd1602只能用并行方式驱动,不能用串行方式驱动。而并行方式又可以选择8位数据接口或4位数据接口。这里我们选择8位数据接口(d7—d0)。我们的设置是8位数据接口,两行显示,5×8点阵,即0b00111000也就是0x38。(注意:nf是10或11的效果是一样的,都是两行5×8点阵。因为它不能以两行5×10点阵方式进行显示,换句话说,这里用0x38或0x3c是一样的)。
2、显示开关控制指令(图14)
图14
d=1:显示开,d=0:显示关。
c=1:光标显示,c=0:光标不显示。
b=1:光标闪烁,b=0:光标不闪烁。
这里的设置是显示开,不显示光标,光标不闪烁,设置字为0x0c。
3、进入模式设置指令(图15、16)
图15
i/d=1:写入新数据后光标右移。
i/d=0:写入新数据后光标左移。
s=1:显示移动。
s=0:显示不移动。
图16
这里的设置是0x06。
4、光标或显示移动指令(图17、18)
图17
图18
在需要进行整屏移动时,这个指令非常有用,可以实现屏幕的滚动显示效果。初始化时不使用这个指令。
5、清屏指令(图19)
图19
清除屏幕显示内容。光标返回屏幕左上角。执行这个指令时需要一定时间。
6、光标归位指令(图20)
图20
光标返回屏幕左上角,它不改变屏幕显示内容。
7、设置cgram地址指令(图21)
图21
这个指令在上面已经介绍过。用法在后面例子中说明。
8、设置ddram地址指令(图22)
图22
这个指令用于设置ddram地址。在对ddram进行读写之前,首先要设置ddram地址,然后才能进行读写。前面我们说过,ddram就是lcd1602的显示存储器。我们要在它上面进行显示,就要把要显示的字符写入ddram。同样,我们想知道ddram某个地址上有什么字符,也要先设置ddram地址,然后将它读出到单片机。
9、读忙信号和地址计数器ac(图23)
图23
这个指令用来读取lcd1602状态。对于单片机来说,lcd1602属于慢速设备。当单片机向其发送一个指令后,它将去执行这个指令。这时如果单片机再次发送下一条指令,由于lcd1602速度较慢,前一条指令还未执行完毕,它将不接受这新的指令,导致新的指令丢失。因此这条读忙指令可以用来判断lcd1602是否忙,能否接收单片机发来的指令。
当bf=1,表示lcd1602正忙,不能接受单片机的指令;当bf=0,表示lcd1602空闲,可以接收单片机的指令。rs=0,表示是指令;rw=1,表示是读取。
这条指令还有一个副产品:即可以得到地址记数器ac的值(address counter)。lcd1602维护了一个地址计数器ac,用来记录下一次读写cgram或ddram的位置。
需要强调的是:这条指令我一次也没有执行成功。很多网友似乎也是这样。好在我们有另外的办法,也就是延时。通过查看每条指令的执行时间,再经过一些试验,可以确定指令的延时。这样就可以在上一条指令执行完毕后再执行下一条指令了。
10、写数据到cgram或ddram指令(图24)
图24
rs=1,数据;rw=0,写。指令执行时,要在db7—db0上先设置好要写入的数据,然后执行写命令。
11、从cgram或ddram读数据指令(图25)
图25
rs=1,数据;rw=1,读。先设置好cgram或ddram的地址,然后执行读取命令。数据就被读入后db7—db0。
实 例
下面我们就以一个实例来结束这篇文章。
先介绍一下背景:
单片机最小系统(扩充了外部ram 62256)。
采用stc89c52rc,晶振22.1184mhz。
以5×8点阵,16×2行,8位数据端口。
首先在第一行显示“i love mcu!”,第二行显示“lcd1602 test!”。延时一段时间,清屏。
然后在第一行显示自定义字符:摄氏温标标志。第二行显示圆周率(pai)标志。再延时一段时间,清屏。
最后在第一行显示“welcome to my blog!”,显示方式是从屏幕右面移入,左面移出。
周而复始(如图26)。
图26
代码如下:
←左右滑动,查看代码→
//file1
#ifndef __zhangtype_h__
#define __zhangtype_h__
#define uint8 unsigned char
#define uint16 unsigned short int
#define uint32 unsigned long int
#define int8 signed char
#define int16 signed short int
#define int32 signed long int
#define uint64 unsigned long long int
#define int64 signed long long int
#endif
//file2
#ifndef __fun_h__
#define __fun_h__
#include “zhangtype.h”
#include
void delay(uint16 time);
#endif
//file3
#include “fun.h”
void delay(uint16 time)
{
while(time--);
}
//file4
#ifndef __1602_h__
#define __1602_h__
#include
#include “zhangtype.h” //变量类型
#include “fun.h” //常用函数
#define setmode 0x38 //16*2显示,5*7点阵,8位数据接口
#define disopen 0x0c //显示开,不显示光标,光标不闪烁
#define dismode 0x06 //读写字符后地址加1,屏显不移动
#define setaddr 0x80 //设置数据地址指针初始值
#define clear 0x01 //清屏,数据指针清零
#define ret 0x02 //回车,数据指针清零
#define port p2 //i/o口
sbit rs = p1^0;
sbit rw = p1^1;
sbit e = p1^2;
void init1602(void); //初始化1602
void write1602_com(uint8 com); //写命令
void write1602_dat(uint8 dat); //写数据
void checkbusy(void); //检查忙
void write1602_one_dat(uint8 x,uint8 y,uint8 dat);
//写一个数据
void write1602_str(uint8 addr,uint8 length,uint8 *pbuf);
//写一个数据串
#endif//
//file5
#include “1602.h”
void write1602_com(uint8 com)
{
e=0;
rs=0; //命令
delay(50); //延时
rw=0; //写
delay(50);
port=com; //端口赋值
delay(50);
e=1; //高脉冲
delay(50);
e=0;
}
void write1602_dat(uint8 dat)
{
e=0;
rs=1; //数据
delay(50); //延时
rw=0; //写
delay(50);
port=dat; //端口赋值
delay(50);
e=1; //高脉冲
delay(50);
e=0;
}
void checkbusy(void)
{
uint8 temp;
rs=0; //命令
rw=1; //读
e=0;
while(1)
{
port=0xff; //端口为输入
e=1; //高脉冲
temp=port;
e=0;
if ((temp&0x80)==0) //检查bf位是否为0
break;
}
}
void init1602(void)
{
write1602_com(setmode); //模式设置
delay(500);
write1602_com(disopen); //显示设置
delay(500);
write1602_com(dismode); //显示模式
delay(500);
write1602_com(clear); //清屏
delay(500);
}
void write1602_one_dat(uint8 x,uint8 y,uint8 dat)
{
x&=0x0f;
y&=0x01;
if(y)
x|=0x40;
x|=0x80;
write1602_com(x);
write1602_dat(dat);
}
void write1602_str(uint8 addr,uint8 length,uint8 *pbuf)
{
uint8 i;
write1602_com(addr);
for(i=0;i
{
write1602_dat(pbuf[i]);
}
}
//file6
/*******************************************************
*名称:主文件(_main.c)
*功能:测试
*日期:2014/09/09
*******************************************************/
#include “1602.h”
#include “fun.h”
uint8 code hot[8]={ //摄氏温度字模
0x10,0x06,0x09,0x08,0x08,0x09,0x06,0x00
};
uint8 code pi[8]={
0x00,0x1f,0x0a,0x0a,0x0a,0x13,0x00,0x00 //pai
};
uint8 code strmcu[]=“i love mcu!”;
uint8 code strtest[]=“lcd1602 test!”;
uint8 code blog[]=“welcome to my blog!”;
uint8 i;
void main()
{
init1602(); //初始化1602
//自定义cgram
write1602_str(0x40,8,hot); //摄氏温标
write1602_str(0x48,8,pi); //pai
write1602_str(0x80,strlen(strmcu),strmcu);
//“i love mcu!”
write1602_str(0x80+0x40,strlen(strtest),strtest);
//“lcd1602 test!”
for(i=0;i《50;i++) //延时一段时间
delay(10000);
write1602_com(clear); //指令执行时间较长
delay(500); //多加一些延时
for(i=0;i《16;i++)
write1602_dat(0);
write1602_com(0xc0); //设置ddram地址
for(i=0;i《16;i++)
write1602_dat(1);
for(i=0;i《50;i++) //延时一段时间
delay(10000);
write1602_com(clear); //指令执行时间较长
delay(500); //多加一些延时
write1602_str(0x80+0x10,strlen(blog),blog);
//写在显示之外
while(1)
{
write1602_com(0x18); //左移
for(i=0;i《20;i++) //延时
delay(10000);
异步电机星形接法和三角形接法有什么不同
数控车床加工螺纹时所受的影响与如何正确的加工方法
拓普微的智能显示模块的优势介绍
中科院丁汉院士:智能制造两大核心技术
关于2GR-FKS/FXS 3.5L V6直喷汽油机的性能开发分析
LCD1602驱动为什么把字符代码写入DDRAM?
传祺GS4改装芬朗汽车音响“打造传奇之声”
普渡机器人走进泰国曼谷 迅速成为异国“新宠儿”
2K屏+双曲面,逆天的iPhone8终于来了!
BOSHIDA AC-DC电源模块元器件的损耗
关于电路保护在太阳能发电上的应用分析和介绍
OnRobot携轻型机器人抓手亮相工博会
小爱同学3.0新添语音切换功能,可随心所欲切换音色
手机厂商刚大面积使用骁龙835 骁龙845却说明年一月就亮相?
未来数字化家庭会如何实现
电路综合实验—直流恒流源设计
油浸式变压器的安装调试方案
回溯清华人与人工智能的40年
四种PLC功能型指令系统
焊接纵向裂纹产生的原因及解决办法