LCD1602液晶显示概述
LCM1602是最常见的点阵字符型液晶显示模块,由液晶板、控制器HD44780、驱动器HD44100及若干电阻电容组成。1602 液晶,可以显示2行,每行有40个字符 ,但由于显示容量的原因,每行只能显示16个字符(可以通过移动光标或者屏幕显示后面的字符,下面再讲),每行排列着40个5×8的点阵,专门用于显示字母、数字以及符号。
一般来说,LCD1602有16条引脚,据说还有14条引脚的,与16脚的相比缺少了背光电源A(15脚)和地线K(16脚)。
1602液晶模块引脚说明:
第1脚:VSS为地电源。
第2脚:VDD接5V正电源。
第3脚:VL为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度。
第4脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。
第5脚:R/W为读写信号线,高电平时进行读操作,低电平时进行写操作。当RS和R/W共同为低电平时可以写入指令或者显示地址,当RS为低电平R/W为高电平时可以读忙信号,当RS为高电平R/W为低电平时可以写入数据。
第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。
第7~14脚:D0~D7为8位双向数据线。
第15脚:背光源正极。
第16脚:背光源负极
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=高脉冲。输出:无。
读操作指令:
写操作指令:
时序时间参数:
1602指令集(11个):
1、清屏指令(clear display) RS=0 ,R/w=0, 01H
功能:
- 清除液晶显示器,即将DDRAM中的内容全部填入20H(空白字符)
- 光标撤回显示屏左上方
- 将地址计数器(AC)设为0,
-
光标移动方向为从左向右,并且DDRAM的自增量为1(I/D=1).
2、光标归位指令(Return Home)RS=0 ,R/w=0, 0000_001X
功能:
- 将地址计数器(AC)设为00H,
-
DDRAM内容保持不变,光标移至左上脚
3、进入模式设置指令(Entry Mode Set)
功能:
- 设 定 每 次 定 入 1 位 数据 后 光 标 移 位 方 向 并且设 定 次 写 入 一 个 字符是 否移动 。
-
I/D = 0 光标左移,DDRAM地址自增1 I/D = 1 光标右移,DDRAM地址自增1 (当从CGRAM中读取或写入数据时,CGRAM操作与DDRAM相同)
SH = 0 且 DDRAM是读操作(CGRAM读或写),整个屏幕不移动
SH = 1 且 DDRAM是写操作,整个屏幕移动,移动方向由I/D决定
4、显示开关控制(Display ON/OFF Control)
功能:
- D = 1 , 显示功能开 D = 0 , 显示功能关,但是DDRAM中的数据依然保留
- C = 1 , 有光标 C = 0 , 没有光标
-
B = 1 , 光标闪烁 B = 0 , 光标不闪烁
5、设置显示屏或光标移动方向的指令
功能:
- 整屏的移动或光标移动
- S/C =0 R/L =0 光标左移 ,地址计数器减1(即显示内容和光标一起左移)
- S/C =0 R/L =1 光标右移 ,地址计数器加1(即显示内容和光标一起右移)
- S/C =1 R/L =0 显示内容左移 ,光标不移动
-
S/C =1 R/L =1 显示内容右移 ,光标不移动
6、功能设定指令
功能:
- 设定数据总线位数、显示的行数及字形。
- DL=1 ,数据总线是8位 DL=0 ,数据总线是4位
- N =0 ,显示一行 N=1 ,显示两行
- F =0 , 58 点阵/字符 F=1 ,511点阵/字符
7、设定 CGRAM地址指令
功能:
- 设定下一个要存入数据的CGRAM地址
- DB5DB4DB3为字符号,即将显示该字符用到的字符地址
-
DB2DB1DB0为行号
8、设置DDRAM地址
功能:
- 设置DDRAM地址用于在屏幕上显示
-
DDRAM的地址与显示屏对照关系(N=LOW)
注意:因为DB7是固定为1,所以DDRAM的地址与屏幕的对照关系要加上0x80才是真实的对照地址。
9、读取忙信号或AC地址指令
功能:
- 如果BF=1 忙碌,无法接收数据或指令
- BF=0可以接收数据、指令
-
读取地址计数器的内容
10、向DDRAM或CGRAM写入数据
功能:
- 写指令 输 入 : RS=L , RW=L , E= 下 降沿脉冲 , DB0 ~ DB7= 指令
- 写数据 输 入 : RS=H , RW=L , E= 下 降沿脉冲 , DB0 ~ DB7= 数据
11、从DDRAM或者CGRAM读数据
功能:
- 读状态 输 入 : RS=L , RW=H , E=H 输出: DB0 ~ DB7= 状态字
-
读数据 输 入 : RS=H , RW=H , E=H 输出: DB0 ~ DB7= 数据
自定义字符显示
想要了解1602的自定义字符显示,就要先了解DDRAM/CGROM/CGRAM,
DDRAM:就是显示数据RAM,用来寄存待显示的字符代码。共80个字节,其地址和屏幕的对应关系如下:
DDRAM相当于计算机的显存,我们为了在屏幕上显示字符,就把字符代码送入显存,这样该字符就可以显示在屏幕上了。同样LCD1602共有80个字节的显存,即DDRAM。但LCD1602的显示屏幕只有16×2大小,因此,并不是所有写入DDRAM的字符代码都能在屏幕上显示出来,只有写在上图所示范围内的字符才可以显示出来,写在范围外的字符不能显示出来。这样,我们在程序中可以利用下面的“光标或显示移动指令”使字符慢慢移动到可见的显示范围内,看到字符的移动效果。
前面说了,为了在液晶屏幕上显示字符,就把字符代码送入DDRAM。例如,如果想在屏幕左上角显示字符‘A’,那么就把字符‘A’的字符代码41H写入DDRAM的00H+0x80地址处即可。至于怎么写入,后面会有说明。
那么为什么把字符代码写入DDRAM,就可以在相应位置显示这个代码的字符呢?我们知道,LCD1602是一种字符点阵显示器,为了显示一种字符的字形,必须要有这个字符的字模数据,什么叫字符的字模数据,看看下面的这个图就明白了,下图是‘A’的字模。
在LCD1602模块上固化了字模存储器,就是CGROM和CGRAM,HD44780内置了192个常用字符的字模,存于字符产生器CGROM(Character Generator ROM)中,另外还有8个允许用户自定义的字符产生RAM,称为CGRAM(Character Generator RAM)。下图说明了CGROM和CGRAM与字符的对应关系。
从ROM和RAM的名字我们也可以知道,ROM是早已固化在LCD1602模块中的,只能读取;而RAM是可读写的。也就是说,如果只需要在屏幕上显示已存在于CGROM中的字符,那么只须在DDRAM中写入它的字符代码就可以了,比如数据‘A’,只需在DDRAM的显示地址上输入‘A’即可;但如果要显示CGROM中没有的字符,比如摄氏温标的符号,那么就只有先在CGRAM中定义,然后再在DDRAM显示地址写入这个自定义字符的存放地址即可。和CGROM中固化的字符不同,CGRAM中本身没有字符,所以要在DDRAM中写入某个CGROM不存在的字符,必须在CGRAM中先定义后使用。程序退出后CGRAM中定义的字符也不复存在,下次使用时,必须重新定义。
可以使用上面提到的1602指令集的第七条指令,设置CGRAM地址,由于DB7和DB6固定为0和1,所以设置CGRAM的地址是从0x40H开始,DB5DB4DB3设置字符在CDRAM中的存放地址,DB2DB1DB0设置字符的字模存放地址,在5x8的字符点阵中,一个字符显示需要8个字节存放字模。
比如摄氏度℃字符,字模为{0x10,0x07,0x08,0x08,0x08,0x08,0x07,0x00},使用指令7设置摄氏度字符的存放位置为 01-000-xxx,000是设置摄氏度在CGRAM中的存放地址为0x00,显示摄氏度时只需在DDRAM中输入0x00即可,xxx是设置摄氏度字模存放的地址,摄氏度字模存放的8个地址为:0100000,01000001,01000010,01000011,01000100,01000101,01000110,01000111,对应的是0x40—0x47这8个地址。由此可得8个自定义字符的显示地址和字模的存放地址对应关系如下:
自定义字符存放地址 | 字模存放地址 |
---|---|
0x00H | 0x40H-0x47H |
0x01H | 0x48H--0x4fH |
0x02H | 0x50H-0x57H |
0x03H | 0x58H-0x5fH |
0x04H | 0x60H-0x67H |
0x05H | 0x68H-0x6fH |
0x06H | 0x70H-0x77H |
0x07H | 0x78H-0x7fH |
LCD1602.h头文件
#ifndef _LCD1602_H
#define _LCD1602_H
#include "reg52.h"
#include <string.h>
#include <intrins.h>
#define LCDPIN4
#define LCD1602 P0
sbit RS = P2^6;
sbit RW = P2^5;
sbit EN = P2^7;
#define LCD_SCREEN_CLR 0x01 //清屏
#define LCD_CURSOR_RST 0x02 //光标复位
#define LCD_DIS_CUR_BLK_ON 0x0F //显示开 有光标,光标闪烁
#define LCD_DIS_CUR_ON 0x0E //显示开 有光标,光标不闪烁
#define LCD_DIS_ON 0x0C //显示开 关光标,光标不闪烁
#define LCD_DIS_OFF 0x08 //显示关
#define LCD_CURSOR_RIGHT 0x06 //光标右移,显示不动
#define LCD_CURSOR_LEFT 0x04 //光标左移,显示不动
#define LCD_DIS_MODE_LEFT 0x07 //AC自增,画面左移
#define LCD_DIS_MODE_RIGHT 0x05 //AC自减,画面右移
#define LCD_POWER_PIN8 0x38 //功能设置指令,数据总线为8位,2行
#define LCD_POWER_PIN4 0x28 //功能设置指令,数据总线为4位,2行
#define LCD_CUR_MOVE_LEFT 0x10 //光标左移
#define LCD_CUR_MOVE_RIGHT 0x14 //光标右移
#define LCD_DIS_MOVE_RIGHT 0x18 //显示右移
#define LCD_DIS_MOVE_LEFT 0x1c //显示左移
void DelayXms(unsigned int num);
void LCDInit();
void LCDWriteData(unsigned char dat);
void LCDWriteCmd(unsigned char cmd);
void LCDShowStr(unsigned char x, unsigned char y, unsigned char* str);
void LCDSetChar(unsigned char x, unsigned char y, unsigned char pos,unsigned char* str);
#endif
LCD1602.c
#include "LCD1602.h"
void DelayXms(unsigned int num) //@12.000MHz
{
unsigned char i, j;
for(;num > 0; num--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
void LCDReadBusy() //忙信号检测
{
unsigned char state, i;
LCD1602 = 0xff; //io口置1,做输入
RS=0;
RW=1;
i=0;
do
{
EN=1;
state = LCD1602;
EN = 0;
i++;
if(i > 100)
break;
}while(state & 0x80); // 如果LCD1602最高位为1,说明液晶显示器忙
}
#ifndef LCDPIN4
//发送命名
void LCDWriteCmd(unsigned char cmd)
{
LCDReadBusy();
RS = 0;
RW = 0;
LCD1602 = cmd;
EN = 1;
EN = 0;
}
//发送数据
void LCDWriteData(unsigned char dat)
{
LCDReadBusy();
RS = 1;
RW = 0;
LCD1602 = dat;
EN = 1;
EN = 0;
}
//初始化屏幕
void LCDInit()
{
LCDWriteCmd(LCD_POWER_PIN8);
LCDWriteCmd(LCD_DIS_ON);
LCDWriteCmd(LCD_CURSOR_RIGHT);
LCDWriteCmd(LCD_SCREEN_CLR);
}
#else
void LCDWriteCmd(unsigned char cmd)
{
LCDReadBusy();
RS = 0;
RW = 0;
LCD1602 = cmd;
//DelayXms(1); //这里延时根据实际情况是否需要
EN = 1;
EN = 0;
LCD1602 = cmd<<4;
//DelayXms(1);
EN =1;
EN =0;
}
void LCDWriteData(unsigned char dat)
{
LCDReadBusy();
RS = 1;
RW = 0;
LCD1602 = dat;
//DelayXms(1); //这里延时根据实际情况是否需要
EN = 1;
EN = 0;
LCD1602 = dat<<4;
//DelayXms(1);
EN = 1;
EN = 0;
}
void LCDInit()
{
// LCDWriteCmd(0x33);
// DelayXms(5);
LCDWriteCmd(0x32); //0x32是4位总线必须要设置的
LCDWriteCmd(LCD_POWER_PIN4);
LCDWriteCmd(LCD_DIS_ON);
LCDWriteCmd(LCD_CURSOR_RIGHT);
LCDWriteCmd(LCD_SCREEN_CLR);
}
#endif
//字符在屏幕上显示的位置
void LCDSetPos(unsigned char x, unsigned char y)
{
if(y == 0)
{
LCDWriteCmd(0x80+x);
}
else
{
LCDWriteCmd(0x80 |(0x40+x));
}
}
//构造和显示自定义字符 x,y为在屏幕上显示的位置,pos为在CGRAM存放字符的位置,str为字符的字模
void LCDSetChar(unsigned char x, unsigned char y, unsigned char pos, unsigned char str[])
{
unsigned char i;
//构造自定义字符,确定CGRAM位置
for(i = 0; i < 8;i++)
{
LCDWriteCmd(0x40 + pos*8 + i); //确定字模位置
LCDWriteData(*(str+i)); //写入字模
}
//显示
LCDSetPos(x, y);//设置显示位置 DDRAM
LCDWriteData(0x00+pos);
}
//在屏幕上显示字符
void LCDShowStr(unsigned char x, unsigned char y, unsigned char* str)
{
LCDSetPos(x ,y);
while(*str != '\0')
{
LCDWriteData(*str++);
}
}
main.c
#include "LCD1602.h"
void main()
{
/*****************************以下是对液晶显示4位和8位数据总线的操作******/
//LCDInit();
//DelayXms(2); //这里延时是必须的,让液晶显示器初始化完成再写数据
// #ifndef LCDPIN4
// unsigned char *str="hello lcd PIN8";
// #else
// unsigned char *str="hello lcd PIN4";
// #endif
// LCDShowStr( 2,0,str);
// while(1);
//
/*****************************以下是显示自定义字符******/
// unsigned char str1[] = {0x10,0x07,0x08,0x08,0x08,0x08,0x07,0x00};//自定义字符 ℃ 的字模
// LCDInit();
// DelayXms(2);
// LCDSetChar(2, 0, 0, str1);
// while(1);
/*****************************以下是两种移屏操作******/
unsigned char i=0;
unsigned char str[]="HELLO LCD 1602";
LCDInit();
DelayXms(10);
//第一种,初始设置字符的输入模式,整屏左移
// LCDWriteCmd(LCD_DIS_MODE_LEFT);
// while(1)
// {
// LCDWriteCmd(0x80);
// for(i =0; i < strlen(str); i++)
// {
// LCDWriteData(str[i]);
// DelayXms(600);
//
// }
// }
//第二种,字符移动,光标不动
LCDWriteCmd(0x80);
for(i =0; i < strlen(str); i++)
{
LCDWriteData(str[i]);
}
while(1)
{
LCDWriteCmd(LCD_DIS_MOVE_LEFT); //第二种
DelayXms(600);
}
}