8*8LED
点阵、串口通信。使用它们可以直观显示一些字符数据,但是它们也有各种局限性,比如显示字符数据太少,硬件设计复杂、代码编写难度大等。这一章就来介绍一种非常简单且常用的显示装置– LCD1602
液晶显示器,使用它可以显示更多的字符数字。我们开发板上集成了一个 LCD1602
液晶显示器接口,将 LCD1602
液晶对应插入即可。本章要实现的功能是:系统运行时,在 LCD1602
液晶上显示字符信息。
1602 液晶也叫 1602 字符型液晶,它能显示 2 行字符信息,每行又能显示 16个字符。它是一种专门用来显示字母、数字、符号的点阵型液晶模块。它是由若干个 5x7
或者 5x10
的点阵字符位组成,每个点阵字符位都可以用显示一个字符,每位之间有一个点距的间隔,每行之间也有间隔,起到了字符间距和行间距的作用,正因为如此,所以它不能很好的显示图片。其实物图如下所示:
在上图中可以看到有 16
个管脚孔,从左至右管脚编号顺序是 1-16
,其功能定义如下所示:
下面对几个管脚做下说明:
LCD1602
的显示对比度,一般会外接电位器用以调整偏压信号,注意此脚电压为 0 时可以得到最强的对比度。1602
进行数据字节的传输操作,而为低电平时,则是进行命令字节的传输操作。命令字节,即是用来对 LCD1602
的一些工作方式作设置的字节;数据字节,即使用以在 1602
上显示的字节。值得一提的是,LCD1602
的数据是 8 位的。LCD1602
进行读数据操作,反之进行写数据操作LCD1602
的数据控制时钟信号,利用该信号的上升沿实现对 LCD1602
的数据传输。51
单片机一组 IO
也是 8 位,使得对 LCD1602
的数据读写大为方便。 在 LCD1602
内部含有 80
个字节的 DDRAM
,它是用来寄存显示字符的。其地址和屏幕的对应关系如下表:
从上图可知,不是所有的地址都可以直接用来显示字符数据,只有第一行中的 00-0F
,第二行中的 40-4F
才能显示,其他地址只能用于存储。要显示字符时要先输入显示字符地址,也就是告诉模块在哪里显示字符,例如第二行第一个字符的地址是 40H
,那么是否直接写入 40H
就可以将光标定位在第二行第一个字符的位置呢?这样不行,因为写入显示地址时要求最高位 D7
恒定为高电平 1 。所以实际写入的数据应该是
01000000B(40H)+10000000B(80H)=11000000B(C0H)
在 1602
中我们就用前 16
个就行了。第二行也一样用前 16
个地址。
在使用 LCD1602
时,我们需要掌握一些常用的指令,这些指令对于 LCD1602
初始化是必须的。
DDRAM
的内容全部填入"空白"的 ASCII
码 20H
;AC
)的值设为 0。模式设置指令:0x06
功能:设定每次写入 1 位数据后光标的移位方向,并且设定每次写入的一个字符是否移动。
I/D
:0=写入新数据后光标左移; 1=写入新数据后光标右移,这里选择1S
:0=写入新数据后显示屏不移动; 1=写入新数据后显示屏整体右移 1 个字符,这里选择0显示开关控制指令:0x0c
功能:控制显示器开/关、光标显示/关闭以及光标是否闪烁。
D
:0=显示功能关; 1=显示功能开,这里选择1C
:0=无光标 ;1=有光标,这里选择0B
:0=光标闪烁; 1=光标不闪烁,这里选择0功能设定指令:0x38
功能:设定数据总线位数、显示的行数及字型。
DL
:0=数据总线为 4 位 ;1=数据总线为 8 位,这里设置为1N
:0=显示 1 行; 1=显示 2 行,这里设置为1F
:0=5×7 点阵/每字符 ;1=5×10 点阵/每字符,这里设置为0X
:表示任意值,这里选择0 要使用 LCD1602
,首先需要对其初始化,即通过写入一些特定的指令实现。然后选择要在 LCD1602
的哪个位置显示并将所要显示的数据发送到 LCD
的DDRAM
。使用 LCD1602
通常都是用于写数据进去,很少使用读功能。LCD1602
操作步骤如下所示:
(1)初始化
(2)写命令(RS=L),设置显示坐标
(3)写数据(RS=H)
在此,不需要读出它的数据的状态或者数据本身。所以只需要看两个写时序:
LCD1602
的工作方式时:需要把 RS
置为低电平,RW
置为低电平,然后将数据送到数据口 D0~D7
,最后 E
引脚一个高脉冲将数据写入。1602
上实现显示时:需要把 RS
置为高电平,RW
置为低电平,然后将数据送到数据口 D0~D7
,最后 E
引脚一个高脉冲将数据写入。 写指令和写数据,差别仅仅在于 RS
的电平不一样而已。以下是 LCD1602
的时序图:
从上图可以看到,以上给的时间参数全部是 ns
级别的,而 51
单片机的机器周期是 1us
,指令周期是 2-4
个机器周期,所以即便在程序里不加延时程序,也可以很好的配合 LCD1602
的时序要求了。
当要写命令字节的时候,时间由左往右,RS
变为低电平,R/W
变为低电平,注意看是 RS
的状态先变化完成。然后这时,DB0~DB7
上数据进入有效阶段,接着 E
引脚有一个整脉冲的跳变,接着要维持时间最小值为 tpw=400ns
的 E
脉冲宽度。然后E
引脚负跳变,RS
电平变化,R/W
电平变化。这样便是一个完整的LCD1602
写命令的时序。
本实验使用到硬件资源如下:
LCD1602
液晶接口,下面来看下开发板上 LCD1602
液晶接口电路,如下图所示:LCD1602
的 8 位数据口 DB0-DB7
与单片机的 P0.0-P0.7
管脚连接,LCD1602
的 RS
、RW
、E
脚与单片机的 P2.6
、P2.5
、P2.7
管脚连接。RJ1
是一个电位器,用来调节 LCD1602
对比度即显示亮度。LCD1602
接口设计,是可以兼容 4 位 LCD1602
的。对于非标准接口的 LCD1602
,我们也会通过转接板将其转接为对应开发板接口的。对于 4 位 LCD1602
在传输数据的时候需要将 8 位的数据截成两段,先发送高四位,在发送低四位。其它引脚操作方法不变。 本节所要实现的功能是:在 LCD1602
液晶上显示字符信息。
程序框架如下:
编写公共文件
打开public.c
,代码如下:
#include "public.h"
/*
函 数 名 : delay_10us
函数功能 : 延时函数,ten_us=1 时,大约延时 10us
输 入 : ten_us
输 出 : 无
*/
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
/*
函 数 名 : delay_ms
函数功能 : ms 延时函数,ms=1 时,大约延时 1ms
输 入 : ten_us
输 出 : 无
*/
void delay_ms(u16 ms)
{
u16 i,j;
for (i=ms;i>0;i--)
{
for (j=110;j>0;j--);
}
}
其头文件public.h
为
/*
头文件作用如下:包含头文件,定义全局变量,声明函数
*/
#ifndef _public_H
#define _public_H
#include "reg52.h"
//对系统默认数据类型进行重定义
typedef unsigned char u8;
typedef unsigned int u16;
//函数声明
void delay_10us(u16 ten_us);
void delay_ms(u16 ms);
#endif
编写 LCD1602 显示函数
编写主函数
打开 lcd1602.c
文件,代码如下:
#include "lcd1602.h"
/*
* 函 数 名 : lcd1602_write_cmd
* 函数功能 : LCD1602 写命令
* 输 入 : cmd:指令
* 输 出 : 无
*/
void lcd1602_write_cmd(u8 cmd)
{
LCD1602_RS=0;//选择命令
LCD1602_RW=0;//选择写
LCD1602_E=0;
LCD1602_DATAPORT=cmd;//准备命令
delay_ms(1);
LCD1602_E=1;//使能脚 E 先上升沿写入
delay_ms(1);
LCD1602_E=0;//使能脚 E 后负跳变完成写入
}
/*
* 函 数 名 : lcd1602_write_data
* 函数功能 : LCD1602 写数据
* 输 入 : dat:数据
* 输 出 : 无
*/
void lcd1602_write_data(u8 dat)
{
LCD1602_RS=1;//选择数据
LCD1602_RW=0;//选择写
LCD1602_E=0;
LCD1602_DATAPORT=dat;//准备数据
delay_ms(1);
LCD1602_E=1;//使能脚 E 先上升沿写入
delay_ms(1);
LCD1602_E=0;//使能脚 E 后负跳变完成写入
}
/*
* 函 数 名 : lcd1602_init
* 函数功能 : LCD1602 初始化
* 输 入 : 无
* 输 出 : 无
*/
void lcd1602_init(void)
{
lcd1602_write_cmd(0x38);//数据总线 8 位,显示 2 行,5*7 点阵/字符
lcd1602_write_cmd(0x0c);//显示功能开,无光标,光标闪烁
lcd1602_write_cmd(0x06);//写入新数据后光标右移,显示屏不移动
lcd1602_write_cmd(0x01);//清屏
}
/*
* 函 数 名 : lcd1602_clear
* 函数功能 : LCD1602 清屏
* 输 入 : 无
* 输 出 : 无
*/
void lcd1602_clear(void)
{
lcd1602_write_cmd(0x01);
}
/*
* 函 数 名 : lcd1602_show_string
* 函数功能 : LCD1602 显示字符
* 输 入 : x,y:显示坐标,x=0~15,y=0~1;
str:显示字符串
* 输 出 : 无
*/
void lcd1602_show_string(u8 x,u8 y,u8 *str) //lcd1602_show_string(0,0,"123456789")
{
u8 i = 0;
if (y>1 || x>15)return; //行列参数不对则强制退出
if (y<1) //第 1 行显示
{
while(*str!='\0')//字符串是以'\0'结尾,只要前面有内容就显示
{
//设置显示坐标
if(i<16-x)//如果字符长度超过第一行显示范围,则在第二行继续显示
{
lcd1602_write_cmd(0x80+i+x);//第一行显示地址设置
}
else
{
lcd1602_write_cmd(0x40+0x80+i+x-16);//第二行显示地址设置,0x40为第二行的首地址,0x80是固定的,写入显示地址时要求最高位 `D7` 恒定为高电平 1
}
lcd1602_write_data(*str);//显示内容
str++;//指针递增
i++;
}
}
else //第 2 行显示
{
while(*str!='\0')
{
if(i<16-x) //如果字符长度超过第二行显示范围,则在第一行继续显示
{
lcd1602_write_cmd(0x80+0x40+i+x);
}
else
{
lcd1602_write_cmd(0x80+i+x-16);
}
lcd1602_write_data(*str);//显示内容
str++;//指针递增
i++;
}
}
}
lcd1602_show_string
函数是 LCD1602
显示字符函数,该函数有 3 个入口参数,x 和 y 用来确定显示的坐标,x 范围是 0-15,y 范围是 0-1。str 参数是一个指针类型变量,用于传递显示的内容。函数内首先判断 xy 坐标是否有效,无效则强制退出,然后根据 xy 值在对应的坐标位置显示内容。
打开 main.c
文件,代码如下:
/*
实验名称:LCD1602 液晶显示实验
接线说明:
实验现象:下载程序后,LCD1602 上显示字符信息
注意事项:
*/
#include "public.h"
#include "lcd1602.h"
/*
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*/
void main(void)
{
lcd1602_init(); //LCD1602初始化
lcd1602_show_string(0,0,"Hello World!");//第一行显示
lcd1602_show_string(0,1,"0123456789");//第二行显示
while (1)
{
}
}
主函数代码非常简单,首先调用外设驱动头文件,然后进入 main 函数初始化 LCD1602
,并调用 LCD1602
显示函数让第一行显示"Hello World!"
,第二行显示"0123456789"
。
这里说明下,在开发中通常会显示数值,比如温度数据,程序中我们获取的温度数据通常存储在 float
变量中,并非字符串,此时需要想办法将 float
变量中的值转换为字符串,比如 float temp=31.2
,那么我们可以再定义一个数组temp_buf[]
用于保存 temp
中的每一个位,操作如下:
float temp=31.2;
u8 temp_buf[5];
u16 temp_value=temp*10;
temp_buf[0]=temp_value/100+0x30;
temp_buf[1]=temp_value%100/10+0x30;
temp_buf[2]='.';
temp_buf[3]=temp_value%100%10+0x30;
temp_buf[4]='\0';
lcd1602_show_string(0,0,temp_buf);
将浮点数转换为整型变量,然后将整型值的每一个位取出来存储在数组temp_buf
中,可以发现取出的每一位后面加了 0X30
,这个值是将数字0-9 转换为 ASCII
码,因为液晶是以 ASCII
码显示的。最后一个数组元素赋值了’\0’
,这是为了让 LCD1602
显示函数知道数组内结束位置。
根据 C 语言指针的特点,此时调用 LCD1602
显示函数时,只需要将数组名传递进去即可,因为数组名即为该数组的起始地址,然后显示函数内部通过指针访问数组的每一位。
使用 USB
线将开发板和电脑连接成功后(电脑能识别开发板上 CH340
串口),把编译后产生的.hex
文件烧入到芯片内,实现现象如下:在 LCD1602
液晶上显示字符信息。
注意:LCD1602
液晶要正确插入到 LCD1602
接口 J2
位置,插反或差错都会导致显示不正常。如果出现显示看不清,可调节板子 LCD1602
接口下的 RJ1
电位器。如果 LCD1602
无法显示,可重启电源或复位。