Intel Edison驱动LCD光标:I2C通信与串口控制实战指南
2026/7/29 16:50:11 网站建设 项目流程

1. 项目概述:当Intel Edison遇见LCD光标

几年前,当Intel Edison这块集成了Atom处理器和Quark微控制器的超小型计算模块横空出世时,很多硬件爱好者都为之兴奋。它比Arduino功能强大,又比树莓派更专注于物联网和嵌入式场景。官方为了让大家快速上手,提供了丰富的代码样例库,其中就包括了驱动LCD显示屏的示例。今天我们要深入拆解的,就是这个名为“Cursor”的LCD显示样例。

这个样例的核心目标非常明确:在连接到Edison的LCD屏幕上,实现一个可移动的光标,并能够通过串口指令控制其位置和显示内容。听起来简单,对吧?但这里面涉及了从硬件引脚连接、I2C通信协议理解,到Arduino IDE环境配置、库文件管理,再到具体的C++代码逻辑和串口通信解析等一系列知识点。很多新手拿到Edison和LCD屏后,照着官方英文文档操作,常常会在某个环节卡住,最终让开发板吃灰。这个中文版的深度解析,就是要帮你打通所有关节,不仅让“Cursor”跑起来,更要让你明白每一步背后的“为什么”。

它适合谁呢?如果你是刚接触Intel Edison或Arduino平台的嵌入式开发新手,想通过一个完整的项目练手;或者你手头有Edison和一块兼容的LCD(比如常见的1602或2004字符型LCD,带I2C转接板),想快速让它显示点东西;亦或是你对硬件与软件的交互过程感兴趣,那么这个内容就是为你准备的。我们将绕过那些晦涩的术语堆砌,用最直白的语言和可一步步“抄作业”的实操,带你完成这个项目。

2. 硬件准备与连接原理

在写第一行代码之前,正确的硬件连接是成功的基石。这一步错了,后面所有的调试都是徒劳。

2.1 核心硬件清单

你需要准备以下硬件:

  1. Intel Edison开发板:可以是Edison芯片模块配合Arduino扩展板,也可以是像Intel Edison for Arduino这样的集成板。它们核心的GPIO引脚定义是一致的。
  2. LCD显示屏:强烈推荐使用带有I2C接口转接板的字符型LCD,例如经典的1602(16字符×2行)或2004(20字符×4行)。这种屏将复杂的并行数据线和背光控制电路集成到了一块小小的转接板上,只需要4根线(VCC, GND, SDA, SCL)就能驱动,极大简化了连接。这也是官方样例默认使用的类型。
  3. 杜邦线:若干,用于连接。
  4. USB数据线:用于为Edison供电和进行串口通信。

注意:如果你使用的是不带I2C转接板的并行LCD,接线将变得非常复杂(需要连接多达10根数据和控制线),并且需要修改代码底层驱动。对于初学者,请务必购买带I2C转接板的型号,这是性价比最高、最省事的选择。

2.2 电路连接详解与“为什么”

连接示意图很简单:LCD的I2C转接板连接到Edison的I2C总线。但具体连到哪个引脚?为什么是这几个引脚?这里就有讲究了。

在Intel Edison的Arduino扩展板上,有两组I2C接口:

  • 主I2C:引脚SDA (数据线) 对应的是 Arduino引脚 A4SCL (时钟线) 对应的是 Arduino引脚 A5。这是最常用的一组。
  • 副I2C:在Edison的特定引脚上,但通常不用于Arduino兼容库。

官方“Cursor”样例默认使用主I2C。因此,连接如下:

  • LCD VCC->Edison/Arduino扩展板的 5V 引脚。为LCD模块供电。
  • LCD GND->Edison/Arduino扩展板的 GND 引脚。共地,确保电压参考基准一致。
  • LCD SDA->Edison/Arduino扩展板的 A4 引脚。传输数据。
  • LCD SCL->Edison/Arduino扩展板的 A5 引脚。提供同步时钟。

实操心得:很多新手会误将VCC接到3.3V引脚上,导致LCD显示暗淡或不工作。虽然Edison的逻辑电平是3.3V,但LCD的I2C转接板上的芯片(通常是PCF8574或类似的IO扩展芯片)工作电压一般是5V,且LCD背光也需要5V驱动才能达到正常亮度。所以,接5V引脚是正确且必要的。I2C总线是开漏结构,3.3V的主设备可以与5V的从设备通过上拉电阻通信,Edison的Arduino扩展板已经处理好了这一点。

连接好后,先通电,你应该能看到LCD背光亮起(有些模块需要调节转接板上的电位器来调整对比度,直到屏幕出现一排黑色小方块)。如果没亮,立即检查电源和接地线。

3. 软件环境搭建与库管理

硬件就绪,接下来是软件战场。我们将使用Arduino IDE来编写和上传代码到Edison。

3.1 Arduino IDE配置Intel Edison支持

Arduino IDE默认并不支持Intel Edison,需要手动添加开发板支持。

  1. 安装Arduino IDE:从官网下载并安装最新稳定版(如1.8.x)。不建议使用过于陈旧的版本。
  2. 添加开发板管理器网址
    • 打开Arduino IDE,进入文件->首选项
    • 在“附加开发板管理器网址”框中,填入:
      https://downloads.intel.com/iot/commercial/ide/package_intel_edison_index.json
    • 如果该地址失效(由于Intel已停止对Edison的官方支持),你可能需要搜索社区维护的索引文件,或者直接使用离线安装包。一个常见的备选是使用离线SDK。
  3. 安装Edison开发板包
    • 打开工具->开发板->开发板管理器...
    • 在搜索框中输入“Intel Edison”。
    • 找到“Intel i686 Boards”或类似的条目(具体名称取决于索引源),选择版本并点击“安装”。这个过程会下载编译工具链和核心库,可能需要一些时间。
  4. 选择开发板和端口
    • 安装完成后,在工具->开发板下,选择“Intel Edison”。
    • 用USB线连接Edison和电脑。在工具->端口下,会新增一个串口(在Windows上是COMx,在macOS/Linux上是/dev/ttyUSBx或/dev/ttyACMx),选择它。

3.2 关键库文件的获取与安装

“Cursor”样例依赖于LiquidCrystal_I2C这个库来驱动I2C接口的LCD。这个库并非Arduino IDE自带。

正确的库安装方法:

  1. 下载库:你可以从GitHub(搜索“LiquidCrystal_I2C”)或一些Arduino库集合网站下载到.zip格式的库文件。确保下载的版本是兼容Arduino的。
  2. 安装库:在Arduino IDE中,选择项目->加载库->添加.ZIP库...,然后选择你下载的.zip文件。
  3. 验证安装:安装成功后,在项目->加载库的管理库列表中,应该能找到“LiquidCrystal_I2C”。你也可以在代码开头尝试#include <LiquidCrystal_I2C.h>,如果不报错,说明库已就位。

踩坑实录:网络上流传着多个版本的LiquidCrystal_I2C库,不同版本间的API(函数接口)可能有细微差别。官方“Cursor”样例是基于一个特定版本的。如果你在编译时遇到“no matching function for call to...”这类错误,很大概率是库版本不匹配。解决方案是:尝试使用样例代码自带的库(如果提供),或者根据编译错误信息,调整代码中的函数调用参数以适配你安装的库版本。例如,旧的库构造函数可能是LiquidCrystal_I2C lcd(0x27, 16, 2),而新版本可能是LiquidCrystal_I2C lcd(0x27, 16, 2, LCD_5x8DOTS)

4. “Cursor”样例代码深度解析

现在,让我们打开“Cursor”样例代码(通常是一个.ino文件),逐部分拆解其工作原理。理解代码,才能举一反三。

4.1 库引入与对象初始化

代码开头通常是这样的:

#include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27, 16, 2); // 设置LCD I2C地址为0x27,16列2行
  • #include <Wire.h>:这是Arduino的I2C通信库,LiquidCrystal_I2C库在底层依赖它来发送数据。
  • LiquidCrystal_I2C lcd(0x27, 16, 2);:这是最关键的一行。它创建了一个名为lcd的对象。
    • 0x27:这是LCD的I2C设备地址。这是一个十六进制数。请注意,这是最常见的地址,但也可能是0x3F或其他值。如果你的LCD不显示,第一件事就是扫描I2C地址。你可以另外写一个简单的I2C扫描程序来确认地址。
    • 16, 2:这指明了LCD的规格是16列2行。如果你用的是2004屏,这里应改为20, 4

4.2setup()函数:初始化舞台

setup()函数中,代码执行一次性的初始化操作:

void setup() { Serial.begin(9600); // 初始化串口,波特率9600 lcd.init(); // 初始化LCD lcd.backlight(); // 打开背光 lcd.print("Cursor Demo"); // 在第一行打印初始信息 lcd.cursor(); // 显示光标(一个下划线) // lcd.blink(); // 如果需要闪烁光标,可以取消这行注释 }
  • Serial.begin(9600):打开了与电脑串口监视器的通信通道,波特率设为9600。后续我们将通过串口发送指令。
  • lcd.init():与LCD建立通信,进行复位和模式设置。
  • lcd.backlight():确保背光开启。有些库版本在init()后默认不开启背光。
  • lcd.print("Cursor Demo"):在光标初始位置(第0行第0列)打印字符串。
  • lcd.cursor()这是本示例的核心函数之一。它命令LCD显示一个下划线形状的光标。与之相对的是noCursor()用于隐藏光标。
  • lcd.blink():如果启用,光标会闪烁,更易于定位。但有时闪烁会让人分心,根据需求选择。

4.3loop()函数:交互的核心逻辑

loop()函数不断循环,检测串口是否有数据输入,并解析指令。

void loop() { if (Serial.available() > 0) { // 检查串口是否有数据 char command = Serial.read(); // 读取一个字符指令 Serial.print("Received: "); // 回显到串口监视器,方便调试 Serial.println(command); switch (command) { case 'w': // 光标上移 moveCursorUp(); break; case 's': // 光标下移 moveCursorDown(); break; case 'a': // 光标左移 moveCursorLeft(); break; case 'd': // 光标右移 moveCursorRight(); break; case 'c': // 清屏 lcd.clear(); lcd.print("Cursor Demo"); // 清屏后重新打印标题 lcd.cursor(); // 清屏会同时清除光标状态,需要重新启用 break; case 't': // 在光标处打印文本 printTextAtCursor(); break; default: Serial.println("Unknown command. Use w,a,s,d,c,t"); } } }

逻辑非常清晰:通过串口发送单个字符(‘w’, ‘a’, ‘s’, ‘d’, ‘c’, ‘t’)来控制光标和屏幕。这是一种简单高效的交互协议。

4.4 移动与打印:子函数详解

让我们看看具体的移动和打印函数是如何实现的。

光标移动函数

void moveCursorUp() { int row = getCursorRow(); int col = getCursorCol(); if (row > 0) { lcd.setCursor(col, row - 1); } } void moveCursorDown() { int row = getCursorRow(); int col = getCursorCol(); if (row < 1) { // 对于2行LCD,行索引是0和1 lcd.setCursor(col, row + 1); } } // ... moveCursorLeft, moveCursorRight 类似

这里有一个关键点:LiquidCrystal_I2C库本身没有直接获取当前光标位置的函数(如getCursorRow(),getCursorCol())。官方样例可能简化了,或者使用了其他方法。在实际项目中,我们通常需要自己维护一个变量来跟踪光标位置。

一个更实用的光标位置跟踪方案: 我们需要在全局定义两个变量currentColcurrentRow,并在每次移动或设置光标后更新它们。

int currentCol = 0; int currentRow = 0; void moveCursorRight() { if (currentCol < 15) { // 假设16列,最大索引是15 currentCol++; } else { // 如果到了行尾,可以跳到下一行行首,或者保持不变 // currentCol = 0; // currentRow = (currentRow == 0) ? 1 : 0; // 换行 } lcd.setCursor(currentCol, currentRow); }

这样,我们就始终知道光标在哪里。

在光标处打印文本printTextAtCursor()函数会等待串口输入一行字符串,然后将其打印在当前光标位置。

void printTextAtCursor() { Serial.println("Enter text (end with newline):"); String inputText = ""; while (true) { if (Serial.available() > 0) { char c = Serial.read(); if (c == '\n') { // 以换行符作为输入结束标志 break; } inputText += c; // 拼接字符 } } lcd.print(inputText); // 在当前位置打印 // 打印后,光标会自动移动到文本末尾,需要更新我们的跟踪变量 currentCol += inputText.length(); if (currentCol > 15) { // 处理打印后光标超出屏幕的情况 currentRow += currentCol / 16; currentCol = currentCol % 16; if (currentRow > 1) currentRow = 1; // 限制在最后一行 lcd.setCursor(currentCol, currentRow); // 实际设置LCD光标 } }

这个函数演示了如何通过串口进行简单的字符串交互。注意,这里使用了String类,在内存有限的嵌入式系统中需谨慎使用,对于短文本是可行的。

5. 完整实操流程与代码实现

结合上面的解析,我们来整合一个增强版、可直接复现的“Cursor”项目代码。这个版本包含了光标位置跟踪和更健壮的输入处理。

5.1 增强版完整代码

/* * Intel Edison LCD Cursor Demo (Enhanced) * 支持光标位置跟踪,并通过串口指令控制 * 指令: * w - 光标上移 * s - 光标下移 * a - 光标左移 * d - 光标右移 * c - 清屏 * t - 在光标处输入文本 * p - 打印当前光标位置(调试用) */ #include <Wire.h> #include <LiquidCrystal_I2C.h> // 定义LCD参数:I2C地址,列数,行数 // 如果屏幕不亮,尝试将0x27改为0x3F LiquidCrystal_I2C lcd(0x27, 16, 2); // 全局变量跟踪光标位置 int cursorCol = 0; int cursorRow = 0; void setup() { // 初始化串口通信 Serial.begin(115200); // 使用更高的波特率,传输更快 while (!Serial) { ; // 等待串口连接(对于Leonardo/Micro等板子很重要,Edison通常不需要) } // 初始化LCD lcd.init(); lcd.backlight(); // 确保背光开启 // 显示初始信息 lcd.clear(); lcd.setCursor(0, 0); lcd.print("Edison LCD Demo"); lcd.setCursor(0, 1); lcd.print("Cmd: w,a,s,d,c,t"); // 显示光标并设置到初始跟踪位置 lcd.cursor(); // lcd.blink(); // 可选:闪烁光标 Serial.println("LCD Cursor Demo Ready."); Serial.println("Commands: w(up), s(down), a(left), d(right), c(clear), t(text), p(print pos)"); } void loop() { // 检查串口是否有指令 if (Serial.available() > 0) { char cmd = Serial.read(); // 可选:回显指令,用于调试 // Serial.print("Cmd: "); // Serial.println(cmd); switch (cmd) { case 'w': moveCursorUp(); break; case 's': moveCursorDown(); break; case 'a': moveCursorLeft(); break; case 'd': moveCursorRight(); break; case 'c': clearScreen(); break; case 't': inputText(); break; case 'p': printCursorPosition(); break; default: // 忽略换行符、回车符等 if (cmd != '\n' && cmd != '\r') { Serial.println("Unknown command. Use: w, a, s, d, c, t, p"); } } } } // --- 光标移动函数 (更新全局变量并设置LCD) --- void moveCursorUp() { if (cursorRow > 0) { cursorRow--; lcd.setCursor(cursorCol, cursorRow); } } void moveCursorDown() { if (cursorRow < 1) { // 对于2行LCD,行索引最大为1 cursorRow++; lcd.setCursor(cursorCol, cursorRow); } } void moveCursorLeft() { if (cursorCol > 0) { cursorCol--; lcd.setCursor(cursorCol, cursorRow); } else { // 如果在行首,可以跳到上一行行尾(可选逻辑) // if (cursorRow > 0) { // cursorRow--; // cursorCol = 15; // lcd.setCursor(cursorCol, cursorRow); // } } } void moveCursorRight() { if (cursorCol < 15) { // 对于16列LCD,列索引最大为15 cursorCol++; lcd.setCursor(cursorCol, cursorRow); } else { // 如果在行尾,可以跳到下一行行首(可选逻辑) // if (cursorRow < 1) { // cursorRow++; // cursorCol = 0; // lcd.setCursor(cursorCol, cursorRow); // } } } // --- 清屏函数 --- void clearScreen() { lcd.clear(); cursorCol = 0; cursorRow = 0; lcd.setCursor(0, 0); lcd.print("Screen Cleared"); delay(1000); // 短暂显示信息 lcd.clear(); lcd.setCursor(cursorCol, cursorRow); // 将光标重置到左上角 lcd.cursor(); // 清屏会关闭光标,需要重新打开 } // --- 文本输入函数 --- void inputText() { lcd.noCursor(); // 输入时暂时隐藏光标,避免干扰 Serial.println("Please enter your text (end with Enter/Return):"); Serial.print("> "); String input = ""; bool receiving = true; while (receiving) { if (Serial.available() > 0) { char c = Serial.read(); if (c == '\n' || c == '\r') { // 以换行或回车结束输入 receiving = false; } else { input += c; // 拼接字符 Serial.print(c); // 本地回显,让你看到输入了什么 } } } Serial.println(); // 输入结束,换行 // 在LCD当前光标位置打印文本 lcd.print(input); // 更新光标位置跟踪变量 // 注意:lcd.print()后,LCD硬件光标会移动,但我们的变量没更新 // 我们需要根据打印的字符数手动计算新位置 int charsPrinted = input.length(); cursorCol += charsPrinted; // 处理打印后光标超出当前行的情况 while (cursorCol > 15) { cursorCol -= 16; cursorRow++; if (cursorRow > 1) { // 如果超出最后一行,停在最后一行行尾 cursorRow = 1; cursorCol = 15; break; } } // 将LCD硬件光标设置到我们计算的新位置 lcd.setCursor(cursorCol, cursorRow); lcd.cursor(); // 重新显示光标 Serial.print("Text printed. Cursor now at ("); Serial.print(cursorCol); Serial.print(", "); Serial.print(cursorRow); Serial.println(")"); } // --- 调试函数:打印当前光标位置 --- void printCursorPosition() { Serial.print("Current Cursor Position: (Col="); Serial.print(cursorCol); Serial.print(", Row="); Serial.print(cursorRow); Serial.println(")"); }

5.2 代码上传与测试步骤

  1. 粘贴代码:将上面的完整代码复制到Arduino IDE的一个新项目中。
  2. 检查与编译:点击“验证”(对勾图标)检查代码是否有语法错误。确保开发板选择“Intel Edison”,端口已选对。
  3. 上传:点击“上传”(右箭头图标)将代码烧录到Edison。观察IDE底部状态栏,显示“上传成功”。
  4. 打开串口监视器:点击IDE右上角的“串口监视器”图标(放大镜)。关键步骤:将右下角的波特率设置为115200(与代码中Serial.begin(115200)一致),并选择“换行符”作为发送结尾(这样按回车发送的就是‘\n’)。
  5. 测试指令
    • 在串口监视器顶部的输入框里,依次输入w,a,s,d并发送,观察LCD上光标(下划线)的移动。
    • 输入c清屏。
    • 输入t,串口监视器会提示“Please enter your text...”,输入一段文字(如“Hello Edison!”)后按回车,文字会显示在LCD当前光标位置。
    • 输入p,会在串口监视器看到当前光标行列坐标。

6. 常见问题排查与进阶技巧

即使按照步骤操作,也可能会遇到问题。这里汇总了常见坑点及其解决方案。

6.1 问题排查速查表

问题现象可能原因解决方案
LCD屏幕无任何显示,背光不亮1. 电源未接通或接错(VCC没接5V)。
2. 接地(GND)不可靠。
3. LCD模块或转接板损坏。
1. 用万用表检查5V和GND引脚间电压是否为5V。
2. 重新插拔杜邦线,确保接触良好。
3. 尝试更换模块。
屏幕亮起但只有一排黑块(无字符)1. I2C地址不正确。
2. 对比度不可调(电位器故障或接线错误)。
3. 代码中行列参数设置错误。
1.运行I2C扫描程序确定地址(见下文)。
2. 调节转接板上的电位器(通常是一个蓝色小方块),缓慢旋转直到字符清晰。
3. 检查代码LiquidCrystal_I2C lcd(addr, cols, rows)中的列数和行数。
编译错误:LiquidCrystal_I2C.h: No such file or directory库未正确安装。通过“项目”->“加载库”->“管理库”搜索并安装,或手动添加ZIP库。
编译错误:no matching function for call to...库版本与代码不兼容。根据错误信息修改构造函数或函数参数,或尝试更换其他版本的LiquidCrystal_I2C库。
上传代码失败1. 端口选择错误。
2. Edison未进入编程模式。
3. 驱动问题(Windows常见)。
1. 确认选择了正确的COM口。
2. 尝试重启Edison,或按一下复位按钮再上传。
3. 在设备管理器中检查驱动,必要时手动安装。
串口监视器无输出或乱码波特率设置不匹配。确保串口监视器的波特率与代码中Serial.begin()设置的数值完全相同(本例为115200)。
光标移动或打印位置错乱光标位置跟踪逻辑有误,或打印后未正确更新位置变量。仔细检查cursorColcursorRow的更新逻辑,确保在每次lcd.setCursor()lcd.print()后都同步更新。使用p指令打印位置辅助调试。

6.2 必备技能:I2C地址扫描

当你的LCD使用0x270x3F都不工作时,第一要务就是扫描I2C总线,找出设备的真实地址。将以下代码上传到Edison,打开串口监视器查看结果。

#include <Wire.h> void setup() { Wire.begin(); Serial.begin(115200); Serial.println("\nI2C Scanner starting..."); } void loop() { byte error, address; int nDevices = 0; Serial.println("Scanning..."); 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++; } else if (error == 4) { Serial.print("Unknown error at address 0x"); if (address < 16) Serial.print("0"); Serial.println(address, HEX); } } if (nDevices == 0) { Serial.println("No I2C devices found. Check wiring!"); } else { Serial.println("Scan complete."); } delay(5000); // 每5秒扫描一次 }

找到的地址(如0x27)替换掉主代码中的地址参数即可。

6.3 进阶技巧与扩展思路

  1. 自定义字符:LCD允许你定义最多8个5x8像素的自定义字符。你可以用它来显示简单的图标、Logo或特殊符号。使用lcd.createChar()函数。
  2. 滚动显示:当文本长度超过屏幕宽度时,可以使用lcd.scrollDisplayLeft()scrollDisplayRight()实现滚动效果,非常适合显示长信息。
  3. 结合传感器:将Edison强大的计算能力与LCD结合。例如,连接一个温湿度传感器(如DHT11),将实时数据读取并格式化显示在LCD上,就构成了一个简单的环境监测站。
  4. 优化功耗:在电池供电的应用中,记得在不需要显示时使用lcd.noBacklight()关闭背光,这是主要的耗电源。
  5. 使用更高效的通信:本例使用串口字符指令,适合手动调试。在实际项目中,可以定义更复杂的串口协议(如JSON格式),或者通过Wi-Fi/蓝牙接收指令,实现无线控制LCD显示内容。

这个“Cursor”项目虽然基础,但它像一把钥匙,打开了使用Intel Edison驱动外部显示设备的大门。通过理解其背后的硬件连接、库函数调用和串口交互逻辑,你就能在此基础上构建更复杂、更有趣的应用,比如物联网信息显示屏、简单的交互式菜单系统等等。硬件编程的魅力就在于,从这样一个闪烁的光标开始,你的想法可以一步步在物理世界中呈现出来。

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

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

立即咨询