LCD ( Liquid Crystal Display 的简称)液晶显示器。能够同时显示16x2,32个字符,是一种专门用来显示字母、数字、符号等的点阵型液晶模块。
LCD1602液晶显示器是广泛使用的一种字符型液晶显示模块。它是由字符型液晶显示屏(LCD)、控制驱动主电路HD44780及其扩展驱动电路HD44100,以及少量电阻、电容元件和结构件等装配在PCB板上而组成。该显示屏的优点是耗电量低、体积小、辐射低。
作为嵌入式中非常非常常用的显示硬件,对于它的各种稀奇古怪的报错却是鲜有系统性的总结,本文lz目的就在于总结比较常见并给出笔者实践过的部分解决方法的思路,如有别的好的解决方法,欢迎各位在评论区补充。(笔者因为遇到了非常多的报错,也挺不容易的,希望大家多多支持笔者的博客笔记,一起学习)
-----------------------------------------------------------------------------------------------
首先介绍一下LCD1602的一些基本信息和使用方式。
首先介绍LCD的发光原理:背光灯发出白色光,经过偏光片对光线进行初步帅选,经过玻璃板到达液晶控制电路,光线穿过液晶间隙,外界施加到液晶的电压,会改变光线的偏转角度,光线在经过玻璃板,穿过偏光片,最终投射到红绿蓝三基色偏光片上,展示出不同的颜色,最终形成一个显示单位,称为像素点。
液晶本身不发光,需要背光灯提供光源,光线经过一系列处理才到输出,输出的光线强度要比光源的强度低很多,能量利用率较低,(比CRT显示器节能),液晶显示的处理方式,会导致显示方向较窄,对应它的视角较小,从侧面看屏幕会看不清他的显示内容,色彩转换,液晶分子转动也需要消耗一定的时间,导致屏幕的响应速度低。
接下来介绍LCD的相关引脚:
1.VSS:接地线;
2.VDD:接电源正极,一般接5V;
3:VO(图中为VE):液晶屏对比度调整,接电源正极时对比度最弱,接地时对比度最强;
4.RS:写入数据/命令选择线;
5.RW:读/写选择线;
6.E:使能端线;
7.D0~D7:8位数据线;
9.A:背光源正极;
10.K(C):背光源负极。
接下来介绍LCD的两种操作:读/写操作。
1、写操作:
写操作是LCD最常用的操作。而因此我们将其再分为两块:写数据、写命令,但二者原理其实相同。
首先说明的是当E线处于低电平时,是无法往其中写入内容(数据/命令)的。当写操作开始时候,拉低RW表示我们选择写入数据,再根据需求拉低或者抬高RS的电平,当RS选中高电平时候,我们进行写入数据的操作,当RS选择低电平的时候,我们进行写入命令的操作。之后我们再拉高E使能线的电平,给一个上升沿触发信号,将数据/命令送入LCD,再延时一小会,让LCD接收数据。之后拉低使能线电平,给到下降沿触发信号,延时让数据发送到LCD。
以下是写入指令的代码:
void WriteCmd(uint8_t cmd)
{
RS = 0; //选择写指令
RW = 0; //选择写
E = 0; //E使能拉低
P0 = cmd; //把指令送入P0
delay_ms(2); //延时一小会儿,让1602准备接收数据
E = 1; //使能线电平变化上升沿,命令送入1602的8位数据口
delay_ms(2);//延时,让数据发送
E = 0; //使能线拉低
}
以下是写入数据的代码:
void WriteData(uint8_t data)
{
RS = 1; //选择写数据
RW = 0; //选择写
E = 0; //E使能拉低
P0 = data; //把指令送入P0
delay_ms(2); //延时一小会儿,让1602准备接收数据
E = 1; //使能线电平变化上升沿,命令送入1602的8位数据口
delay_ms(2);//延时,让数据发送
E = 0; //使能线拉低
}
2、读操作:
相比于写操作而言,读操作的使用频率往往低很多,但它依旧是很有用的。当读操作开始时候,拉高RW表示我们选择读取数据,之后我们再拉高E使能线的电平,给一个上升沿触发信号,让LCD的数据传入单片机,再延时一小会,让单片机接收数据。之后拉低使能线电平,给到下降沿触发信号,延时。
接下来关于对LCD初始化,我们进行以下的代码实现,注意8位和4位不同的设置方法。
void lcd_Init()
{
WriteCmd(0x38); // 16*2显示,5*7点阵,8位数据口
/*WriteCmd(0x28); // 16*2显示,5*7点阵,4位数据口*/
WriteCmd(0x0C); // 开显示,不显示光标
WriteCmd(0x06); // 地址加1,当写入数据后光标右移
WriteCmd(0x01); // 清屏
}
接下来是移动光标地址的代码,在移动光标时候,我们相应的命令为0x80 + col,如果是第二行,指令则为0x80 + 0x40 +col。
void lcd_locate(uint8_t line,uint8_t column)
{
// 第一行
if(line==0)
LcdWriteCmd(0x80+column);
// 第二行
if(line==1)
LcdWriteCmd(0x80+0x40+column);
}
最后介绍相应的输出字符串的代码:
void lcd_PrintStr(const uint8_t *str)
{
while(*str!='\0')
LcdWriteData(*str++);
}
接下来介绍LCD1602的字库表,如图:
接下来介绍一些比较常见的报错:
首先,说在前面,代码一般是不太会出错的,主要是硬件连接和硬件故障的问题。
1、LCD根本没亮
首先LCD没亮的话,A和K的背光板的电源和LCD的电压VSS、VDD正负极一定是接错的。然后一般使用者前面的接线也是有问题的,比如RS和RW接到数据线上去了,接错串口了(该情况常见于4位输入的情况)。
也有可能是LCD对应的串口没设置好,比如一般我们串口是设置为推挽输出,查看我们的推挽输出是否给到了对应的电平。
需要在硬件连接和软件代码两方面都要仔细检查。
2、LCD亮,但没有方块,全是亮的
这种情况有以下几种可能:
1.VO对比度设置太低,可以通过增大对比度的方式调节,通常我们在VO与地之间接入一个电位器,可以给到一个比较容易调节的阻值,一般选择量程大于5k的电位器;
2.初始化后没有数据输入,所以显示为空白;
3.查看硬件是否短路,将万用表红黑表笔接到VSS和VDD上进行检查;
3、LCD亮,但只有第一行有黑格
这个问题一般和初始化有错有关,一般的问题主要是两个:
1.编写的初始化代码有误,详见上面的初始化代码作为参考;
2.接线有误导致无法正常输入初始化数据。
3.VO对比度设置太高,可以通过降低对比度的方式调节,通常我们在VO与地之间接入一个电位器;
此外,当你在输入完数据/指令后,拔出LCD电源,再次插入,不进行系统重启和新的输入时,你也能观察到这个现象。
4、输入字符出现乱码或者日文
一般是接线是不太可能有问题的,因为一般来说,接线有问题的话直接就停留在上一个阶段了,根本完成不了初始化,但不排除接线问题的可能。
一般是复位的问题,或者是延时过短,数据顺利无法写入。
由于LCD复位后默认是8线输入,如果函数LCD_write_command是一个按四线输入写入的函数,那么执行LCD_write_command(0x28);等效为先写入8线指令0x20;写入后LCD已经改为了四线模式,再写入4线指令8x的高四位,但是不再有低四位写入,所以之后写入其他数据时,LCD仍在等待上一个指令的低四位。这样就导致了不能正常的进行后面的操作。
解决方法:先执行LCD_write_command(0x02)(0x20应该也是可以的),先是写入了8线指令2,再写入8线指令0`,这样就将LCD改为4线传输,在紧跟这步之后,写入0x32,之后按4线写数据时,就没有高低错乱的问题。
5、双数位置字符无法显示(极少见,据笔者所知,就本人遇到过)
这个应该只有使用mbed的同僚会遇到,因为mbed是用C语言写出程序逻辑,再转写为可识别的.bin文件,这个过程是很未知的,而不是像keil一样直接对寄存器或者使用HAL库对寄存器进行修改,比较清晰。这种容易引发问题。
以笔者的STM32L432为例,笔者因为使用了原本作为模拟输出的A5~7作为数字输出,应该是启动了引脚复用导致了LCD的数据引脚被间歇性制空或者被失能,导致了输出有误。
解决的思路主要是重新启用对应原本用于数字输出的D管脚,而不要用A引脚,即使要用A引脚,也要用数字偏小的,笔者实测有效的。