51单片机lcd显示字符 (基于proteus微程序cpu设计)

1. 基于proteus的51单片机开发实例18-LCD1602液晶显示

1.1. 实验目的

基于proteus的51单片机,基于proteus微程序cpu设计

图1 LCD1602显示电路

前面我们已经学习了使用数码管显示数字和简单的字符,但是数码管显示内容有很大的局限性,例如显示的字符数量有限,并且显示复杂的字符形状时,显示的效果不是很好。例如使用8段数码管显示字符“S”时,效果如下图。可以看出这个“S”与数字“5”很像。事实上,数码管显示字符的段码表里面,字符“S”和数字“5”的段码是一样的。

基于proteus的51单片机,基于proteus微程序cpu设计

数码管显示数字和字符

在本例中,我们使用显示内容更为丰富的液晶来显示字符和数字。使用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液晶的实物图和引脚图。

基于proteus的51单片机,基于proteus微程序cpu设计

LCD1602液晶实物图

基于proteus的51单片机,基于proteus微程序cpu设计

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。

基于proteus的51单片机,基于proteus微程序cpu设计

LCD1602液晶内部显示地址

1.3.3. LCD1602液晶的控制指令

清屏指令

将屏幕显示的内容清空。用于清除DDRAM和AC的值。

基于proteus的51单片机,基于proteus微程序cpu设计

归零指令

将屏幕的光标回归原点。

基于proteus的51单片机,基于proteus微程序cpu设计

输入方式选择指令

设置光标和画面的移动方式。

基于proteus的51单片机,基于proteus微程序cpu设计

I/D=1:数据读、写操作后,AC自动加1;

I/D=0:数据读、写操作后,AC自动减1.

S=1:数据读、写操作后,画面平移;

S=0:数据读、写操作后,画面保持不变

显示开关控制指令

用于设置显示、光标及闪烁开、关。

基于proteus的51单片机,基于proteus微程序cpu设计

D=1:显示开;

D=0:显示关。

C=1:光标开;

C=0:光标关。

B=1:闪烁开;

B=0:闪烁关。

光标和画面移动指令

用于在不影响DDRAM的情况下,使光标、画面移动。

基于proteus的51单片机,基于proteus微程序cpu设计

S/C=1:画面平移一个字符位;

S/C=0:光标平移一个字符位。

R/L=1:右移;

R/L=0:左移。

功能设置指令

用于设置工作方式。(初始化指令)

基于proteus的51单片机,基于proteus微程序cpu设计

DL=1:8位数据接口;

DL=0:4位数据接口。

N=1:两行显示;

N=0:一行显示。

F=1:5*10点阵字符;

F=0:5*7点阵字符。

CGRAM设置指令

用于设置CGRAM地址,A5~A0=0x00~0x3f。

基于proteus的51单片机,基于proteus微程序cpu设计

DDRAM设置指令

用于设置DRAM地址。

基于proteus的51单片机,基于proteus微程序cpu设计

N=0:一行显示,地址:A6~A0=0~4fH;

N=1:两行显示,第一行地址A6~A0=0~2fH,第二行地址A6~A0=40~64H。

l读RF和AC指令

基于proteus的51单片机,基于proteus微程序cpu设计

BF=1:忙;

BF=0:准备好。此时AC值为最近一次地址设置(CGRAM或DDRAM)的值。

写数据指令

用于将地址码写入DDRAM以使LCD显示出相应的图形或将用户自创的图形存入CGRAM内。

基于proteus的51单片机,基于proteus微程序cpu设计

读数据指令

根据当前设置的地址,从DDRAM或CGRAM读出数据。

基于proteus的51单片机,基于proteus微程序cpu设计

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液晶的更多功能。