1602液晶屏(LCD)
1602液晶屏可以同时显示16*2即32个字符(16列2行)。我用的1602是基于HD44870的,HD44780内置了DDRAM、CGROM和CGRAM:
DDRAM(Display Data RAM):显示数据存放RAM单元,就是显示数据RAM,把字符写在DDRAM上才可以显示出来,用来寄存待显示的字符代码。共80个字节;
CGROM(Character Generator ROM):字符产生器单元,CGROM相当于库,常用字符的存储库;CGRAM(Character Generator RAM):自定义字符产生器单元,自定义符号的话会用到CGRAM。
其中RAM掉电容易丢失。我使用的是16脚1602,引脚说明表:
第三脚为液晶显示器对比度调整端,接正电源时最弱,接地时对比度最高。对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器(在蓝桥杯单片机开发板上是在LCD左侧的电位器)调整对比度。
控制指令:1602液晶显示屏内部的控制器共有11条控制指令,如表:
时序
在使用1602时,时序也是其关键部分,它的读和写操作都是基于时序图进行操作。读操作时序写操作时序
由于51单片机的操作都是us级的,而1602时序图中时间都是ns级的,所以可以不使用延时函数但是在一些高速的单片机上我们要注意加延时,我们可以就依据上面的指令表格进行读写操作。液晶屏主要时通过E、RS、R/W三个引脚进行控制。
使用1602的基本步骤:
初始化液晶屏——>指定显示的位置——>在指定位置显示
1.初始化液晶屏根据上面的指令表写出LCD的初始化程序
void Lcd_Init()//初始化
{ LcdWrCmd(0X38);//0011 1000
//指令置功能,DL=1用八位数据总线DB7-DB0;N=1设置为两行显示模式;F=0,5*7点阵+游标显示
LcdWrCmd(0X0C);//0000 1100
//指令为开/关控制,D=1 开显示;C=0 游标不显示
LcdWrCmd(0X06);//0000 0011
//指令为游标或显示移位,S/C=0,R/L=1,游标向左移动,AC自动加1
LcdWrCmd(0X01);//0000 0001清显示操作
}
2.指定显示的位置根据指令8,DDRAM位址设置指令设置DDRAM位置指针,它将DDRAM存储显示字符法日字符码的首地址ADD6~ADD0送入AC中,于是显示字符的字符吗就可以写入DDRAM中或从DDRAM 中读取。
addr需要|上0X80的原因是要求DDRAM中显示地址时要求最高位DB7(最高位)恒定为高电平1。
指定显示位置的代码
void Lcd_show(uchar x,uchar y)
//显示函数 指定显示的位置
{ uchar addr;
//X表示第几个开始显示,Y表示第几行开始显示 if(0 == y)//和y == 0是一样的,但0 =y会报错
addr = 0x00+x; else
addr = 0x40+x; LcdWrCmd(addr|0x80);//地址设置
}```
要显示字符是要线输入显示字符地址,也就是告诉模块在哪显示字符,写命令程序代码
```javascript
void LcdWrCmd(uchar cmd)//写命令程序 根据时序图
{
LcdBusy();//判断是否忙
LcdRS = 0;//选择指令寄存器
LcdRW =0;//选择写
LcdDB = cmd;//把命令字送入P0
LcdEN =1;//使能线电平变化,命令送入1602的8位数据口
LcdEN =0;
}
写数据(内容)函数代码和写命令类似只有RS管脚不同,这根据上面指令表格来写
void LcdWrDat(uchar Dat)//写数据程序
{
LcdBusy();
LcdRS = 1;
LcdRW = 0;
LcdDB = Dat;//把要显示的数据送入P0
LcdEN = 1;
LcdEN = 0;
}
液晶显示模块是一个慢显示器件,所以在执行每条指令之前一定要确认模块的忙标志为低电平,表示不忙,否则此指令失效。所以我们需要一个判断模块是否为忙的函数
void LcdBusy()//判断忙函数
{ LcdDB = 0XFF;
LcdRS = 0;//读命令 LcdRW = 1;
LcdEN = 1; while(LcdDB&0x80);//1:表示忙;0:表示闲
LcdEN = 0;}
3.在指定位置显示我们徐要用到数组和指针,将想要显示的内容放在数组中,然后用取值符*将数组中存放的内容依次取出显示
Lcd_show(2,0);//定位
while(*str1 !='\0')//判断str1取出的值是否为\0,*为取值符 {
LcdWrDat(*str1++);//依次取值直到\0 }
Lcd_show(0,1);//定位 while(*str2 !='\0')//判断str2取出的值是否为\0
{ LcdWrDat(*str2++);//依次将数值取出,会显示出字符串
}
实验部分
首先时将LCD1602接到蓝桥杯开发板上,把代码中的RS、R/W、E对应着原理图定义为相应的管脚。
注意
- 注意自己的芯片时钟周期是否需要加延时;
- 注意自己的接线有无错误;
- 实验过程中可能出现无法下载的情况,应该是串口被占用了,LCD接着VCC上电了,LCD数据传输会占用资源的;
- 如果发现没有显示,扭动控制第三脚VL的电位器,可以调节对比度,我一开始没看到显示内容一直以为是自己代码有问题,扭动电位器后成功显示了;
- 如果出现蜂鸣器无法关闭,数码管和LED灯不对现象,改动锁存器的代码。
最后附上完整代码.和实验效果图
//显示两行字符串
#include "reg52.h"
#define uchar unsigned char
#define uint unsigned int
#define LcdDB P0
sbit LcdRS = P2^0;
sbit LcdRW = P2^1;
sbit LcdEN = P1^2;//看原理图
sbit buzz=P0^6;
sbit A1=P2^5;
sbit B1=P2^6;
sbit C1=P2^7;
uchar code table1[] = "I LOVE MCU";
uchar code table2[] = "I LOVE shanwei";
uchar *str1 = table1;
uchar *str2 = table2;
void Lcd_Init();
void LcdWrCmd(uchar cmd);
void LcdWrDat(uchar Dat);
void LcdBusy();
void Lcd_show(uchar X,uchar Y);
void buzzoff()
{A1=1;
B1=0;
C1=1;
buzz=0;
}
void Delay(uint t)
{
while(t--);
}
void main()
{
buzzoff();
A1=0;
B1=0;
C1=0;
Lcd_Init();//初始化
Lcd_show(2,0);//定位
while(*str1 !='\0')//判断str1取出的值是否为\0,*为取值符
{
LcdWrDat(*str1++);//依次取值直到\0
}
Lcd_show(0,1);//定位
while(*str2 !='\0')//判断str2取出的值是否为\0
{
LcdWrDat(*str2++);//依次将数值取出,会显示出字符串
}
while(1)
{
}
}
void Lcd_Init()//初始化
{
LcdWrCmd(0X38);//0011 1000
//指令置功能,DL=1用八位数据总线DB7-DB0;N=1设置为两行显示模式;F=0,5*7点阵+游标显示
LcdWrCmd(0X0C);//0000 1100
//指令为开/关控制,D=1 开显示;C=0 游标不显示
LcdWrCmd(0X06);//0000 0011
//指令为游标或显示移位,S/C=0,R/L=1,游标向左移动,AC自动加1
LcdWrCmd(0X01);//0000 0001清显示操作
}
void LcdWrCmd(uchar cmd)//写命令程序 根据时序图
{
LcdBusy();//判断是否忙
LcdRS = 0;//选择指令寄存器
LcdRW =0;//选择写
LcdDB = cmd;//把命令字送入P0
LcdEN =1;//使能线电平变化,命令送入1602的8位数据口
LcdEN =0;
}
void LcdWrDat(uchar Dat)//写数据程序
{
LcdBusy();
LcdRS = 1;
LcdRW = 0;
LcdDB = Dat;//把要显示的数据送入P0
LcdEN = 1;
LcdEN = 0;
}
void LcdBusy()//判断忙函数
{
LcdDB = 0XFF;
LcdRS = 0;//读命令
LcdRW = 1;
LcdEN = 1;
while(LcdDB&0x80);//1:表示忙;0:表示闲
LcdEN = 0;
}
void Lcd_show(uchar x,uchar y)//显示函数 指定显示的位置
{
uchar addr;
//X表示第几个开始显示,Y表示第几行开始显示
if(0 == y)//和y == 0是一样的,但0 =y会报错
addr = 0x00+x;
else
addr = 0x40+x;
LcdWrCmd(addr|0x80);//地址设置
}