实物图
![](https://i-blog.csdnimg.cn/blog_migrate/aa8a39fdfd1d25190e9796a2dee90fb4.png)
LCD1602电路连接图
![](https://i-blog.csdnimg.cn/blog_migrate/976389acbb8f1e81861ce9e680550b6a.png)
LCD1602主要技术参数
![](https://i-blog.csdnimg.cn/blog_migrate/9e29f8902747d2538462b61cf70d74d3.png)
LCD1602 液晶引脚功能
![](https://i-blog.csdnimg.cn/blog_migrate/f9d87a019c556c24cae8289b9e7b6647.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1e3ebd00b57d4f65bde8ebd8aa26565a.png)
第一行的地址是 0x00H 到 0x27,第二行的地址从 0x40 到 0x67,其中第一行 0x00 到 0x0F是与液晶上第一行 16 个字符显示位置相对应的,第二行 0x40 到 0x4F 是与第二行 16 个字符显示位置相对应的。而每行都多出来一部分,是为了显示移动字幕设置的。
1602 液晶状态字
![](https://i-blog.csdnimg.cn/blog_migrate/6205fbbd3554a86b2dd389dbcb05a0af.png)
最高位表示了当前液晶是不是“忙”,如果这个位是 1 表示液晶正“忙”,禁止我们读写数据或者命令,如果是 0,则可以进行读写。而低 7 位就表示了当前数据地址指针的位置。
LCD1602的四个基本操作时序:
一、读状态:RS=L,R/W=H,E=H。
二、读数据:RS=H,R/W=H,E=H。
三、写指令:RS=L,R/W=L,D0~D7=指令码,E=高脉冲。读完了液晶状态,指令或者数据会通过P0口输出,为释放P0口,读完后应将E置位为0。
四、写数据:RS=H,R/W=L,D0~D7=数据,E=高脉冲。读完了液晶状态,指令或者数据会通过P0口输出,为释放P0口,读完后应将E置位为0。
指令介绍
1、显示模式设置。
写指令 0x38,设置 16x2 显示,5x7 点阵,8 位数据接口。这条指令对我们这个液晶来说是固定的,必须写 0x38,大家仔细看会发现我们的液晶实际上内部点阵是 5x8 的,还有一些1602 液晶还兼容串行通信,用 2 个 IO 口即可,但是速度慢,我们这个液晶就是固定的 0x38模式。
2、显示开/关以及光标设置指令
![](https://i-blog.csdnimg.cn/blog_migrate/795bda96c4af5d20ea0d56dfdb1a778b.png)
3、清屏指令
写入 0x01 表示显示清屏,包含了数据指针清零,所有的显示清零。写入0x02 则仅仅是数据指针清零,显示不清零。
4、RAM 地址设置指令
该指令码的最高位为 1,低 7 位为 RAM 的地址。通常在读写数据之前都要先设置好地址,然后再进行数据的读写操作。
简单实例:
代码:
#include <reg52.h>
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[] = "he bei ruan jian!";
InitLcd1602(); //初始化液晶
LcdShowStr(2,0,str);
LcdShowStr(0,1,"heruan");
while(1);
}
/*等待液晶准备好 */
void LcdWaitReady()
{
unsigned char sta;
P0 = 0xff;
LCD1602_RS = 0; //指令
LCD1602_RW = 1; //读
do{
LCD1602_E = 1; //高脉冲期间读取液晶状态
sta = P0;
LCD1602_E = 0;
}while(sta&0x80); //sta高位为0表示不忙,跳出循环
}
/*向LCD写指令 */
void LcdWriteCmd(unsigned char cmd)
{
LcdWaitReady(); //等待液晶准备好
LCD1602_RS = 0; //指令
LCD1602_RW = 0; //写入
P0 = cmd; //P0赋予指令
LCD1602_E = 1; //高脉冲写入液晶
LCD1602_E = 0;
}
/*向LCD写数据 */
void LcdWriteDat(unsigned char dat)
{
LcdWaitReady(); //等待液晶准备好
LCD1602_RS = 1; //数据
LCD1602_RW = 0; //写入
P0 = dat; //P0赋予指令
LCD1602_E = 1; //高脉冲写入液晶
LCD1602_E = 0;
}
/* 设置显示 RAM 起始地址,(x,y)对应数学坐标第三象限 */
void LcdSetCursor(unsigned char x, unsigned char y)
{
unsigned char addr;
if(y==0) //如果LCD第0行
{
addr = 0x00 + x; //第0行地址从0开始
}
else //如果第一行
{
addr = 0x40 + x; //第一行地址从0x40开始
}
LcdWriteCmd(addr | 0x80); //设置 RAM 地址 最高位必须为1
}
/* 在液晶上显示字符串,str字符串指针 */
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
{
LcdSetCursor(x,y);
while(*str != '\0')
{
LcdWriteDat(*str);
str++;
}
}
/* 初始化 1602 液晶 */
void InitLcd1602()
{
LcdWriteCmd(0x38); //0011 1000 16*2 显示,5*7 点阵,8 位数据接口
LcdWriteCmd(0x0C); //0000 1100 显示器开,光标关闭
LcdWriteCmd(0x06); //0000 0110 文字不动,地址自动+1
LcdWriteCmd(0x01); //0000 0001 清屏
}
调用关系:
![](https://i-blog.csdnimg.cn/blog_migrate/f3860854bcea9841db288d84e9f6910d.png)
LCD读写时序
![](https://i-blog.csdnimg.cn/blog_migrate/f007742935981d35dc2dd15c57ec0580.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c5fb29bb0e193a1ce0aec9ece1d0a3e2.png)
简单实例:
mian()函数:
#include <reg52.h>
bit flag500ms = 0;
unsigned char T0RH = 0;
unsigned char T0RL = 0;
unsigned char code str1[] = "hebeiruanjia";
unsigned char code str2[] = "LCD1602 move";
void ConfigTimer0(unsigned int ms);
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x,unsigned char y,unsigned char *str,unsigned char len);
void main()
{
unsigned char i;
unsigned char index = 0;
unsigned char pdata bufMove1[16+sizeof(str1)+16];
unsigned char pdata bufMove2[16+sizeof(str2)+16];
EA = 1;
ConfigTimer0(10);
InitLcd1602();
for(i=0;i<16;i++) //缓冲区前16位填充空格
{
bufMove1[i]=' ';
bufMove2[i]=' ';
}
for(i=0;i<sizeof(str1)-1;i++) //将显示字符复制到缓冲区中间位置, sizeof(str1)中包含了字符串末尾的\0
{
bufMove1[i+16]=str1[i];
bufMove2[i+16]=str2[i];
}
for(i=(16+sizeof(str1)-1);i<sizeof(bufMove1);i++)
{
bufMove1[i] = ' ';
bufMove2[i] = ' ';
}
while(1)
{
if(flag500ms)
{
flag500ms = 0;
LcdShowStr(0,0,bufMove1+index,16);
LcdShowStr(0,1,bufMove2+index,16);
index++;
if(index>=(16+sizeof(str1)-1))
{
index = 0;
}
}
}
}
void ConfigTimer0(unsigned int ms)
{
unsigned long tmp;
tmp = 65536 - ms/1000/(11059200/12);
T0RH = (unsigned char)(tmp>>8);
T0RL = (unsigned char)tmp;
TMOD &= 0xf0;
TMOD |= 0x01;
TH0 = T0RH;
TL0 = T0RL;
ET0 = 1;
TR0 = 1;
}
void InterruptTimer0() interrupt 1
{
static unsigned char tmr500ms = 0;
TH0 = T0RH;
TL0 = T0RL;
tmr500ms++;
if(tmr500ms >= 50)
{
tmr500ms = 0;
flag500ms = 1;
}
}
Lcd1602.c:
#include <reg52.h>
#define LCD1602_DB P0
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E = P1^5;
/*等待LCD1602准备好*/
void LcdWaitReady()
{
unsigned char sta;
LCD1602_DB = 0xff; //先置高电平
LCD1602_RS = 0; //指令
LCD1602_RW = 1; //读
do{
//高脉冲
LCD1602_E = 1;
sta = LCD1602_DB; //读取LCD1602当前是否忙
LCD1602_E = 0;
}while(sta & 0x80); //当高位为0不忙时,才能跳出循环
}
/*向LCD1602写一字节指令*/
void LcdWriteCmd(unsigned char cmd)
{
LcdWaitReady(); //等待LCD准备好
LCD1602_RS = 0; //指令
LCD1602_RW = 0; //写
LCD1602_DB = cmd; //数据通过p0口写入LCD
LCD1602_E = 1; //高脉冲
LCD1602_E = 0;
}
/*向LCD1602写一字节数据*/
void LcdWriteDat(unsigned char dat)
{
LcdWaitReady();
LCD1602_RS = 1; //数据
LCD1602_RW = 0; //写
LCD1602_DB = dat; //将数据通过p0口送入LCD
LCD1602_E = 1; //高脉冲
LCD1602_E = 0;
}
/*设置RAM*/
void LcdSetCursor(unsigned char x,unsigned char y)
{
unsigned char addr;
if(y==0)
{
addr = 0x00 + x;
}
else
{
addr = 0x40 + x;
}
LcdWriteCmd(addr|0x80);
}
/*液晶显示字符串*/
void LcdShowStr(unsigned char x,unsigned char y,unsigned char *str,unsigned char len)
{
LcdSetCursor(x,y);
while(len--)
{
LcdWriteDat(*str++);
}
}
/*液晶初始化*/
void InitLcd1602()
{
LcdWriteCmd(0x38); //16*2 显示,5*7 点阵,8 位数据接口
LcdWriteCmd(0x0c); //显示器开,光标关闭
LcdWriteCmd(0x06); //文字不动,地址自动+1
LcdWriteCmd(0x01); //清屏
}