1602液晶介绍


大家学习这些电子器件,头脑中要逐渐形成一种意识,不管是我们的单片机,还是74HC138,甚至三极管等等,都是有数据手册的。不管是设计电路还是编写程序,器件的数据手册是我们最好的参考资料,那我们今天来学习 1602,首先就要看它的数据手册。手册大家可以在网上找到,这里我讲的时候只挑手册的重点讲。

首先我们来看一个主要技术参数表格,如表 12-3 所示。


表 12-3 1602 液晶主要技术参数
显示容量
16 x 2 个字符
芯片工作电压
4.5~5.5V
工作电流
2.0mA(5.0V)
模块最佳工作电压
5.0V
字符尺寸
2.95 x 4.35mm (宽乘高)

1602 液晶,从它的名字我们就可以看出它的显示容量,就是可以显示 2 行,每行 16 个字符的液晶。它的工作电压是 4.5V~5.5V,对于这点我们设计电路的时候,直接按照 5V 系统设计,但是保证我们的 5V 系统最低不能低于 4.5V。在 5V 工作电压下测量它的工作电流是 2mA,大家注意,这个 2mA 仅仅是指液晶,而它的黄绿背光都是用 LED 做的,所以功耗不会太小的,一二十毫安还是有的。

1602 液晶一共 16 个引脚,每个引脚的功能,我们都可以在它的数据手册上获得。而这些基本的信息,在我们设计电路和编写代码之前,必须先看明白,如表 12-4 所示。


表 12-4 1602 液晶引脚功能
编号
符号
引脚说明
编号
符号
引脚说明
1
VSS
电源地
9
D2
Data  I/O
2
VDD
电源正极
10
D3
Data  I/O
3
VL
液晶显示偏压信号
11
D4
Data  I/O
4
RS
数据/命令选择端(H/L)
12
D5
Data  I/O
5
R/W
读/写选择端(H/L)
13
D6
Data  I/O
6
E
使能信号
14
D7
Data  I/O
7
D0
Data  I/O
15
BLA
背光源正极
8
D1
Data  I/O
16
BLK
背光源负极

液晶的电源 1 脚 2 脚以及背光电源 15 脚 16 脚,不用多说,正常接就可以了。

3 脚叫做液晶显示偏压信号,大家注意到小黑块没有,当我们要显示一个字符的时候,有的黑点显示,有的黑点就不能显示,这样就可以实现我们想要的字符了。我们这个 3 脚就是用来调整显示的黑点和不显示的之间的对比度,调整好了对比度,就可以让我们的显示更加清晰一些。在进行电路设计实验的时候,通常的办法是在这个引脚上接个电位器,也就是我们初中学过的滑动变阻器。通过调整电位器的分压值,来调整 3 脚的电压。而当产品批量生产的时候,我们可以把我们调整好的这个值直接用简单电路来实现,就如同在我们板子上,我们直接使用的是一个 18 欧的下拉电阻,市面上有的 1602 的下拉电阻大概 1 到 1.5K 也是比较合适的值。

4 脚是数据命令选择端。对于液晶,有时候我们要发送一些命令,让它实现我们想要的一些状态,有时候我们要发给它一些数据,让它显示出来,液晶就通过这个引脚来判断接收到的是命令还是数据,这个引脚我们接到了 ADDR0 上,通过跳线帽和 P1.0 连接在一起。大家注意学会读手册,看到这个引脚描述里:数据/命令选择端,而后跟了括号(H/L),他的意思就是当这个引脚是 H(High)高电平的时候,是数据,当这个引脚是 L(Low)低电平的时候,是命令。

5 脚和 4 脚用法类似,功能是读写选择端。我们既可以写给液晶数据或者命令,也可以读取液晶内部的数据或状态,就是控制这个引脚。因为液晶本身内部有 RAM,实际上我们送给液晶的命令或者数据,液晶需要先保存在缓存里,然后再写到内部的寄存器或者 RAM中,这个就需要一定的时间。所以我们进行读写操作之前,首先要读一下液晶当前状态,是不是在“忙”,如果不忙,我们可以读写数据,如果在“忙”,我们就需要等待液晶忙完了,再进行操作。读状态是常用的,不过读液晶数据我接触的场合没怎么用过,大家了解这个功能即可。这个引脚我们接到了 ADDR1 上,通过跳线帽和 P1.1 连接在一起。

6 脚是使能信号,很关键,液晶的读写命令和数据,都要靠它才能正常读写,我们后边详细讲这个引脚怎么用。这个引脚我们通过跳线帽接到了 ENLCD 上,这个位置的跳线是为了和另外一个 12864 液晶的切换使用而设计的。

7 到 14 引脚就是 8 个数据引脚了,我们就是通过这 8 个引脚读写数据和命令的。我们统一接到了 P0 口上。来看一下开发板上的 1602 接口的原理图,如图 12-1 所示。

图 12-1  1602 液晶接口原理图



1602液晶的读写时序介绍

1602 液晶内部带了 80 个字节的显示 RAM,用来存储我们发送的数据,它的结构如图12-2 所示。

图 12-2  1602 内部 RAM 结构


第一行的地址是 0x00H 到 0x27,第二行的地址从 0x40 到 0x67,其中第一行 0x00 到 0x0F是与液晶上第一行 16 个字符显示位置相对应的,第二行 0x40 到 0x4F 是与第二行 16 个字符显示位置相对应的。而每行都多出来一部分,是为了显示移动字幕设置的。1602 字符液晶是显示字符的,因此它跟 ASCII 字符表是对应的。比如我们给 0x00 这个地址写一个„a‟,也就是十进制的 97,液晶的最左上方的那个小块就会显示一个字母 a。此外,我们本章学过指针了,液晶内部有个数据指针,它指向哪里,我们写的那个数据就会送到相应的那个地址里。

液晶有一个状态字字节,我们通过读取这个状态字的内容,就可以知道 1602 液晶的一些内部情况,如表 12-5 所示。


表 12-5 1602 液晶状态字
bit0~bit6
当前数据的指针的值
bit7
读写操作使能
1:禁止   0:允许

这个状态字节有 8 个位,最高位表示了当前液晶是不是“忙”,如果这个位是 1 表示液晶正“忙”,禁止我们读写数据或者命令,如果是 0,则可以进行读写。而低 7 位就表示了当前数据地址指针的位置。

1602 的基本操作时序,一共有 4 个,这些大家都不需要记住,但是都需要理解,因为我们现在不是为了应付考试,所以不需要你把手册背熟,但是你写程序的时候,打开手册要能看懂如何操作,还要再提醒一句,单片机读外部状态前,必须先保证自己是高电平哦。

我们这里要做 1602 液晶的程序,因此先把用到的总线接口做一个统一声明:#define LCD1602_DB P0sbit LCD1602_RS = P1^0;sbit LCD1602_RW = P1^1;sbit LCD1602_E = P1^5;
1、读状态:RS=L,R/W=H,E=H。这是个很简单的逻辑,就是说,我们直接写

  • LCD1602_DB = 0xFF;

  • LCD1602_RS = 0;

  • LCD1602_RW = 1;

  • LCD1602_E = 1;

  • sta = LCD1602_DB;




这样就把当前液晶的状态字读到了 sta 这个变量中,我们可以通过判断 sta 最高位的值来了解当前液晶是否处于“忙”状态,也可以得知当前数据的指针位置。两个问题,一是如果我们当前读到的状态是“不忙”,那么我们程序可以进行读写操作,如果当前状态是“忙”,那么我们还得继续等待重新判断液晶的状态;问题二,大家可以看我们的原理图,流水灯、数码管、点阵、1602 液晶都用到了 P0 口总线,我们读完了液晶状态继续保持 LCD1602_E 是高电平的话,1602 液晶会继续输出它的状态值,输出的这个值会占据了 P0 总线,干扰到流水灯数码管等其它外设,所以我们读完了状态,通常要把这个引脚拉低来释放总线,这里我们用了一个 do...while 循环语句来实现。

  • LCD1602_DB = 0xFF;

  • LCD1602_RS = 0;

  • LCD1602_RW = 1;

  • do {

  •     LCD1602_E = 1;

  •     sta = LCD1602_DB; //读取状态字

  •     LCD1602_E = 0;  //读完撤销使能,防止液晶输出数据干扰 P0 总线

  • } while (sta & 0x80);

  • //bit7 等于 1 表示液晶正忙,重复检测直到其等于 0 为止





2、读数据:RS=H,R/W=L,E=H。这个逻辑也很简单,但是读数据不常用,大家了解一下就可以了,这里就不详细解释了。

3、写指令:RS=L,R/W=L,D0~D7=指令码,E=高脉冲。

这个在逻辑上没什么难的,只是 E=高脉冲这个问题要解释一下。这个指令一共有 4 条语句,其中前三条语句顺序无所谓,但是 E=高脉冲这一句很关键。实际上流程是这样的:因为我们现在是写数据,所以我们首先要保证我们的 E 引脚是低电平状态,而前三句不管我们怎么写,1602 液晶只要没有接收到 E 引脚的使能控制,它都不会来读总线上的信号的。当通过前三句准备好数据之后,E 使能引脚从低电平到高电平变化,然后 E 使能引脚再从高电平到低电平出现一个下降沿,1602 液晶内部一旦检测到这个下降沿后,并且检测到 RS=L,R/W=L,就马上来读取 D0~D7 的数据,完成单片机写 1602 指令过程。归纳总结我们写了个 E=高脉冲,意思就是:E 使能引脚先从低拉高,再从高拉低,形成一个高脉冲。

4、写数据:RS=H,R/W=L,D0~D7=数据,E=高脉冲。

写数据和写指令是类似的,就是把 RS 改成 H,把总线改成数据即可。

此外要顺便提一句,这里用的 1602 液晶所使用的接口时序是摩托罗拉公司所创立的 6800时序,还有另外一种时序是 Intel 公司的 8080 时序,也有部分液晶模块采用,只是相对来说比较少见,大家知道这么回事即可。

这里还要说明一个问题,就是从这 4 个时序大家可以看出来,1602 液晶的使能引脚 E,高电平的时候是有效,低电平的时候是无效,前面也提到了高电平时会影响 P0 口,因此正常情况下,如果我们没有使用液晶的话,那么程序开始写一句 LCD1602_E=0,就可以避免1602 干扰到其它外设。我们之前的程序没有加这句,是因为我们板子在这个引脚上加了一个15K 的下拉电阻,这个下拉电阻就可以保证这个引脚上电后默认是低电平,如图 12-3 所示。

图 12-3  液晶使能引脚的下拉电阻


如果不加这个下拉电阻,刚开始讲点亮 LED 小灯的时候,我们就得写一句:LCD1602_E=0,可能很多初学者容易弄不明白,所以我们才加了这样一个电路。但是在实际开发过程中,就不必要这样了。如果这是个实际产品,能用软件去处理的,我们就不会用硬件去实现,所以大家在做实际产品的时候,这块电路可以直接去掉,只需要在程序开头多加一条语句即可。



1602液晶指令介绍


与单片机寄存器的用法类似,1602 液晶在使用的时候,我们首先要进行初始的功能配置,1602 液晶有以下几个指令需要了解。


1) 显示模式设置

写指令 0x38,设置 16x2 显示,5x7 点阵,8 位数据接口。这条指令对我们这个液晶来说是固定的,必须写 0x38,大家仔细看会发现我们的液晶实际上内部点阵是 5x8 的,还有一些1602 液晶还兼容串行通信,用 2 个 IO 口即可,但是速度慢,我们这个液晶就是固定的 0x38模式。


2) 显示开/关以及光标设置指令

这里有 2 条指令,第一条指令,一个字节中 8 位,其中高 5 位是固定的 0b00001,低 3位我们分别用 DCB 从高到低表示,D=1 表示开显示,D=0 表示关显示;C=1 表示显示光标,C=0 表示不显示光标;B=1 表示光标闪烁,B=0 表示光标不闪烁。

第二条指令,高 6 位是固定的 0b000001,低 2 位我们分别用 NS 从高到低表示,其中N=1 表示读或者写一个字符后,指针自动加 1,光标自动加 1,N=0 表示读或者写一个字符后指针自动减 1,光标自动减 1;S=1 表示写一个字符后,整屏显示左移(N=1)或右移(N=0),以达到光标不移动而屏幕移动的效果,如同我们的计算器输入一样的效果,而 S=0 表示写一个字符后,整屏显示不移动。


3) 清屏指令

固定的,写入 0x01 表示显示清屏,其中包含了数据指针清零,所有的显示清零。写入0x02 则仅仅是数据指针清零,显示不清零。


4) RAM 地址设置指令

该指令码的最高位为 1,低 7 位为 RAM 的地址,RAM 地址与液晶上字符的关系如上图12-2 所示。通常,我们在读写数据之前都要先设置好地址,然后再进行数据的读写操作。



1602液晶简单显示程序

1602 液晶手册提供了一个初始化过程,由于不检测“忙”位,所以程序比较复杂,而我们总结了一个更加简易方便的过程提供给大家,手册上描述的那个,大家仅仅作为了解就可以了,下面我把程序写出来大家看下,我们的初始化只用了 4 条语句,没有像手册介绍的那么繁琐。
纯文本
复制



  • #include <reg52.h>

  • #define LCD1602_DB P0

  • sbit LCD1602_RS = P1^0;

  • sbit LCD1602_RW = P1^1;

  • sbit LCD1602_E = P1^5;


  • void InitLcd1602();

  • void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);


  • void main(){

  •     unsigned char str[] = "Kingst Studio";

  •     InitLcd1602();

  •     LcdShowStr(2, 0, str);

  •     LcdShowStr(0, 1, "Welcome to KST51");

  •     while (1);

  • }

  •     /* 等待液晶准备好 */

  • void LcdWaitReady(){

  •     unsigned char sta;

  •     LCD1602_DB = 0xFF;

  •     LCD1602_RS = 0;

  •     LCD1602_RW = 1;

  •     do {

  •         LCD1602_E = 1;

  •         sta = LCD1602_DB; //读取状态字

  •         LCD1602_E = 0;

  •     }while (sta & 0x80); //bit7 等于 1 表示液晶正忙,重复检测直到其等于 0 为止

  • }

  • /* 向 LCD1602 液晶写入一字节命令,cmd-待写入命令值 */

  • void LcdWriteCmd(unsigned char cmd){

  •     LcdWaitReady();

  •     LCD1602_RS = 0;

  •     LCD1602_RW = 0;

  •     LCD1602_DB = cmd;

  •     LCD1602_E = 1;

  •     LCD1602_E = 0;

  • }

  • /* 向 LCD1602 液晶写入一字节数据,dat-待写入数据值 */

  • void LcdWriteDat(unsigned char dat){

  •     LcdWaitReady();

  •     LCD1602_RS = 1;

  •     LCD1602_RW = 0;

  •     LCD1602_DB = dat;

  •     LCD1602_E = 1;

  •     LCD1602_E = 0;

  • }

  • /* 设置显示 RAM 起始地址,亦即光标位置,(x,y)-对应屏幕上的字符坐标 */

  • void LcdSetCursor(unsigned char x, unsigned char y){

  •     unsigned char addr;

  •     if (y == 0){ //由输入的屏幕坐标计算显示 RAM 的地址

  •         addr = 0x00 + x; //第一行字符地址从 0x00 起始

  •     }else{

  •         addr = 0x40 + x; //第二行字符地址从 0x40 起始

  •     }

  •     LcdWriteCmd(addr | 0x80); //设置 RAM 地址

  • }

  • /* 在液晶上显示字符串,(x,y)-对应屏幕上的起始坐标,str-字符串指针 */

  • void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str){

  •     LcdSetCursor(x, y); //设置起始地址

  •     while (*str != '\0'){ //连续写入字符串数据,直到检测到结束符

  •         LcdWriteDat(*str++); //先取 str 指向的数据,然后 str 自加 1

  •     }

  • }

  • /* 初始化 1602 液晶 */

  • void InitLcd1602(){

  •     LcdWriteCmd(0x38); //16*2 显示,5*7 点阵,8 位数据接口

  •     LcdWriteCmd(0x0C); //显示器开,光标关闭

  •     LcdWriteCmd(0x06); //文字不动,地址自动+1

  •     LcdWriteCmd(0x01); //清屏

  • }




程序中有详细的注释,结合本节前面的讲解,大家自己分析下,掌握 1602 液晶的基本操作函数。LcdWriteDat(*str++)这行语句中对指针 str 的操作大家一定要理解透彻,先把 str指向的数据取出来用,然后 str 再加 1 以指向下一个数据,这是非常常用的一种简写方式。另外关于本程序还有几点值得提一下:

  • 首先,我们把程序所有的功能都使用函数模块化了,这样非常有利于程序的维护,不管要写一个什么样的功能,只要调用相应的函数就可以了,大家注意学习这种编程方法。

  • 其次,我们使用液晶的习惯,也是用数学上的(x,y)坐标来进行屏幕定位,但与数学坐标系不同的是,液晶的左上角的坐标是 x=0,y=0,往右边是 x+偏移,下边是 y+偏移。

  • 第三,第一次接触多个参数传递的函数,而且还带了指针类型的参数,所以多留心熟悉一下。

  • 第四,读写数据和指令程序,每次都必须进行“忙”判断。

  • 第五,领略一下指针在这个地方的巧妙用法,你可以尝试不用指针改写程序试试,感受一下指针的优势。