IIC 驱动 LCD1602 液晶屏幕
上一课介绍了如何使用四位数码管模块来显示数字,本课将进一步介绍如何使用基于 IIC 接口驱动的 LCD1602 液晶屏。
在前面章节,我们已经学习过两种显示装置,数码管和 4 位数码管。使用它们可以直观显示一些字符数据,但是它们也有各种局限性,比如显示字符数据太少,硬件设计复杂、代码编写难度大等。这一章就来介绍一种非常简单且常用的显示装置--LCD1602 液晶显示器,使用它可以显示更多的字符数字。
实验原理
1. LCD1602 液晶屏
LCD1602 是很多单片机爱好者较早接触的字符型液晶显示器,所以,在这里花点时间是值得的。
1602 液晶屏的称呼来自于其显示的内容容量,其中的 16 代表每行的字符(数字或英文字符)数,02 代表屏幕一共两行,实际开发中根据需要显示信息的内容多少不但可以选用 1602 屏,还可以选用诸如 2004 屏等。如下图:
1602 液晶显示屏除了电源、地以外,有 3 个控制引脚 RS R/W E 和 8 个数据引脚 DB0-7。
2. 认识 IIC(I2C)接口
由于 1602 的管脚数过多,如果直接与 ESP32 开发板连接需要占用大量的 GPIO 管脚,不但容易造成资源浪费,连接也非常不方便。
因此实际使用时往往会给 1602 屏增加一块 IIC 驱动版,将 1602 的 16 个管脚连接到由 PCF8574T 作为主要芯片的驱动版上,将接口转换为 IIC 再连接开发板,具体情况如上图所示。
IIC 是一种硬件设备间常用的接口通讯协议,全称是 Inter-Integrated Circuit,也可以写为 I2C。他的设计时的理念是:信号线尽量少并且速率要尽量高。 信号线少,可以减少引脚占用,这对早期的芯片(引脚很少)的很重要。
使用 IIC 接口时一共需要连接四根线,包括:VCC、GND、SDA、SCL,其中 SDA 和 SCL 需要占用 GPIO 管脚,连接到开发板上任何一组 IIC 接口的对应管脚都可以。
标准的 I2C 需要两根信号线:
- SCL(Serial Clock):时钟线,时钟都是有 master 提供的
- SDA(Serial Data):双向数据线,发数据或者收数据(收发不能同时)
简单来说,只需要 2 根线,就可以对多台设备传输大量数据,减少单片机上 IO 口的占用。
硬件电路设计
物料清单(BOM 表):
材料名称 | 数量 |
---|---|
带有 IIC 模块的 LCD1602 液晶屏 | 1 |
杜邦线(跳线) | 若干 |
将材料按照下图相连:
注意
注意需要使用开发板上的 5V 电压,而不是 3.3V。真实环境下使用 3.3V 会无法显示或者显示很暗。
这里连接的这两个引脚可不是随便连接的,而是对应了 ESP32 芯片原理图,ESP32 的 I2C 引脚的 SDA 对应 D21,SCL 对应 D22,如图:
软件程序设计
在 Arduino 中使用 I2C 控制 LCD1602 需要下载第三方代码 LiquidCrystal_I2C
,其中包括 LiquidCrystal_I2C.h
和 LiquidCrystal_I2C.cpp
两个文件,我们只需要把这两个文件放在项目的文件夹中即可。
Arduino 项目的创建非常简单,就是打开 Arduino IDE 后保存,它会在你选择的位置新建一个文件夹,这就是项目的文件夹。
.h
与 .cpp
文件到底是什么?在 Arduino 开发中,.h
和 .cpp
文件同样是用于代码组织和模块化的文件类型,但在 Arduino 环境中有些特殊的用法和约定。
.h
文件(头文件):在 Arduino 中,头文件通常包含库、类、函数和变量的声明。头文件的目的是为了让多个源代码文件可以共享相同的声明,以便在程序中使用这些声明而无需重复编写。头文件通常使用预处理器指令#include
引入到源文件中。在 Arduino 库中,.h
文件中通常包含类的声明、常量定义、函数原型等。.cpp
文件(源文件):在 Arduino 中,.cpp
文件是用于存放函数和类的实现代码的文件。.cpp
文件中包含了类成员函数的具体实现和其他函数的定义。通常,Arduino 项目中的.cpp
文件中会包含.h
文件,以便使用其中的声明。
需要注意的是,Arduino 开发中的 .h
和 .cpp
文件的约定与传统的 C++ 开发并不完全相同。Arduino IDE 会在编译过程中将 .ino
文件转换为 .cpp
文件,并将其中的代码放置在全局范围。因此,在 Arduino 项目中,.ino
文件也可以包含全局变量和函数,而不仅限于 .cpp
文件。
总结起来,.h
文件是用于声明库、类、函数和变量的头文件,而 .cpp
文件是用于实现函数和类的源文件。这样的组织方式有助于提高代码的可读性、可维护性和重用性。
接着,我们就可以了解 LiquidCrystal_I2C
库的使用:
LiquidCrystal_I2C(uint8_t addr, uint8_t cols, uint8_t rows)
:构造函数,用于构造 LCD I2C 对象,参数:addr
是地址,默认的是 0x27,cols
是 LCD 显示的列数,rows
是 LCD 显示的函数;void init()
:初始化显示屏;void clear()
:清除 LCD 屏幕上内容,并将光标置于左上角;void home()
:将光标在定位在屏幕左上角;void noBacklight()
与void backlight()
:是否开启背光;print()
:显示内容;void leftToRight()
与void rightToLeft()
:控制文字显示的方向,默认是从左向右;void noDisplay()
与void display()
:关闭显示或恢复显示(内容不会丢失);void setCursor(uint8_t col, uint8_t row)
:设置光标的位置,列,行,基于 0;void noCursor()
与void cursor
:显示与不显示光标,默认不显示;void noBlink()
与void blink()
:光标是否闪烁,默认不闪烁。
现在,我们就可以写代码了。
1. 在屏幕上显示 Hello,world
了解了第三方库之后,我们先写一个最简单的程序,比如,在屏幕上显示 Hello, world
,代码如下:
#include "LiquidCrystal_I2C.h"
// 设置 LCD1602 的地址,列数,行数
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup()
{
// 初始化 LCD 对象
lcd.init();
// 打印内容
lcd.backlight();
lcd.print("Hello, world!");
}
void loop()
{
}
2. 读取串口输入内容
在这个程序中,我们需要用到串口的另外两个方法 Serial.available()
与 Serial.read()
:
Serial.available()
:返回串口缓冲区中当前剩余的字符个数。一般用这个函数来判断串口的缓冲区有无数据,当Serial.available()>0
时,说明串口接收到了数据,可以读取;Serial.read()
指从串口的缓冲区取出并读取一个 Byte 的数据,比如有设备通过串口向 Arduino 发送数据了,我们就可以用Serial.read()
来读取发送的数据。
#include "LiquidCrystal_I2C.h"
// 设置 LCD1602 的地址,列数,行数
LiquidCrystal_I2C lcd(0x27,16,2);
void setup()
{
// 初始化 LCD 对象
lcd.init();
// 开启背光
lcd.backlight();
// 开启串口通信
Serial.begin(9600);
}
void loop()
{
// 检测是否有串口输入
if (Serial.available()) {
// 延时以等待所有数据传输完成
delay(100);
// 清屏
lcd.clear();
// 反复读取串口的数据并在 LCD1602 屏幕上显示,直到数据读完
while (Serial.available() > 0) {
lcd.write(Serial.read());
}
}
}