Arduino - I2C 总线通信
Arduino - I2C 通信协议
Section titled “Arduino - I2C 通信协议”I2C(Inter-Integrated Circuit)协议是一种流行的串行通信总线,用于将微控制器连接到传感器、存储模块和其他外设。它是一个两线制系统,非常适合需要连接多个设备而无需使用大量 GPIO 引脚的项目。
I2C 基础知识
Section titled “I2C 基础知识”I2C 通信涉及两种角色:
- 主设备(Master):发起通信、生成时钟信号并寻址从设备的设备。Arduino 通常充当主设备。
- 从设备(Slave):监听其地址并响应主设备请求的设备。例如传感器、显示器和 I/O 扩展器。
使用的两根线是:
- SDA(串行数据线):用于传输和接收数据的线路。
- SCL(串行时钟线):承载时钟信号,同步数据传输的线路。
一个主设备可以在同一总线上与多达 112 个从设备通信,只要每个从设备都有唯一的地址。总线需要在 SDA 和 SCL 两条线上都连接上拉电阻(通常为 4.7kΩ),尽管许多传感器扩展板已经包含了这些电阻。
板载 I2C 引脚
Section titled “板载 I2C 引脚”I2C 引脚因 Arduino 板而异。以下是常见的引脚:
- Uno、Nano、Pro Mini:
A4(SDA)、A5(SCL) - Mega、Due:
20(SDA)、21(SCL) - Leonardo、Micro:
2(SDA)、3(SCL)
使用 Wire 库
Section titled “使用 Wire 库”Arduino 内置的 Wire 库简化了 I2C 通信。核心函数包括:
Wire.begin():初始化库并加入 I2C 总线。主设备调用时不带地址。从设备调用时带上其 7 位地址,例如Wire.begin(0x08)。Wire.beginTransmission(address):开始向指定地址的从设备进行传输。Wire.write(data):向从设备发送数据字节。Wire.endTransmission():结束传输。至关重要的是,它会返回一个状态码,您应该检查该状态码以查找错误。Wire.requestFrom(address, byteCount):主设备向从设备请求特定数量的字节。Wire.available():返回已接收并可读取的字节数。Wire.read():读取一个已接收的字节。
实用示例:主设备发送器 / 从设备接收器
Section titled “实用示例:主设备发送器 / 从设备接收器”在此示例中,一个作为主设备的 Arduino 向另一个作为从设备的 Arduino 发送命令,以控制其板载 LED。
主设备发送器代码
Section titled “主设备发送器代码”#include <Wire.h>
#define SLAVE_ADDRESS 0x08 // 定义从设备的地址
void setup() { Wire.begin(); // 作为主设备加入 I2C 总线 Serial.begin(9600); Serial.println("I2C Master Writer Initialized");}
void loop() { // --- 打开 LED --- Serial.println("Sending ON command..."); Wire.beginTransmission(SLAVE_ADDRESS); Wire.write('1'); // 发送一个字节以打开 LED byte error = Wire.endTransmission(); // 检查错误
if (error == 0) { Serial.println("Command sent successfully."); } else { Serial.print("Error sending command: "); Serial.println(error); } delay(2000);
// --- 关闭 LED --- Serial.println("Sending OFF command..."); Wire.beginTransmission(SLAVE_ADDRESS); Wire.write('0'); // 发送一个字节以关闭 LED error = Wire.endTransmission();
if (error == 0) { Serial.println("Command sent successfully."); } else { Serial.print("Error sending command: "); Serial.println(error); } delay(2000);}从设备接收器代码
Section titled “从设备接收器代码”#include <Wire.h>
#define SLAVE_ADDRESS 0x08 // 此从设备的地址
void setup() { pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, LOW); // 初始状态为 LED 关闭
Wire.begin(SLAVE_ADDRESS); // 带地址加入 I2C 总线 Wire.onReceive(receiveEvent); // 注册事件处理函数
Serial.begin(9600); Serial.println("I2C Slave Receiver Initialized");}
void loop() { // 主循环可用于其他任务。 // 所有 I2C 逻辑由事件处理。 delay(100);}
// 每当从主设备接收到数据时,都会调用此函数。void receiveEvent(int byteCount) { while (Wire.available()) { // 遍历所有接收到的字节 char c = Wire.read(); // 将接收到的字节作为字符 Serial.print("Command received: "); Serial.println(c);
if (c == '1') { digitalWrite(LED_BUILTIN, HIGH); } else if (c == '0') { digitalWrite(LED_BUILTIN, LOW); } }}调试和常见问题
Section titled “调试和常见问题”- 地址错误:最常见的问题。使用“I2C 扫描器”程序(sketch)来扫描总线并查找连接设备的地址。
- 缺少上拉电阻:如果您的传感器模块没有上拉电阻,总线将无法工作。您需要一个 4.7kΩ 电阻从 SDA 连接到 5V,另一个从 SCL 连接到 5V。
- 接线错误:仔细检查 SDA 是否连接到 SDA,SCL 是否连接到 SCL。接反会阻止通信。
- 电源问题:确保总线上的所有设备共用一个地(GND)。
理解 endTransmission() 返回码
Section titled “理解 endTransmission() 返回码”始终检查 Wire.endTransmission() 的返回值以构建健壮的系统:
0:成功。1:数据过长,无法放入传输缓冲区。2:在传输地址时收到 NACK(非应答)。这意味着在该地址没有从设备响应。3:在传输数据时收到 NACK。从设备存在但未接受数据。4:其他错误。