DS1602液晶显示
LCM1602是最常见的点阵字符型液晶显示模块,由液晶板、控制器HD44780、驱动器HD44100及若干电阻电容组成。
1602 液晶,从它的名字我们就可以看出它的显示容量,就是可以显示 2 行,每行 16 个字符的液晶,即其液晶板上分2行,每行排列着16个5×7的点阵,专门用于显示字母、数字以及符号。
///
1602实物图:
0它可与8位或4位微处理器连接;内建CGROM可提供160种工业标准字符,包括全部大小写字母、阿拉伯数字、日文假名以及32个特殊字符或符号的显示;内建CGRAM可根据用户需要,由用户自行设计定义8个字符或符号;
先了解以下几个概念:CGROM、CGRAM、DDRAM和AC
CGROM是只读字符发生器,存放192个字符的点阵数据,每个字符都有一个唯一的代码,在0x20~0x7f区间代码值就是该字符的ASCII码,图12-9为LCM1602的内置字符集。
CGRAM是自定义字符发生器,可用于存放用户自定义字符的点阵,只有64个字节最多能定义8个字符,这8个字符也有对应的代码,0x00对应第一个自定义字符,0x07对应最后一个自定义字符,0x08~0x0f与0x00~0x07指向相同的字符。
DDRAM是显示存储器,有80个字节,内部存放的是显示内容的代码。
AC是DDRAM和CGRAM共用的地址指针计数器,另外AC也作为光标和闪烁位置指针。
1602 液晶一共 16 个引脚:
左起为1脚,各引脚功能如下:
1脚:VSS为模块电源地,接地;
2脚:VDD为模块电源正,接+5V;
3脚:VEE为对比度调节引脚,接电源正时对比度最差,接电源地时对比度最高,通常接一个10K电位器(滑动变阻器)到地(可手动调节)调节显示对比度;
4脚:RS为寄存器选择位,输入脚,“1”数据寄存器,“0”命令寄存器;
5脚:RW为读写操作选择位,输入脚,“1”读操作,“0”写操作;
6脚:E为使能信号,输入脚,读操作时上升沿有效,写操作时下降沿有效;
7~14脚:DB0~DB7双向数据线,DB7还做忙闲标志位使用,4线工作方式下仅DB4~DB7有效;
15脚:BL1背光电源正,接+5V;
16脚:BL2背光电源地,接地。
重要引脚ps:
(
4 脚是数据命令选择端。对于液晶,有时候我们要发送一些命令,让它实现我们想要的一些状态,有时候我们要发给它一些数据,让它显示出来,液晶就通过这个引脚来判断接收到的是命令还是数据,这个引脚我们接到了 ADDR0 上,通过跳线帽和 P1.0 连接在一起。读手册看到这个引脚描述里:数据/命令选择端,而后跟了括号(H/L),他的意思就是当这个引脚是 H(High)高电平的时候,是数据,当这个引脚是 L(Low)低电平的时候,是命令。
5 脚和 4 脚用法类似,功能是读写选择端。我们既可以写给液晶数据或者命令,也可以读取液晶内部的数据或状态,就是控制这个引脚。因为液晶本身内部有 RAM,实际上我们送给液晶的命令或者数据,液晶需要先保存在缓存里,然后再写到内部的寄存器或者 RAM中,这个就需要一定的时间。所以我们进行读写操作之前,首先要读一下液晶当前状态,是不是在“忙”,如果不忙,我们可以读写数据,如果在“忙”,我们就需要等待液晶忙完了,再进行操作。读状态是常用的,大家了解这个功能即可。这个引脚我们接到了 ADDR1 上,通过跳线帽和 P1.1 连接在一起。
6 脚是使能信号,很关键,液晶的读写命令和数据,都要靠它才能正常读写,我们后边详细讲这个引脚怎么用。这个引脚我们通过跳线帽接到了 ENLCD 上,这个位置的跳线是为了和另外一个 12864 液晶的切换使用而设计的。
7 到 14 引脚就是 8 个数据引脚了,我们就是通过这 8 个引脚读写数据和命令的。
)
、、、、、、、、、、、、、、、、
1602 液晶的读写时序介绍:
第一行的地址是 0x00H 到 0x27(40个,0x00-0x0f有16个,依此类推,共16+16+8=40),第二行的地址从 0x40 到 0x67,其中第一行 0x00 到 0x0F是与液晶上第一行 16 个字符显示位置相对应的,第二行 0x40 到 0x4F 是与第二行 16 个字符显示位置相对应的。而每行都多出来一部分,是为了显示移动字幕设置的。 1602 字符液晶是显示字符的,因此它跟 ASCII 字符表是对应的。比如我们给 0x00 这个地址写一个‘a’,也就是十进制的 97,液晶的最左上方的那个小块就会显示一个字母 a。液晶内部有个数据指针,它指向哪里,我们写的那个数据就会送到相应的那个地址里。
液晶有一个状态字字节,我们通过读取这个状态字的内容,就可以知道 1602 液晶的一些内部情况,如表 12-5 所示。
这个状态字节有 8 个位,最高位表示了当前液晶是不是“忙”,如果这个位是 1 表示液晶正“忙”,禁止我们读写数据或者命令,如果是 0,则可以进行读写。
读状态的判忙语句:
void LcdWaitReady()
{
unsigned char sta;
LCD1602_DB = 0xFF;
LCD1602_RS = 0; //0 为写命令 1为写数据
LCD1602_RW = 1; //1 为读 0为写
do {
LCD1602_E = 1;
sta = LCD1602_DB; //读取状态字
LCD1602_E = 0;
} while (sta & 0x80);
//bit7等于1表示液晶正忙,重复检测直到其等于0为止
}
为什么要判断忙闲?
因为流水灯、数码管、点阵、 1602 液晶都用到了 P0 口总线,我们读完了液晶状态继续保持 LCD1602_E 是高电平的话, 1602 液晶会继续输出它的状态值,输出的这个值会占据了 P0 总线,干扰到流水灯数码管等其它外设,所以我们读完了状态,通常要把这个引脚拉低来释放总线。
读数据: RS=H, R/W=L, E=H。
写指令: RS=L, R/W=L, D0~D7=指令码, E=高脉冲。
/* 向 LCD1602 液晶写入一字节命令, cmd-待写入命令值 */
void LcdWriteCmd(unsigned char cmd)
{
LcdWaitReady();
LCD1602_RS = 0;
LCD1602_RW = 0;
LCD1602_DB = cmd;
LCD1602_E = 1;
LCD1602_E = 0;
}
写数据: RS=H, R/W=L, D0~D7=数据, E=高脉冲。
/* 向 LCD1602 液晶写入一字节数据, dat-待写入数据值 */
void LcdWriteDat(unsigned char dat)
{
LcdWaitReady();
LCD1602_RS = 1;
LCD1602_RW = 0;
LCD1602_DB = dat;
LCD1602_E = 1;
LCD1602_E = 0;
}
ps:
E=高脉冲这个问题要解释一下。这个指令一共有 4 条语句,其中前三条语句顺序无所谓,但是 E=高脉冲这一句很关键。实际上流程是这样的:因为我们现在是写数据,所以我们首先要保证我们的 E 引脚是低电平状态,而前三句不管我们怎么写, 1602 液晶只要没有接收到 E 引脚的使能控制,它都不会来读总线上的信号的。当通过前三句准备好数据之后, E 使能引脚从低电平到高电平变化,然后 E 使能引脚再从高电平到低电平出现一个下降沿,1602 液晶内部一旦检测到这个下降沿后,并且检测RS=L, R/W=L,就马上来读取 D0~D7的数据,完成单片机写1602指令过程。归纳总结我们写了个E=高脉冲,意思就是: E 使能引脚先从低拉高,再从高拉低,形成一个高脉冲。
而读状态和读命令都是只需要E=H即可。
即读操作时上升沿有效,写操作时下降沿有效。
1602 液晶简单实例
#include
#define LCD1602_DB P0
***it LCD1602_RS = P1^0;
***it LCD1602_RW = P1^1;
***it 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 != '