1.LCD1602简介
LCD1602是字符型液晶显示模块,可显2行16字符,有多种接口,应用广、易控制。
LCD1602有16个 管脚孔,从左自右管脚编号顺序是1-16,功能定义如下:
各引脚功能如下:
电源相关引脚
- VSS(引脚1):接地引脚,接电源负极,为模块提供零电位参考。
- VDD(引脚2):电源正极引脚,一般接+5V电源,为模块提供工作所需的电能。
- VL/VO(引脚3):对比度调节引脚,通过外接电位器来调节液晶显示的对比度,使显示内容清晰可见。
数据传输引脚
- RS(引脚4):寄存器选择引脚。当RS=0时,选择指令寄存器,用于写入指令;当RS=1时,为读取操作,用于写入或读取显示数据。
- RW(引脚5):读写控制引脚。当RW=0时,为写入操作,向LCD写入指令或数据;当RW=1时,为读取操作,从LCD读取状态或数据。
- E(引脚6):使能信号引脚。在进行读写操作时,E引脚会产生一个高电平脉冲信号,LCD在这个脉冲的下降沿完成数据的读写。
- D0-D7(引脚7-14):8位并行双向数据总线引脚,用于在微控制器和LCD之间传输指令、数据等信息。
背光相关引脚
- A(引脚 15):背光源正极引脚,一般接 +5V 电源,为背光源提供电能,使屏幕在光线较暗时也能看清显示内容。
- K(引脚 16):背光源负极引脚,接地,与 A 引脚共同构成背光源的供电回路。
LCD1602内部含有80个字节的DDRAM,它是用来寄存器显示字符的。其地址和屏幕的对应关系如下表:
LCD1602 的控制指令系统是基于 16×2 的显示模式设计的。通过指令设置显示位置时,只能在预先定义好的这 32 个字符位置范围内进行定位。例如,使用设置 DDRAM 地址的指令,其参数范围只能指向这 32 个有效位置,无法对超出此范围的显示位置进行定位和数据写入,进而无法实现 40 列的显示。
2.LCD1602常用指令
清屏指令
- 指令码:0x01
- 功能:清除液晶显示屏上的所有显示内容,将 DDRAM(显示数据随机存取存储器)全部清零,同时把光标复位到屏幕左上角(即第 1 行第 1 列)。
- 使用场景:当需要重新显示新的内容,且不希望之前的显示内容残留时,可使用该指令进行清屏操作。
光标归位指令
- 指令码:0x02
- 功能:将光标移动到屏幕左上角(第 1 行第 1 列),但不清空 DDRAM 中的内容,即显示屏上原有的显示内容保持不变。
- 使用场景:在需要将光标重新定位到起始位置,以便从开头开始显示新数据时使用。
显示开关控制指令
- 指令码:0x08 | D | C | B (其中 D、C、B 为标志位)
- 功能:
- D(第 3 位):控制整体显示的开关。D = 1 时,显示开启;D = 0 时,显示关闭,但 DDRAM 中的数据不会被清除。
- C(第 2 位):控制光标的显示。C = 1 时,光标显示;C = 0 时,光标隐藏。
- B(第 1 位):控制光标闪烁。B = 1 时,光标闪烁;B = 0 时,光标不闪烁。
- 使用场景:例如,若要关闭显示但保留数据,可将 D 置为 0;若要显示光标但不使其闪烁,可设置 D = 1、C = 1、B = 0。
进入模式设置指令
- 指令码:0x04 | I/D | S
- 功能:设定每次写入 1 位数据后光标的移位方向,并且设定每次写入的一个字符时显示屏是否移动。
- 参数设定情况:
- I/D(第 2 位):控制写入新数据后光标的移动方向。当 I/D = 0 时,写入新数据后光标左移;当 I/D = 1 时,写入新数据后光标右移。
- S(第 1 位):控制写入新数据后显示屏的移动情况。当 S = 0 时,写入新数据后显示屏不移动;当 S = 1 时,写入新数据后显示屏整体右移 1 个字。
- 使用场景:在实际使用 LCD1602 显示数据时,可根据具体的显示需求来设置该指令的参数。例如,若希望数据从左到右依次显示且显示屏不移动,可将 I/D 设为 1,S 设为 0;若需要实现类似走马灯效果,每次写入数据后显示屏整体右移,可将 I/D 设为 1,S 设为 1。
3.使用LCD1602
如果要使用 LCD1602 液晶显示屏,第一步要做的就是对它进行初始化。初始化的操作其实就是向 LCD1602 写入一些特定的指令,让它准备好工作。
初始化完成后,我们要确定在 LCD1602 的哪个具体位置显示内容。确定好位置后,再把想要显示的数据发送到 LCD 的 DDRAM(显示数据随机存取存储器)里。
在实际使用中,我们一般只用 LCD1602 的写数据功能,很少会用到它的读功能。它的基本操作步骤可以分成下面三步:
- 初始化:这是让 LCD1602 进入正常工作状态的关键步骤。
- 写命令设置显示位置:在这个步骤里,要把 RS 引脚设置为低电平(用 “L” 表示),通过写入命令来告诉 LCD1602 我们要在哪个位置显示内容。
- 写数据进行显示:把 RS 引脚设置为高电平(用 “H” 表示),然后将需要显示的数据发送过去。
因为我们一般不读取 LCD1602 里的数据状态或者数据本身,所以只需要了解两种写操作的时序就行:
- 写指令设置工作方式:当我们要写指令来设置 LCD1602 的工作方式时,要把 RS 引脚设置为低电平,RW 引脚也设置为低电平。接着把相关指令数据送到数据口 D0 - D7。最后,在 E 引脚上产生一个高脉冲,这样指令数据就会被写入到 LCD1602 里。
- 写数据实现显示:要是想在 LCD1602 上显示具体的数据,就得把 RS 引脚设置为高电平,RW 引脚设置为低电平。把要显示的数据送到数据口 D0 - D7,同样在 E 引脚上产生一个高脉冲,数据就会被写入并显示出来。
可以看到,写指令和写数据这两个操作,主要的区别就是 RS 引脚的电平不一样。下面给大家展示 LCD1602 的时序图。
从上面的图能发现,图里给出的时间参数全都是纳秒(ns)级别的。可 51 单片机呢,它的机器周期是 1 微秒(us),指令周期得 2 - 4 个机器周期。这就好比一个大块头和一个小不点,就算我们在程序里不额外加延时程序,51 单片机也能轻轻松松配合上 LCD1602 的时序要求。
咱们来看看写命令字节的过程。首先 RS 引脚变成低电平,紧接着 R/W 引脚也变成低电平,而且 RS 引脚状态先变完。这时候,DB0 到 DB7 这些数据线上的数据就进入 “有效状态” 。
接着,E 引脚会有一个完整脉冲的跳变。这个 E 脉冲的宽度最小得维持 400ns。之后 E 引脚来个负跳变,RS 引脚和 R/W 引脚的电平也跟着变化,这就完成了一次完整的 LCD1602 写命令的过程。
这里要提醒一下哈,我们说的是 8 位的 LCD1602。现在有些公司为了让引脚数量少一些,就做了 4 位的 LCD1602。要是你用 4 位的 LCD1602,可得多看看它的手册,找出和 8 位的不同之处,然后把原来的程序改一改。
4.LCD1602原理图
从上面的图能看出来,LCD1602 的 8 位数据口,也就是 DB0 到 DB7 这些引脚,和单片机的 P0.0 到 P0.7 管脚连在一起,就像把两座房子的对应房间门都打通,方便两边传递东西。LCD1602 的 RS、RW、E 这三个引脚呢,分别和单片机的 P2.6、P2.5、P2.7 管脚相连,这样单片机就能控制 LCD1602 的工作状态啦,就像你通过几个开关控制玩具的不同动作。
图里还有个叫 RJ1 的东西,它是个电位器,作用就像电视机的亮度调节按钮,通过调节它可以改变 LCD1602 显示内容的对比度,也就是让显示亮度变亮或者变暗。
5.代码实现
1.定义
sbit
是 51 单片机特有的关键字,用于定义可位寻址的特殊功能寄存器的某一位。这里将LCD_RS
定义为 P2 端口的第 6 位,LCD_RW
定义为 P2 端口的第 5 位,LCD_E
定义为 P2 端口的第 7 位。这三个引脚分别对应 LCD1602 的寄存器选择引脚(RS)、读写控制引脚(RW)和使能引脚(E),用于控制 LCD1602 的操作模式和数据传输。#define
是 C 语言的预处理指令,用于定义宏。这里将LCD_DataPort
定义为 P0 端口,意味着 P0 端口将作为与 LCD1602 进行 8 位数据传输的接口。
#include <REGX52.H>
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_E=P2^7;
#define LCD_DataPort P0
void LCD_Delay(void) //@11.0592MHz 1ms
{
unsigned char data i, j;
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
2. 向 LCD1602 写入命令
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_E=1;
LCD_Delay();
LCD_E=0;
LCD_Delay();
}
LCD_RS = 0;
LCD_RS
是寄存器选择引脚,当LCD_RS
为低电平(0)时,表示要写入的是命令。这一步操作告诉 LCD1602,接下来要接收的是用于设置其工作状态的命令。
LCD_RW = 0;
LCD_RW
是读写控制引脚,当LCD_RW
为低电平(0)时,表示进行写操作。这里明确当前的操作是向 LCD1602 写入数据,而不是读取数据。
LCD_DataPort = Command;
LCD_DataPort
是数据端口,这里将传入的Command
命令数据赋值给LCD_DataPort
,即将命令数据送到与 LCD1602 连接的数据总线上,准备发送给 LCD1602。
LCD_E = 1;
LCD_E
是使能引脚,当LCD_E
变为高电平(1)时,触发 LCD1602 开始接收数据。此时 LCD1602 会检测数据总线上的数据。
LCD_Delay();
- 调用之前定义的
LCD_Delay
函数进行延时。这个延时的作用是确保 LCD1602 有足够的时间来稳定地读取数据总线上的命令数据。
- 调用之前定义的
LCD_E = 0;
- 将
LCD_E
引脚拉低为低电平(0)。在LCD_E
从高电平变为低电平的下降沿,LCD1602 会将数据总线上的数据锁存并处理,完成命令的写入操作。
- 将
LCD_Delay();
- 再次调用
LCD_Delay
函数进行延时,为下一次操作做准备,确保 LCD1602 有足够的时间处理刚刚写入的命令。
- 再次调用
3.向 LCD1602 写入数据
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_E=1;
LCD_Delay();
LCD_E=0;
LCD_Delay();
}
该函数的代码结构和执行过程与 LCD_WriteCommand
函数基本相同,唯一的区别在于 LCD_RS
的设置。
4.初始化
LCD_Init
函数的作用是对 LCD1602 液晶显示屏进行初始化设置,通过向 LCD1602 写入一系列特定的命令,使其进入正常的工作状态。
void LCD_Init(void)
{
LCD_WriteCommand(0x38);
LCD_WriteCommand(0x0C);
LCD_WriteCommand(0x06);
LCD_WriteCommand(0x01);
}
1. LCD_WriteCommand(0x38);
- 功能:设置 LCD1602 的工作模式。
- 详细解释:命令码
0x38
对应的二进制是00111000
。在 LCD1602 的指令系统中,这个命令用于设置数据总线为 8 位(对应第 5 位为 1),显示为 2 行(对应第 4 位为 1),字符字体为 5x7 点阵(对应第 3 位为 0)。简单来说,就是告诉 LCD1602 采用 8 位数据传输方式,以 2 行显示,每个字符由 5x7 的点阵组成。
2. LCD_WriteCommand(0x0C);
- 功能:控制显示开关。
- 详细解释:命令码
0x0C
对应的二进制是00001100
。第 3 位为 1 表示开启显示功能;第 2 位为 0 表示不显示光标;第 1 位为 0 表示光标不闪烁。所以这条命令的作用是开启 LCD1602 的显示功能,并且不显示光标和让光标不闪烁。
3. LCD_WriteCommand(0x06);
- 功能:设置输入模式。
- 详细解释:命令码
0x06
对应的二进制是00000110
。第 2 位为 1 表示写入新数据后光标右移;第 1 位为 0 表示写入新数据后显示屏不移动。也就是说,当我们向 LCD1602 写入数据时,光标会自动向右移动一个位置,而显示屏整体不会发生移动。
4. LCD_WriteCommand(0x01);
- 功能:清屏操作。
- 详细解释:命令码
0x01
对应的二进制是00000001
。这条命令会清除 LCD1602 显示屏上的所有显示内容,将显示数据随机存取存储器(DDRAM)中的数据全部清零,同时把光标复位到屏幕的左上角(第 1 行第 1 列)。
总结
LCD_Init
函数通过依次调用 LCD_WriteCommand
函数,向 LCD1602 发送了一系列初始化命令,完成了对 LCD1602 的工作模式、显示开关、输入模式的设置,并进行了清屏操作,使得 LCD1602 处于一个可以正常接收和显示数据的初始状态。在后续的程序中,就可以向 LCD1602 写入具体的数据进行显示了。
5.显示光标
这段代码定义了一个名为 LCD_SetCursor
的函数,其功能是设置 LCD1602 显示屏上光标的位置。
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else
{
LCD_WriteCommand(0x80|(Column-1)+0x40);
}
}
- 条件判断:当
Line
的值等于 1 时,说明要将光标设置在 LCD1602 的第一行。 - 命令计算与发送:
0x80
是设置 DDRAM(显示数据随机存取存储器)地址的基本命令码。在 LCD1602 中,要设置显示位置,需要向其发送设置 DDRAM 地址的命令。Column - 1
是将用户输入的列号(从 1 开始计数)转换为 DDRAM 地址偏移量(从 0 开始计数)。0x80 | (Column - 1)
是通过按位或运算将基本命令码和地址偏移量组合成完整的设置 DDRAM 地址的命令。例如,如果要将光标设置在第一行第 5 列,Column
为 5,Column - 1
为 4,0x80 | 4
得到0x84
,然后调用LCD_WriteCommand
函数将该命令发送给 LCD1602,从而将光标定位到第一行第 5 列。
- 条件判断:当
Line
的值不等于 1 时,即Line
为 2,说明要将光标设置在 LCD1602 的第二行。 - 命令计算与发送:
- 第二行的 DDRAM 地址是从
0x40
开始的。所以在设置第二行的光标位置时,除了要加上列号对应的偏移量Column - 1
,还需要加上0x40
。 - 同样通过按位或运算将基本命令码
0x80
和偏移量(Column - 1) + 0x40
组合成完整的命令,然后调用LCD_WriteCommand
函数发送给 LCD1602,实现将光标定位到第二行指定列的功能。
- 第二行的 DDRAM 地址是从
总结
LCD_SetCursor
函数通过判断用户输入的行号,结合列号计算出对应的 DDRAM 地址,并将设置地址的命令发送给 LCD1602,从而实现了在 LCD1602 显示屏上精确设置光标位置的功能,为后续在指定位置显示字符数据提供了基础。
6.显示字符
这段代码定义了一个名为 LCD_ShowChar
的函数,其主要功能是在 LCD1602 显示屏的指定位置显示一个字符。
void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
LCD_ShowChar
函数通过先调用 LCD_SetCursor
函数定位光标位置,再调用 LCD_WriteData
函数写入字符数据,实现了在 LCD1602 显示屏的指定位置显示单个字符的功能。这个函数为后续在 LCD 上显示更复杂的信息(如字符串、数字等)提供了基础。
7.显示字符串
这段代码定义了一个名为 LCD_ShowString
的函数,其功能是在 LCD1602 显示屏的指定位置显示一个字符串。
void LCD_ShowString(unsigned char Line,unsigned char Column,unsigned char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
String
:这是一个指向无符号字符型的指针,它指向要显示的字符串的首地址。在 C 语言里,字符串是以'\0'
(空字符)结尾的字符数组。
LCD_ShowString
函数先通过 LCD_SetCursor
函数定位字符串的起始显示位置,然后利用 for
循环遍历字符串中的每个字符,再调用 LCD_WriteData
函数将字符逐个写入 LCD1602,从而实现了在指定位置显示字符串的功能。
8.整数的指定次幂
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
LCD_Pow
函数通过一个简单的 for
循环,将底数 X
累乘 Y
次,实现了整数的幂运算功能。这个函数在处理一些需要进行数值计算的场景中可能会用到,比如在显示数字时可能需要将数字拆分为不同的位进行处理,这时候就可能会用到幂运算。
9.显示数字
这段代码定义了一个名为 LCD_ShowNum
的函数,其主要功能是在 LCD1602 显示屏的指定位置显示一个指定长度的无符号整数。
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+0x30);
}
}
- 参数:
Line
:表示要显示数字的起始行号,由于 LCD1602 通常为 2 行显示,所以该参数一般取值为 1 或 2。Column
:表示要显示数字的起始列号,LCD1602 每行可显示 16 个字符,因此该参数取值范围是 1 到 16。Number
:是一个无符号整型变量,代表要显示的无符号整数。Length
:是一个无符号字符型变量,指定了要显示数字的长度,即显示该数字所占的字符位数。
- 循环体:
LCD_Pow(10, i - 1)
:调用之前定义的LCD_Pow
函数,计算 10 的i - 1
次幂。例如,当i
为 3 时,计算 10 的 2 次幂,结果为 100。Number / LCD_Pow(10, i - 1)
:将Number
除以 10 的i - 1
次幂,得到该数字的高位部分。例如,若Number
为 123,i
为 3 时,Number / LCD_Pow(10, 2)
即 123 / 100,结果为 1。Number / LCD_Pow(10, i - 1) % 10
:对上述结果取模 10,得到该数字当前位的值。例如,1 % 10 结果仍为 1。Number / LCD_Pow(10, i - 1) % 10 + 0x30
:在 ASCII 码中,数字 0 - 9 的编码是从 0x30 开始的,所以将当前位的值加上 0x30 就得到了该数字字符对应的 ASCII 码值。例如,数字 1 对应的 ASCII 码值为 0x31。LCD_WriteData(Number / LCD_Pow(10, i - 1) % 10 + 0x30)
:调用LCD_WriteData
函数将该数字字符的 ASCII 码值写入 LCD1602 的 DDRAM 中,从而在显示屏上显示该数字位。
LCD_ShowNum
函数通过先定位光标位置,再利用循环依次提取并显示数字的每一位,实现了在 LCD1602 显示屏的指定位置显示指定长度无符号整数的功能。
10.显示有符号整数
这段代码定义了一个名为 LCD_ShowSignedNum
的函数,其功能是在 LCD1602 显示屏的指定位置显示一个有符号整数。
void LCD_ShowSignedNum(unsigned char Line, unsigned char Column, int Number, unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line, Column);
if (Number >= 0)
{
LCD_WriteData('+');
}
else
{
LCD_WriteData('-');
Number = -Number; // 将负数转换为正数进行后续处理
}
// 调整显示位置,从符号后面开始显示数字
LCD_SetCursor(Line, Column + 1);
for (i = Length; i > 0; i--)
{
LCD_WriteData(Number / LCD_Pow(10, i - 1) % 10 + 0x30);
}
}
LCD_ShowSignedNum
函数先在指定位置显示有符号整数的符号,然后将负数转换为正数,接着调整光标位置,最后通过循环依次提取并显示数字的数值部分的每一位,实现了在 LCD1602 显示屏上显示有符号整数的功能。
11.显示十六进制
这段代码定义了一个名为 LCD_ShowHexNum
的函数,其功能是在 LCD1602 显示屏的指定位置显示一个无符号整数的十六进制形式。
void LCD_ShowHexNum(unsigned char Line, unsigned char Column,unsigned int Number, unsigned char Length)
{
unsigned char i;
unsigned char 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+'A'-10);
}
}
}
- 显示十六进制位:
- 数字 0 - 9:如果
SingleNumber
小于 10,说明该十六进制位是数字 0 - 9,将其加上字符'0'
的 ASCII 码值,就得到了对应的数字字符的 ASCII 码值,然后调用LCD_WriteData
函数将其写入 LCD1602 进行显示。 - 字母 A - F:如果
SingleNumber
大于等于 10,说明该十六进制位是字母 A - F,将其加上字符'A'
的 ASCII 码值再减去 10,得到对应的字母字符的 ASCII 码值,然后调用LCD_WriteData
函数进行显示。
- 数字 0 - 9:如果
LCD_ShowHexNum
函数通过循环依次提取无符号整数的每一位十六进制值,并根据其值是数字还是字母,将其转换为对应的 ASCII 码字符,最后在 LCD1602 的指定位置显示出来,实现了十六进制数的显示功能。
12.显示二进制
这段代码定义了一个名为 LCD_ShowBinNum
的函数,其作用是在 LCD1602 显示屏的指定位置显示一个无符号整数的二进制形式。
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+0x30);
}
}
- 显示二进制位:
- 在 ASCII 码中,数字 0 的编码是 0x30,数字 1 的编码是 0x31。将提取到的二进制位的值(0 或 1)加上 0x30,就得到了对应的数字字符的 ASCII 码值。
LCD_WriteData(Number / LCD_Pow(2, i - 1) % 2 + 0x30)
:调用LCD_WriteData
函数将该二进制位对应的数字字符的 ASCII 码值写入 LCD1602 的 DDRAM 中,从而在显示屏上显示该二进制位。
LCD_ShowBinNum 函数通过循环依次提取无符号整数的每一位二进制值,将其转换为对应的 ASCII 码字符,然后在 LCD1602 的指定位置显示出来,实现了二进制数的显示功能。
13.总结
本章全面介绍 LCD1602,包括硬件特性、指令、使用方法及代码实现。讲解各引脚功能、常用指令作用,给出初始化等代码。多个显示函数可实现字符、数字等显示。
建议:深入研究指令,优化代码,添加注释,使用LCD1602显示数字、字符等。