IIC 驱动 LCD1602 液晶屏幕

罗大富 BigRich大约 7 分钟ESP32Arduino

上一课介绍了如何使用四位数码管模块来显示数字,本课将进一步介绍如何使用基于 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.hLiquidCrystal_I2C.cpp 两个文件,我们只需要把这两个文件放在项目的文件夹中即可。

Arduino 项目的创建非常简单,就是打开 Arduino IDE 后保存,它会在你选择的位置新建一个文件夹,这就是项目的文件夹。

.h.cpp 文件到底是什么?在 Arduino 开发中,.h.cpp 文件同样是用于代码组织和模块化的文件类型,但在 Arduino 环境中有些特殊的用法和约定。

  1. .h 文件(头文件):在 Arduino 中,头文件通常包含库、类、函数和变量的声明。头文件的目的是为了让多个源代码文件可以共享相同的声明,以便在程序中使用这些声明而无需重复编写。头文件通常使用预处理器指令 #include 引入到源文件中。在 Arduino 库中,.h 文件中通常包含类的声明、常量定义、函数原型等。

  2. .cpp 文件(源文件):在 Arduino 中,.cpp 文件是用于存放函数和类的实现代码的文件。.cpp 文件中包含了类成员函数的具体实现和其他函数的定义。通常,Arduino 项目中的 .cpp 文件中会包含 .h 文件,以便使用其中的声明。

需要注意的是,Arduino 开发中的 .h.cpp 文件的约定与传统的 C++ 开发并不完全相同。Arduino IDE 会在编译过程中将 .ino 文件转换为 .cpp 文件,并将其中的代码放置在全局范围。因此,在 Arduino 项目中,.ino 文件也可以包含全局变量和函数,而不仅限于 .cpp 文件。

总结起来,.h 文件是用于声明库、类、函数和变量的头文件,而 .cpp 文件是用于实现函数和类的源文件。这样的组织方式有助于提高代码的可读性、可维护性和重用性。

接着,我们就可以了解 LiquidCrystal_I2C 库的使用:

  1. LiquidCrystal_I2C(uint8_t addr, uint8_t cols, uint8_t rows):构造函数,用于构造 LCD I2C 对象,参数:addr 是地址,默认的是 0x27,cols 是 LCD 显示的列数,rows 是 LCD 显示的函数;
  2. void init():初始化显示屏;
  3. void clear():清除 LCD 屏幕上内容,并将光标置于左上角;
  4. void home():将光标在定位在屏幕左上角;
  5. void noBacklight()void backlight():是否开启背光;
  6. print():显示内容;
  7. void leftToRight()void rightToLeft():控制文字显示的方向,默认是从左向右;
  8. void noDisplay()void display():关闭显示或恢复显示(内容不会丢失);
  9. void setCursor(uint8_t col, uint8_t row):设置光标的位置,列,行,基于 0;
  10. void noCursor()void cursor:显示与不显示光标,默认不显示;
  11. 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():

  1. Serial.available():返回串口缓冲区中当前剩余的字符个数。一般用这个函数来判断串口的缓冲区有无数据,当 Serial.available()>0 时,说明串口接收到了数据,可以读取;
  2. 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());
    }
  }
}
上次编辑于:
贡献者: Luo