【I²C】驱动 LCD1602 液晶屏幕
本例程将介绍如何使用基于 IIC 接口驱动的 LCD1602 液晶屏。
LCD1602 液晶屏
LCD1602 是很多单片机爱好者较早接触的字符型液晶显示器
602 液晶屏的称呼来自于其显示的内容容量,其中的 16 代表每行的字符(数字或英文字符)数,
02 代表 屏幕一共两行,实际开发中根据需要显示信息的内容多少不但可以选用 1602 屏,还可以选用诸如 2004 屏等。
如下图:

1602 液晶显示屏除了电源、地以外,有 3 个控制引脚 RS R/W E 和 8 个数据引脚 DB0-7。

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 口的占用
硬件电路设计
如下图:

注意需要使用开发板上的 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. 在屏幕上显示 Hellow, 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());
}
}
}