Arduino驱动双LCD屏:共享总线与I2C方案详解
2026/8/19 4:20:07 网站建设 项目流程

1. 项目概述:为什么需要连接两块LCD屏?

在Arduino的众多项目中,单块16x2字符液晶屏(LCD)的应用已经非常普遍,它能显示两行共32个字符,用于显示传感器读数、菜单选项或简单的状态信息绰绰有余。但当你手头的项目信息量增大,或者希望构建一个更直观、信息分层更清晰的人机界面时,单块屏幕的显示区域就显得捉襟见肘了。比如,你想同时监控温湿度传感器的实时数据和历史曲线趋势,或者在一个小型控制台上同时显示系统状态和详细的参数配置菜单。这时候,将两块甚至更多的LCD屏连接到一个Arduino上,就成了一种非常实用且成本可控的解决方案。

这个项目的核心,就是解决Arduino Uno这类微控制器I/O引脚资源有限的问题。一块标准的基于HD44780驱动芯片的16x2 LCD,如果使用4位数据模式,也需要至少6个数字引脚(RS, EN, D4, D5, D6, D7)。连接两块的话,如果简单粗暴地各自独立连接,就需要12个数字引脚,这几乎耗尽了Arduino Uno的所有数字I/O,留给传感器、执行器的引脚就所剩无几了。因此,如何高效、智能地复用引脚,或者利用其他通信协议来驱动多块屏幕,就成了本项目的技术关键。这不仅仅是简单的连线,更涉及到对LCD驱动原理、总线共享、软件库应用等知识的深入理解。接下来,我将拆解几种主流且可靠的实现方案,从原理到实操,一步步带你完成这个扩展显示系统的搭建。

2. 核心方案选型与原理剖析

面对连接两块LCD的需求,我们主要有三种技术路径,每种都有其适用的场景和优缺点。选择哪种方案,取决于你的项目对引脚数量、编程复杂度和响应速度的具体要求。

2.1 方案一:独立引脚驱动(最直接但最耗资源)

这是最容易想到的方法,即为每一块LCD分配一组完全独立的I/O引脚。例如,LCD1使用引脚2-7,LCD2使用引脚8-13。在代码中,你需要初始化两个LiquidCrystal对象,分别指定其引脚。

原理与局限: 这种方法在软件层面最简单明了,两块屏幕完全独立,互不干扰。但其缺点显而易见:极度消耗宝贵的I/O资源。对于Arduino Uno(14个数字I/O)或Nano这类核心板,这几乎是一种“奢侈”的方案,因为它严重限制了项目连接其他外设的能力。除非你的项目极其简单,只需要这两块屏幕而无需其他输入输出,否则不推荐作为首选。

2.2 方案二:共享数据总线,独立控制线(最经典与平衡的方案)

这是驱动多块HD44780 LCD最经典和推荐的方法。其核心思想是:让两块LCD共享同一组4位或8位数据总线(D0-D7或D4-D7),但为每一块LCD提供独立的寄存器选择(RS)和使能(EN)引脚。

工作原理深度解析: HD44780控制器内部有指令寄存器(IR)和数据寄存器(DR)。RS引脚的作用就是告诉控制器:“现在发送的是命令(RS=0)还是数据(RS=1)”。EN引脚是一个下降沿触发信号,用于锁存数据总线上的数据。

当我们让两块屏共享数据总线时,它们“看到”的数据流是完全一样的。那么如何区分这条指令是发给A屏还是B屏呢?答案就在于独立的RS和EN引脚。在发送数据或指令前,我们通过Arduino的I/O口,只将目标屏幕的EN引脚拉高,而保持另一块屏幕的EN引脚为低。这样,只有EN被拉高的那块屏幕才会响应随后在数据总线上出现、并在EN下降沿被锁存的数据。通过分别控制两块屏的RS和EN,我们就能像指挥两个共享同一电话线但拥有独立铃铛的座机一样,精准地对它们进行分别“喊话”和“通信”。

优势: 这种方法在引脚使用上非常经济。假设使用4位模式,我们需要4个数据引脚(D4-D7),加上两块屏各自的RS和EN引脚,总共只需要4 + 2*(1 RS + 1 EN) = 8个数字引脚。相比独立驱动的12个引脚,节省了4个,这为连接其他模块留出了宝贵空间。

2.3 方案三:通过I2C/IIC适配器驱动(最节省引脚,硬件略复杂)

这是目前非常流行的一种方式,尤其适合I/O资源紧张的项目。你需要为每一块LCD屏购买并焊接一个I2C转接板(通常是一个蓝色的小模块,上面有一个电位器用于调节对比度)。

原理与优势: I2C是一种两线式串行总线,只需要两根信号线(SDA-数据线,SCL-时钟线)就能与多个设备通信。每个I2C设备都有一个唯一的地址。LCD的I2C转接板本质上是一个“翻译官”,它把Arduino通过I2C总线发送过来的指令和数据,翻译成HD44780能理解的并行信号,驱动LCD屏幕。

连接两块屏时,你需要确保它们的I2C地址不同。大多数模块默认地址是0x27或0x3F,并且通常提供了1到3个地址选择焊盘(A0, A1, A2),通过焊接这些焊盘来改变地址。例如,将第一块模块的A0断开(地址0x27),将第二块模块的A0短接(地址可能变为0x26),这样它们就在I2C总线上拥有了独立的“门牌号”。

优势: 无论连接多少块LCD,都只占用Arduino的两个I/O引脚(SDA, SCL)。编程时使用专用的LiquidCrystal_I2C库,通过地址来区分不同的屏幕对象,代码非常简洁。缺点是增加了额外的硬件成本(每个转接板约几元人民币),并且需要你具备基础的焊接能力来设置地址。

注意:I2C模块上的对比度电位器非常重要,如果上电后屏幕只亮背光无字符,首先应调节此电位器。

3. 硬件连接详解与电路搭建

我们将以最经典、最通用的方案二(共享数据总线)为例,进行详细的硬件连接说明。这是理解多屏驱动原理的基础,掌握了它,其他方案便能触类旁通。

3.1 所需物料清单

  • Arduino Uno 开发板 x1
  • 16x2 LCD 屏幕(基于HD44780) x2
  • 10KΩ 电位器 x2 (用于分别调节两块屏的对比度)
  • 面包板 x1
  • 杜邦线(公对公) 若干
  • 220Ω 电阻 x2 (可选,用于限流保护背光LED,如果屏幕背光已内置电阻则可省略)

3.2 共享数据总线连接图与步骤

假设我们使用4位数据模式,这是最节省引脚且最常用的模式。

引脚定义与分配

  • 共享数据总线:Arduino 数字引脚D4, D5, D6, D7分别连接到两块LCD的D4, D5, D6, D7引脚
  • 独立控制线
    • LCD1: RS -> ArduinoD8, EN -> ArduinoD9
    • LCD2: RS -> ArduinoD10, EN -> ArduinoD11
  • 公共连接
    • VSS(GND): 两块LCD的引脚1(VSS)和引脚5(R/W,我们通常接地使其始终处于写入模式)连接到Arduino的GND。
    • VDD(VCC): 两块LCD的引脚2(VDD)连接到Arduino的5V。
    • V0(对比度): 两块LCD的引脚3(V0)分别连接到一个10K电位器的中间抽头。电位器两端分别接5V和GND。务必为每块屏单独配置一个电位器,因为它们的对比度特性可能有细微差别,独立调节才能保证两块屏显示都清晰。
    • 背光: 引脚15(A/ LED+)通过一个220Ω电阻连接到5V,引脚16(K/ LED-)连接到GND。如果屏幕背光已集成限流电阻,则可直接连接。

连接核对表

Arduino 引脚连接到 LCD1连接到 LCD2功能说明
D4引脚11 (D4)引脚11 (D4)数据位4(共享)
D5引脚12 (D5)引脚12 (D5)数据位5(共享)
D6引脚13 (D6)引脚13 (D6)数据位6(共享)
D7引脚14 (D7)引脚14 (D7)数据位7(共享)
D8引脚4 (RS)--LCD1 寄存器选择
D9引脚6 (EN)--LCD1 使能信号
D10--引脚4 (RS)LCD2 寄存器选择
D11--引脚6 (EN)LCD2 使能信号
5V引脚2 (VDD), 引脚15 (LED+)引脚2 (VDD), 引脚15 (LED+)电源与背光正极
GND引脚1 (VSS), 引脚5 (R/W), 引脚16 (LED-)引脚1 (VSS), 引脚5 (R/W), 引脚16 (LED-)电源地与背光负极

实操心得: 在面包板上搭建电路时,建议先完成第一块屏(LCD1)的所有连接并测试正常,再接入第二块屏的独立控制线(RS, EN),最后将第二块屏的数据引脚并联到已有的数据总线上。这种“分步验证”的方法,能在出现问题时快速定位是哪个环节出了错。连接数据总线时,确保杜邦线接触牢固,避免虚接导致显示乱码。

4. 软件编程与库函数应用

硬件连接妥当后,软件就是指挥两块屏幕协同工作的“大脑”。我们将使用Arduino IDE自带的LiquidCrystal库。

4.1 库的初始化与对象创建

在代码开头,我们需要包含库并创建两个LiquidCrystal对象。关键点在于,创建对象时,我们为两个对象指定相同的4个数据引脚,但不同的RS和EN引脚

#include <LiquidCrystal.h> // 初始化LCD对象 // 格式:LiquidCrystal lcd(rs, en, d4, d5, d6, d7); LiquidCrystal lcd1(8, 9, 4, 5, 6, 7); // LCD1 使用 D8(RS), D9(EN) LiquidCrystal lcd2(10, 11, 4, 5, 6, 7); // LCD2 使用 D10(RS), D11(EN)

4.2 初始化设置与基础显示

setup()函数中,我们需要分别初始化两块屏幕,设置显示模式,并可以显示一些初始信息。

void setup() { // 初始化LCD1, 16列2行 lcd1.begin(16, 2); lcd1.print("Screen 1: Hello!"); // 初始化LCD2, 16列2行 lcd2.begin(16, 2); lcd2.print("Screen 2: Ready."); delay(2000); // 显示初始信息2秒 // 清屏,准备主循环显示 lcd1.clear(); lcd2.clear(); }

为什么需要分别调用begin()begin()函数会向LCD发送一系列初始化指令,设置数据长度、显示行数、光标是否显示等。由于我们通过独立的EN引脚来控制通信对象,因此对lcd1.begin()的调用只会初始化第一块屏,对lcd2.begin()的调用只会初始化第二块屏。库函数内部在每次通信前,都会通过我们传入的rsen引脚参数来操作对应的物理引脚。

4.3 主循环中的独立控制示例

loop()函数中,你可以像控制单块屏幕一样,分别控制lcd1lcd2。以下是一个简单的示例,让两块屏分别显示一个递增的计数器和传感器模拟值。

void loop() { // 控制LCD1:显示一个计数器 static int counter = 0; lcd1.setCursor(0, 0); // 将光标移动到LCD1的第1行第1列 lcd1.print("Counter: "); lcd1.print(counter); counter++; // 控制LCD2:显示一个模拟输入值(假设传感器接在A0) int sensorValue = analogRead(A0); lcd2.setCursor(0, 0); // 将光标移动到LCD2的第1行第1列 lcd2.print("Sensor A0: "); lcd2.print(sensorValue); // 在两块屏的第二行显示一些固定信息 lcd1.setCursor(0, 1); lcd1.print("Status: OK"); lcd2.setCursor(0, 1); lcd2.print("Voltage: --V"); delay(1000); // 每秒更新一次 // 为了演示清屏和重新定位,每10秒清空一次LCD2的第二行 static unsigned long lastClear = 0; if (millis() - lastClear > 10000) { lcd2.setCursor(0, 1); lcd2.print(" "); // 用空格清除一行 // 或者用 lcd2.clear(); 但这样会清空整个屏幕,需要重新打印所有内容 lastClear = millis(); } }

编程技巧:避免显示闪烁频繁使用clear()函数会导致屏幕闪烁。更优的做法是:

  1. 局部更新:只更新变化的数据部分。例如计数器,可以先在旧数字位置打印空格,再打印新数字。
  2. 格式化输出:对于数字,可以使用lcd.print(number, DEC)或固定宽度打印,确保数字位数变化时不会残留旧字符。
    // 更好的计数器显示方式 lcd1.setCursor(9, 0); // “Counter: ”后面是第9列 if (counter < 10) { lcd1.print(" "); // 补两个空格 } else if (counter < 100) { lcd1.print(" "); // 补一个空格 } lcd1.print(counter);

5. 方案三(I2C)的快速实现指南

如果你选择了更节省引脚的I2C方案,硬件和软件会有所不同。

5.1 硬件连接

  1. 为每块LCD焊接好I2C转接板,并务必设置不同的I2C地址。参考模块说明书,通常通过焊接A0/A1/A2焊盘来设置。
  2. 连接电路:
    • ArduinoSDA(A4引脚) -> 连接所有I2C模块的SDA。
    • ArduinoSCL(A5引脚) -> 连接所有I2C模块的SCL。
    • Arduino5V-> 连接所有I2C模块的VCC。
    • ArduinoGND-> 连接所有I2C模块的GND。
    • LCD的背光引脚(LED+/-)通常已在转接板上连接好。

5.2 软件编程

你需要安装LiquidCrystal_I2C库(可通过Arduino IDE的库管理器搜索安装)。

#include <Wire.h> #include <LiquidCrystal_I2C.h> // 初始化对象,参数:(I2C地址, 列数, 行数) // 地址需要根据你的模块设置来填写,常用0x27或0x3F,通过修改焊盘可能得到0x26, 0x25等 LiquidCrystal_I2C lcd_i2c_1(0x27, 16, 2); // 第一块屏,地址0x27 LiquidCrystal_I2C lcd_i2c_2(0x26, 16, 2); // 第二块屏,地址0x26 void setup() { // 初始化I2C通信 Wire.begin(); // 初始化LCD,并打开背光 lcd_i2c_1.init(); lcd_i2c_1.backlight(); lcd_i2c_1.print("I2C Screen 1"); lcd_i2c_2.init(); lcd_i2c_2.backlight(); lcd_i2c_2.print("I2C Screen 2"); } void loop() { // 控制方法与标准库类似,使用 lcd_i2c_1 和 lcd_i2c_2 对象即可 // 例如: lcd_i2c_1.setCursor(0, 1); lcd_i2c_1.print(millis() / 1000); }

6. 常见问题排查与调试技巧

即使按照步骤操作,第一次尝试也可能会遇到问题。以下是基于大量实操经验总结的排查清单。

6.1 屏幕无任何显示(背光也不亮)

  • 检查电源:用万用表测量LCD的VCC和GND引脚之间是否有5V电压。检查面包板电源轨连接是否通畅。
  • 检查背光:确认LED+和LED-是否正确连接。如果通过电阻连接,尝试减小电阻值(如从220Ω换为100Ω)增加亮度,但注意电流不要超过引脚承载能力(通常20mA)。

6.2 屏幕背光亮,但无字符(显示全白方块或空白)

  • 调节对比度:这是最常见的原因!立即缓慢旋转电位器,直到字符出现。两块屏的“最佳对比度”点可能不同,需分别调节。
  • 检查接线:重点检查RS、EN、D4-D7这6根(4位模式)核心信号线是否与代码定义、物理连接一一对应,有无接错、虚接。
  • 检查初始化:确保在setup()中正确调用了lcd.begin(16,2)

6.3 显示乱码(非预期字符)

  • 时序问题:在setup()中初始化LCD后,添加一个短暂的延时delay(100),给LCD足够的启动时间。
  • 电源噪声:在Arduino的5V和GND之间,靠近LCD电源引脚处,并联一个10uF-100uF的电解电容,可以稳定电源,消除噪声干扰。
  • 数据线干扰:确保数据线不要太长,且不要与电机、继电器等大电流器件导线紧挨着平行走线。

6.4 只有一块屏幕工作

  • 检查独立控制线:确认不工作的那块屏幕的RS和EN引脚是否独立且正确地连接到了Arduino,并且代码中创建对象时使用的引脚号与之匹配。
  • 检查共享数据线:确认不工作的屏幕其D4-D7引脚是否与另一块屏和Arduino都可靠连接。
  • 库对象操作错误:在代码中,确保你对lcd1lcd2的操作没有弄混。例如,想写屏2却调用了lcd1.print()

6.5 I2C屏幕无法找到地址

  • 扫描I2C地址:上传一个I2C扫描程序,确认你的模块地址是否正确。
    #include <Wire.h> void setup() { Wire.begin(); Serial.begin(9600); while (!Serial); Serial.println("I2C Scanner..."); } void loop() { byte error, address; int nDevices = 0; for(address = 1; address < 127; address++ ) { Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address<16) Serial.print("0"); Serial.print(address,HEX); Serial.println(); nDevices++; } } if (nDevices == 0) Serial.println("No I2C devices found"); delay(5000); }
  • 检查焊接与接线:确认I2C模块地址焊盘设置正确,SDA、SCL线连接无误且上拉电阻正常(许多模块已集成,若没有,需要在SDA和SCL线上各接一个4.7KΩ电阻上拉到5V)。

7. 项目进阶与扩展思路

成功驱动两块基础LCD只是起点,你可以在此基础上构建更复杂的交互系统。

思路一:构建分层菜单系统将第一块屏(LCD1)作为主菜单导航屏,显示可选项(如“设置参数”、“查看数据”、“系统信息”)。将第二块屏(LCD2)作为内容显示屏,根据LCD1上光标选中的项目,动态显示详细内容或参数设置界面。这需要你实现一个状态机来管理菜单逻辑。

思路二:主从式信息显示在一个监控系统中,让LCD1始终显示最关键、最需要实时关注的核心数据(如当前温度、紧急报警状态)。让LCD2轮播显示或通过按钮切换显示次要或详细数据(如过去一小时的平均值、不同传感器的读数、系统日志)。这样信息层次分明,用户体验更好。

思路三:结合其他输入设备8个引脚驱动两块屏的方案,依然为Arduino Uno留下了6个数字I/O和6个模拟输入。你可以轻松接入:

  • 旋转编码器:用于滚动菜单、调整数值,比按键操作更直观。
  • 按键矩阵:实现多个功能按键。
  • 传感器阵列:如DHT11(温湿度)、DS18B20(温度)、光敏电阻等,将采集的数据分类显示在两块屏上。

实操心得:电源管理当项目中外设增多时,务必注意电源总电流。Arduino Uno的5V引脚从USB或稳压器取电,总输出能力有限(约500mA-1A,具体看供电方式)。两块LCD的背光LED是耗电大户,每个可能消耗20-60mA。如果同时驱动多块屏且背光全开,加上其他传感器和芯片,可能接近或超过极限。如果出现屏幕闪烁、Arduino重启等情况,可以考虑使用外部5V电源(如手机充电器配合DC接口)为整个系统供电,或者降低LCD背光亮度(在背光回路串联更大电阻)。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询