图1 LCD1602显示电路
前面我们已经学习了使用数码管显示数字和简单的字符,但是数码管显示内容有很大的局限性,例如显示的字符数量有限,并且显示复杂的字符形状时,显示的效果不是很好。例如使用8段数码管显示字符“S”时,效果如下图。可以看出这个“S”与数字“5”很像。事实上,数码管显示字符的段码表里面,字符“S”和数字“5”的段码是一样的。
数码管显示数字和字符
在本例中,我们使用显示内容更为丰富的液晶来显示字符和数字。使用LCD1602液晶显示“laomashitu mcu”和“0123456789”两行字符,从而熟悉和掌握LCD1602液晶的原理、驱动方法和编程实现。
本实例有两个目的:
1、了解LCD1602液晶的显示原理、驱动电路设计;
2、掌握51单片机编程控制LCD1602液晶显示字符和数字的方法。
1.2. 设计思路本实例先从LCD1602液晶的工作原理入手,根据其工作原理在proteus中画出LCD1602液晶的工作电路,然后使用51单片机编程实现LCD1602液晶的显示控制。
1.3. 基础知识关于液晶,想必大家都很熟悉了。从液晶的显示内容可分为段式、字符式、点阵式3种。其中段式液晶的显示内容与数码管一样,原理和驱动方法采用数码管的原理和驱动方法即可。本例中我们使用的LCD1602液晶是字符式液晶。点阵式液晶在后面的例子中再学习。
首先从字面来理解LCD1602液晶。16代表该LCD每行可显示16个字符,02表示该液晶共有两行。因此LCD1602液晶一共可显示32个字符。
下面两个图分别是LCD1602液晶的实物图和引脚图。
LCD1602液晶实物图
LCD液晶引脚图
我们知道单片机编程主要是根据不同的条件检测端口的电平高低(模拟功能除外),控制端口输出高低电平。所以,对于控制lcd1602液晶,了解其引脚功能是编程的必要条件。
1.3.1. Lcd1602液晶的引脚功能VSS:电源地
VDD:电源
VEE:液晶对比度调节引脚,可以用一个电位器连接到VDD以调节对比度。
RS:寄存器选择引脚,当RS=0时,选择指令寄存器;RS=1时,选择数据存储器。
R/W:读、写控制引脚。R/W=0时,可以对LCD1602执行写操作,R/W=1,可以对LCD1602执行读操作。
E:使能信号引脚。在该引脚的下降沿,数据被写入LCD1602,当该引脚为高电平时,可以对LCD1602进行读数据操作。
D0~D7:数据总线引脚。
LEDA:背光电源地
LEDK:背光电源
1.3.2. LCD1602液晶显示字符的过程
要通过电路和编程控制LCD1602液晶显示字符,需要解决三个问题。
1、字符标准ASCII码的产生
常用字符的标准ASCII码必须人工产生,在程序中定义字符常量或字符串常量时,C语言在编译后会自动产生其标准的ASCII码。只要将生成的标准ASCII码通过单片机的I/O口送入LCD1602的数据显示寄存器DDRAM中,内部控制线路就会自动将字符传送到显示器上。
单片机中字符型数据或字符串数据的定义一般使用数组方式。例如定义一个字符串unsigned char code string[ ]={"laomashitu mcu"}; 大家可以看到,这个数组没有定义长度,C51会自动根据字符串的长度来指定数组的长度,注意这个数组的最后一个字符默认是空字符“\0”,字符串用双引号括起来。
2、LCD1602显示模式的设置
要让LCD1602液晶显示字符,必须对“有无光标、光标的移动方向、光标是否闪烁、字符的移动方向”等内容进行设置,液晶才会按照设计要求进行显示。这些设置是通过一系列控制指令实现的。控制指令在下面会有详细描述。
需要说明的时候,发送控制指令时,需要对RS和R/W引脚的电平进行相应设置,不同的指令对RS和R/W引脚的电平要求不同。
3、如何指定字符显示位置
要显示字符,必须要让LCD1602液晶控制器直到要在哪个位置显示,即要在第几行第几列显示。也就是说,要先向液晶控制器输入待显示字符的地址。(具体的地址定义在下面控制指令里有详细说明)。
LCD1602液晶内部显示地址如下图所示。指定显示位置的方法是“80H 地址码(第一行00H~0FH,第二行40H~4FH)”。例如要在第一行第2列显示一个字符,则应指定地址码为80H 01H=81H。
LCD1602液晶内部显示地址
1.3.3. LCD1602液晶的控制指令清屏指令将屏幕显示的内容清空。用于清除DDRAM和AC的值。
归零指令
将屏幕的光标回归原点。
输入方式选择指令
设置光标和画面的移动方式。
I/D=1:数据读、写操作后,AC自动加1;
I/D=0:数据读、写操作后,AC自动减1.
S=1:数据读、写操作后,画面平移;
S=0:数据读、写操作后,画面保持不变
显示开关控制指令用于设置显示、光标及闪烁开、关。
D=1:显示开;
D=0:显示关。
C=1:光标开;
C=0:光标关。
B=1:闪烁开;
B=0:闪烁关。
光标和画面移动指令用于在不影响DDRAM的情况下,使光标、画面移动。
S/C=1:画面平移一个字符位;
S/C=0:光标平移一个字符位。
R/L=1:右移;
R/L=0:左移。
功能设置指令用于设置工作方式。(初始化指令)
DL=1:8位数据接口;
DL=0:4位数据接口。
N=1:两行显示;
N=0:一行显示。
F=1:5*10点阵字符;
F=0:5*7点阵字符。
CGRAM设置指令用于设置CGRAM地址,A5~A0=0x00~0x3f。
DDRAM设置指令
用于设置DRAM地址。
N=0:一行显示,地址:A6~A0=0~4fH;
N=1:两行显示,第一行地址A6~A0=0~2fH,第二行地址A6~A0=40~64H。
l读RF和AC指令
BF=1:忙;
BF=0:准备好。此时AC值为最近一次地址设置(CGRAM或DDRAM)的值。
写数据指令用于将地址码写入DDRAM以使LCD显示出相应的图形或将用户自创的图形存入CGRAM内。
读数据指令
根据当前设置的地址,从DDRAM或CGRAM读出数据。
1.3.4. LCD1602的初始化步骤:
1、设置LCD1602的功能;
2、设置LCD1602的输入方式
3、设置LCD1602的显示方式
4、清除LCD1602的屏幕
1.4. 电路设计
本实例电路如图1所示。LCD1602液晶的数据管脚连接单片机的P3口的P3.0~P3.7。LCD1602液晶的控制管脚RW,RS,E连接P2口的3个引脚。整体电路很简单。
1.5. 程序设计本实例程序代码如下。程序代码中在重要的地方都有详细注释。
#include<AT89x52.h> //单片机端口、寄存器定义头文件
#include<intrins.h> //库函数调用,包含_nop_()语句的头文件
//单片机引脚位定义
sbit RS=P2^5; //RS位定义,寄存器选择位定义
sbit RW=P2^6; //RW位定义,读写选择位
sbit E=P2^7; //E位定义,使能信号位
//位变量定义
sbit BF=P0^7; //忙标志检测位,即LCD1602的D7位
//全局变量定义
unsigned char code digit[ ]={"0123456789"}; //定义数字字符串,code表示不占用RAM空间,保存在ROM中
unsigned char code string[ ]={"laomashitu mcu"}; //定义字符串数组
//函数声明
//延时函数,1ms延时函数
void delay1ms();
//延时函数
void delay(unsigned char n);
//液晶忙状态检测函数
unsigned char BusyTest(void);
//液晶工作模式设置或显示地址函数,
void WriteInstruction (unsigned char dictate);
//指定字符显示地址函数
void WriteAddress(unsigned char x);
//数据写入液晶控制模块函数
void WriteData(unsigned char y);
//显示模式初始化函数
void LcdInitiate(void);
//主程序
void main(void)
{
unsigned char i;
LcdInitiate(); //LCD1602液晶初始化
WriteAddress(0x00); //写地址,从第一行第一列开始显示字符
i=0;
while(string[i]!='\0') //显示字符,直到显示完字符串数字的内容
{
WriteData(string[i]); //
delay(1000);//每显示一个字符,延时一会
i ; //显示下一个字符
}
WriteAddress(0x40); //第二行液晶显示,从第1列开始
i=0; //
while(digit[i]!='\0') //直到显示完数组中全部内容
{
WriteData(digit[i]); //
delay(1000);
i ; //
}
while(1) //主循环
{
}
}
/*****************************************************
函数功能描述:延时1ms,大约1ms
***************************************************/
void delay1ms()
{
unsigned char i,j; //变量定义和程序执行内容之间最好空一行
for(i=0;i<10;i )
for(j=0;j<33;j ) ;
}
/*****************************************************
函数功能;延时若干ms
***************************************************/
void delay(unsigned char n)
{
unsigned char i;
for(i=0;i<n;i )
delay1ms();
}
/*****************************************************
函数功能:判断液晶模块是否处于忙状态,
如果处于忙状态,则不能对液晶进行操作
***************************************************/
unsigned char BusyTest(void)
{
bit result;//位变量定义
RS=0; //读忙状态前,需要将RS置低,RW置高
RW=1;
E=1; //E=1£¬²ÅÔÊÐí¶Áд
_nop_(); //空操作,相当于延时
_nop_();
_nop_();
_nop_(); //一个nop的空操作相当于延时一个机器周期
result=BF; //读取忙状态标志
E=0; //将E恢复为低电平
return result;//函数返回忙状态值
}
/*****************************************************
函数功能:模式设置
***************************************************/
void WriteInstruction (unsigned char dictate)
{
while(BusyTest()==1); //设置前先判断液晶空闲状态,空闲,则可进行设置
RS=0; //模式设置前,RS和RW置低电平
RW=0;
E=0; //E置低电平,写指令时,需要E有一个正跳变,即从0跳变到1
_nop_();
_nop_(); //延时一会
P3=dictate; //将数据送到液晶的数据端口
_nop_();
_nop_();
_nop_();
_nop_(); //
E=1; //E置高电平,写数据前E置低,这样就是一个从0到1的跳变
_nop_();
_nop_();
_nop_();
_nop_(); //
E=0; //E由高到低的跳变,液晶模块开始执行命令
}
/*****************************************************
函数功能:指定字符显示地址
***************************************************/
void WriteAddress(unsigned char x)
{
WriteInstruction(x|0x80); //显示位置的制定规则:80H 地址码
}
/*****************************************************
函数功能:将数据写入液晶模块
***************************************************/
void WriteData(unsigned char y)
{
while(BusyTest()==1);
RS=1; //写数据前,需要设置RS=1,RW=0
RW=0;
E=0; //写命令需要E有一个从0到1的跳变,
// 写之前E置0,数据送出后E置1,这样就产生了跳变
P3=y; //数据送到液晶数据端口
_nop_();
_nop_();
_nop_();
_nop_(); //
E=1; //数据送出后,E置1,以便产生0到1的跳变
_nop_();
_nop_();
_nop_();
_nop_(); //
E=0; //E由1跳变到0,则液晶开始执行命令
}
/*****************************************************
函数功能:显示模式初始化设置
***************************************************/
void LcdInitiate(void)
{
delay(15); //
WriteInstruction(0x38); //显示模式设置:16*2显示,5*7点阵,8位数据接口
delay(5); //每一条指令后都延时一会,便于液晶有时间执行完指令
WriteInstruction(0x38);
delay(5);
WriteInstruction(0x38); //初始化指令可多执行几次,确保初始化成功
delay(5);
WriteInstruction(0x0c); //显示模式设置:显示开,无光标,光标不闪烁
delay(5);
WriteInstruction(0x06); //显示模式设置:光标右移,字符不移
delay(5);
WriteInstruction(0x01); //清屏指令:将液晶屏幕当前显示内容清除
delay(5);
}
这段程序实现的功能是:LCD1602液晶从第一行第1列开始逐个显示字符“laomashitu mcu”,第二行逐个显示字符“0123456789”。
1.6. 实例仿真编译程序后,将生成的hex文件载入proteus电路的单片机中,开始仿真,LCD1602液晶显示的内容,是否符合设计预期。
下面视频是本实例的仿真过程。
1.7. 总结
通过本实例的学习,初步学习了LCD1602液晶的显示电路和编程控制。在接下来的实例中,我们将会进一步学习LCD1602液晶的更多功能。
,