LCD1602(液晶显示屏)

发布时间:2024年01月23日

工具

1.Proteus 8 仿真器

2.keil 5 编辑器

原理图

注意:proteus8 中没有lcd1602本篇仿真采用LM016L代替

讲解

引脚

引脚说明
引脚说明
VSS
VDD电源 (+5V)
V0对比度调整,接正电源对比度最弱,接地对比度最高(对比度高时会产生"鬼影",使用时可以通过一个10K的电位器调整对比度)
RS寄存器选择,高电平时为数据寄存器,低电平时为指令寄存器
R/W??R/W为读写信号线,高电平时进行操作,低电平时进行操作
E使能端,下降沿使能
DB0~DB7数据总线
BLA背光电源正极
BLK背光电源负极

寄存器选择?

寄存器选择控制表
RSR/W说明
00写入指令寄存器
01读取指令寄存器
10写入数据寄存器
11读取数据寄存器

时序图

写操作时序图

写指令寄存器

RS低电平,R/W低电平,E低电平,写入DB0~DB7并行数据---(>40ns)---E高电平---(>150ns)---E低电平---(>40ns)---下一段写入

写数据寄存器

RS高电平,R/W低电平,E低电平,写入DB0~DB7并行数据---(>40ns)---E高电平---(>150ns)---E低电平---(>40ns)---下一段写入

读操作时序图

读指令寄存器

RS低电平,R/W高电平,E低电平---(>30ns)---E高电平---(>15ns)---读取DB0~DB7并行数据---(>150ns)---E低电平---(>40ns)---下一段读取

读数据寄存器

RS高电平,R/W高电平,E低电平---(>30ns)---E高电平---(>15ns)---读取DB0~DB7并行数据---(>150ns)---E低电平---(>40ns)---下一段读取

忙闲状态

LCD1602的忙闲可以通过读取忙标志位(BF)来判断。忙标志位(BF)是一个8位寄存器,用于指示LCD1602是否处于忙状态。如果BF寄存器的第7位为1,则表示LCD1602处于忙状态,此时无法进行读写操作;如果BF寄存器的第7位为0,则表示LCD1602处于空闲状态,可以进行读写操作。

可以通过以下步骤来读取忙标志位(BF):

  1. 将数据总线(DB)清零。
  2. 将寄存器选择(RS)设为低电平,选择数据寄存器。
  3. 将读/写选择(RW)设为高电平,准备读取数据。
  4. 将使能信号(EN)设为高电平,启动读取操作。
  5. 读取数据总线(DB)上的值,并将其存储在变量中。
  6. 将使能信号(EN)设为低电平,结束读取操作。
  7. 检查读取的值,如果最高位(第7位)为1,则表示LCD1602处于忙状态;如果最高位为0,则表示LCD1602处于空闲状态。

需要注意的是,在读取忙标志位(BF)之前,需要先向LCD1602发送一个写命令(0x80)或写数据(0x00)的指令,以确保LCD1602处于准备状态。同时,在读取忙标志位(BF)之后,需要根据LCD1602的状态来决定是否需要等待一段时间再执行后续操作。

指令集

清屏指令:

用于清除DDRAM和AC(内存和光标寄存器)的数值,用于将1602当前屏幕的显示清空(无显示)

1602的清屏指令
RSRWD7D6D5D4D3D2D1D0
0000000001
归零指令:

将1602屏幕的光标(当前字符显示点)回归原点(可以预先设置,通常来说是左上角)

1602的归零指令
RSRWD7D6D5D4D3D2D1D0
000000001*
输入方式选择指令:

用于设置1602的光标和画面移动方式。其中,I/D=1:数据读、写操作后,AC(光标寄存器)自动加1;I/D=0:数据读、写操作后,AC(光标寄存器)自动减1;S=1:数据读、写操作,画面平移;S=0:数据读、写操作,画面保持不变。

1602的输入方式选择指令
RSRWD7D6D5D4D3D2D1D0
00000001I/DS
显示开关控制i指令:

用于设置显示,光标及闪烁开、关。其中,D表示显示开关:D=1 为开,D=0 为关;C表示光标开关:C=1 为开,C=0 为关;B表示闪烁开关:B=1 为开,B=0 为关。

1602的显示开关控制i指令?
RSRWD7D6D5D4D3D2D1D0
0000001DCB
光标和画面移动指令:

用于在不影响DDRAM(内存)的情况下使光标、画面移动。其中,S/C=1 :画面平移一个字符位;S/C=0 :光标平移一个字符位;R/L=1 :右移;R/L=0 :左移。

1602的光标和画面移动指令?
RSRWD7D6D5D4D3D2D1D0
000001S/CR/L**
功能设置指令:

用于设置工作方式(初始化指令)。其中,DL=1 :8位数据接口;DL=0 :4位数据接口;N=1 :分两行显示;N=0 :在同一行显示;F=1 :5x10点阵字符;F=0 :5x7点阵字符。

1602的功能设置指令
RSRWD7D6D5D4D3D2D1D0
00001DLNF**
CGRAM设置指令:

用于设置CGRAM的地址。A5~A0对应的地址为0x00~0x3F。

1602的CGRAM设置指令
RSRWD7D6D5D4D3D2D1D0
0001A5A4A3A2A1A0
DDRAM设置指令:

用于设置DDRAM的地址。N=0 :在一行中显示,此时A6~A0对应0x00~0x4F:N=1 :分两行显示,首行A6~A0对应0x00~0x2F,次行A6~A0对应0x40~0x64.

1602的DDRAM设置指令
RSRWD7D6D5D4D3D2D1D0
001A6A5A4A3A2A1A0
?读BF和AC指令:

其中,BF=1 表示当前忙;BF=0 表示已经准备好。此时,AC值意义为最后一次地址设置(CGRAM或DDRAM)定义。

1602的读BF和AC指令
RSRWD7D6D5D4D3D2D1D0
01BFAC6AC5AC4AC3AC2AC1AC0
写数据指令:

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

1602的写数据指令
RSRWD7D6D5D4D3D2D1D0
10数据
读数据指令:

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

1602的读数据指令
RSRWD7D6D5D4D3D2D1D0
11数据

初始化

(1)延时 15ms
(2)写指令 38H( 不检测忙信号 )
(3)延时 5ms
(4)以后每次写指令,读 / 写数据操作均需要检测忙信号
(5)写指令 38H :显示模式设置
(6)写指令 08H :显示关闭
(7)写指令 01H :显示清屏
(8)写指令 06H :显示光标移动设置
(9)写指令 0CH :显示开及光标设置

ASCll 码表

注意:?先行后纵? 例如感叹号? !=? 0010 0001? =? ?0x21

显示地址

位置

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

第一行

00H

01H

02H

03H

04H

05H

06H

07H

08H

09H

0AH

0BH

0CH

0DH

0EH

0FH

第二行

40H

41H

42H

43H

44H

45H

46H

47H

48H

49H

4AH

4BH

4CH

4DH

4EH

4FH

注意:位置选择时最高位必须为1,第一行最左边地址为 0x00 | 0x80 ;第二行为 0x40 | 0x80 ? ?

代码

#include <reg52.h>  
#include "Delay.h"

unsigned char text[]={"Hello world"};
unsigned char text2[]={"Old_man"};
unsigned char i;

sbit RS=P2^0;					//LCD数据/命令选择方式端口定义
sbit RW=P2^1;					//LCD读写使能端口定义
sbit E=P2^2;					//LCD使能端口定义 
sbit bflag=P0^7;			//LCD忙指示端口,高电平为忙

void busy_1602()		   //判忙忙闲? 
{  
   do
   {
   	P0=0x00;
    RS=0;							//RS=0,RW=1时才可读忙信号
	RW=1;
	E=1;
	delay_ms(1);
	bflag=P0;
	E=0;
   }while(bflag);	    //当其值为0时,表示不忙,才可以接收命令或者数据信号
}

void wirte_com(unsigned char com)	 //函数功能:写指令函数 
{
   busy_1602();
   RS=0;		          //当RS=0,RW=0时,表明写入的是命令
   RW=0;
   E=0;
   P0=com;		        //当使能由高到低时,LCD执行相应命令
   E=1;
   delay_ms(1);
   E=0;
}

void wirte_data(unsigned char dat)	 //函数功能:写数据函数 
{
   busy_1602();
   RS=1;		          //当RS=1,RW=0时,表明写入的是数据
   RW=0;
   E=0;
   P0=dat;		        //当使能由高到低时,LCD执行相应命令
   E=1;
   delay_ms(1);
   E=0;
}

void lcd_pos(unsigned char pos)	//函数功能:指定数据显示地址
{
	wirte_com(pos | 0x80);		//最高位必须为1,第一行最左边地址为0x00;第二行为0x40    
}

void init_1602()			  //函数功能:设置LCD_1602的开显示 光标不闪烁等功能 
{
	 //(1)延时 15ms
		delay_ms(15);
		//(2)写指令 38H(不检测忙信号)
		wirte_com(0x38);
		//(3)延时 5ms
		delay_ms(5);
		//(4)以后每次写指令,读/写数据操作均需要检测忙信号
		//(5)写指令 38H:显示模式设置
		wirte_com(0x38);
		//(6)写指令 08H:显示关闭
		wirte_com(0x08);
		//(7)写指令 01H:显示清屏
		wirte_com(0x01);
		//(8)写指令 06H:显示光标移动设置
		wirte_com(0x06);
		//(9)写指令 0CH:显示开及光标设置}
		wirte_com(0x0c);
}

void main(void)
{
   init_1602();
   lcd_pos(0x00);			//第一行
   for(i=0;i<11;i++)
   {
     wirte_data(text[i]);
   }
   lcd_pos(0x40);			//第二行
   for(i=0;i<7;i++)
   {
     wirte_data(text2[i]);
   }
   while(1)
   {
		
   }	
}

实现

进阶

解决问题:当我们直接写入0x01时 LCD1602并不会显示1这个数字 (我们给LCD1602十六进制他只会从显示的ASCLL码表中找到该十六进制所对应的内容并将其显示)

LCD1602.c

#include <REGX52.H>
#include "Delay.h"
 
//引脚配置:
sbit LCD_RS=P2^0;
sbit LCD_RW=P2^1;
sbit LCD_EN=P2^2;
#define LCD_DataPort P0
 
void busy_1602()		   //判忙忙闲? 
{  
   do
   {
   	LCD_DataPort=0x00;
    LCD_RS=0;							//RS=0,RW=1时才可读忙信号
	  LCD_RW=1;
	  LCD_EN=1;
	  delay_ms(1);
  	LCD_DataPort;
  	LCD_EN=0;
   }while(LCD_DataPort);	    //当其值为0时,表示不忙,才可以接收命令或者数据信号
}
 
/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
	busy_1602();
	LCD_RS=0;
	LCD_RW=0;
	LCD_EN=0;
	LCD_DataPort=Command;
	LCD_EN=1;
	delay_ms(1);
	LCD_EN=0;
	delay_ms(1);
}
 
/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
	busy_1602();
	LCD_RS=1;
	LCD_RW=0;
	LCD_EN=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	delay_ms(1);
	LCD_EN=0;
	delay_ms(1);
}
 
/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}
 
/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}
 
/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}
 
/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}
 
/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}
 
/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}
 
/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
	LCD_SetCursor(Line,Column);
	if(Number>=0)
	{
		LCD_WriteData('+');
		Number1=Number;
	}
	else
	{
		LCD_WriteData('-');
		Number1=-Number;
	}
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
	}
}
 
/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(SingleNumber-10+'A');
		}
	}
}
 
/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
	}
}

LCD1602.h

#ifndef __LCD1602_H__
#define __LCD1602_H__

//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);

#endif

提示:C51学习中对此部分理解下就可以,ASCLL码与十六进制的互相转换。

文章来源:https://blog.csdn.net/werluo/article/details/135712317
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。