Arduino I2C地址扫描器:手把手教你用两块Arduino UNO板实现主从机通讯
·
Arduino I2C地址扫描与主从通讯实战指南
两块Arduino开发板之间的对话听起来像是科幻场景,但通过I2C协议,这完全可以成为你工作台上的现实。作为硬件爱好者最常用的通讯协议之一,I2C以其简洁的两线制连接方式赢得了广泛青睐。本文将带你从零开始,用两块Arduino UNO搭建一个完整的I2C通讯系统,涵盖地址扫描、主从机配置到双向数据传输的全过程。
1. I2C通讯基础与硬件准备
I2C(Inter-Integrated Circuit)是一种同步、多主多从的串行通讯总线,仅需两根信号线即可实现多个设备间的数据交换。在Arduino UNO上,I2C接口固定位于A4(SDA数据线)和A5(SCL时钟线)引脚。
所需材料清单 :
- 2块Arduino UNO开发板
- 4条杜邦线(建议使用不同颜色区分功能)
- 1个面包板(可选,用于更整洁的布线)
- 2个4.7kΩ上拉电阻(部分开发板已内置)
硬件连接示意图:
| 主机引脚 | 从机引脚 | 连接说明 |
|---|---|---|
| A4 (SDA) | A4 (SDA) | 数据线,需接上拉电阻 |
| A5 (SCL) | A5 (SCL) | 时钟线,需接上拉电阻 |
| GND | GND | 共地连接,确保电位基准一致 |
提示:虽然部分Arduino开发板已内置上拉电阻,但为确保通讯稳定性,建议外接4.7kΩ电阻将SDA和SCL线拉高至VCC。
2. I2C地址扫描实战
在建立通讯前,我们需要确认从机设备的地址。以下是一个增强版的地址扫描程序,不仅能发现设备,还能识别可能的通讯错误。
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(115200);
while (!Serial); // 等待串口就绪
Serial.println("=== I2C扫描仪 ===");
}
void loop() {
byte error, address;
int foundDevices = 0;
Serial.println("开始扫描I2C总线...");
for(address = 1; address < 127; address++) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("发现设备 @ 0x");
if(address < 16) Serial.print("0");
Serial.println(address, HEX);
foundDevices++;
}
else if (error == 4) {
Serial.print("地址 0x");
if(address < 16) Serial.print("0");
Serial.print(address, HEX);
Serial.println(" 存在通讯错误");
}
}
if (foundDevices == 0)
Serial.println("未发现任何I2C设备");
else
Serial.print("扫描完成,共发现 ");
Serial.print(foundDevices);
Serial.println(" 个设备");
delay(5000); // 5秒后重新扫描
}
常见I2C设备地址范围参考表:
| 设备类型 | 典型地址范围 | 备注 |
|---|---|---|
| EEPROM | 0x50-0x57 | 如AT24C系列 |
| 温度传感器 | 0x48-0x4F | 如LM75 |
| 实时时钟 | 0x68 | DS1307/DS3231 |
| LCD显示器 | 0x27/0x3F | PCF8574转接板 |
3. 主从机通讯实现
3.1 从机配置
从机需要设置一个唯一的I2C地址(通常为1-127之间的值)。以下是从机接收数据的完整示例:
#include <Wire.h>
#define SLAVE_ADDR 0x08 // 自定义从机地址
void receiveData(int byteCount);
void setup() {
Wire.begin(SLAVE_ADDR); // 以从机模式加入I2C总线
Wire.onReceive(receiveData); // 注册接收事件回调
Serial.begin(9600);
Serial.println("从机准备就绪");
}
void loop() {
delay(100); // 保持低功耗
}
void receiveData(int byteCount) {
Serial.print("收到 ");
Serial.print(byteCount);
Serial.println(" 字节数据:");
while(Wire.available()) {
char c = Wire.read();
Serial.write(c);
}
Serial.println();
}
3.2 主机配置
主机负责初始化通讯并发送数据。以下是主机发送字符串和数值的示例:
#include <Wire.h>
#define SLAVE_ADDR 0x08 // 与从机地址匹配
void setup() {
Wire.begin(); // 主机无需指定地址
Serial.begin(9600);
}
void loop() {
static int counter = 0;
Wire.beginTransmission(SLAVE_ADDR);
Wire.write("计数: ");
Wire.write(counter);
byte error = Wire.endTransmission();
if(error == 0) {
Serial.print("成功发送数据: ");
Serial.println(counter);
} else {
Serial.print("传输错误代码: ");
Serial.println(error);
}
counter++;
delay(1000);
}
4. 双向通讯进阶实现
完整的双向通讯需要主从机都能发送和接收数据。以下是实现方案:
4.1 从机代码升级
#include <Wire.h>
#define SLAVE_ADDR 0x08
byte responseData[] = {0xAA, 0x55, 0x01}; // 示例响应数据
void setup() {
Wire.begin(SLAVE_ADDR);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent); // 新增请求事件处理
Serial.begin(9600);
}
void loop() { delay(100); }
void receiveEvent(int howMany) {
while(Wire.available()) {
Serial.print((char)Wire.read());
}
}
void requestEvent() {
Wire.write(responseData, sizeof(responseData));
Serial.println("收到主机请求,已发送响应数据");
}
4.2 主机代码升级
#include <Wire.h>
#define SLAVE_ADDR 0x08
void setup() {
Wire.begin();
Serial.begin(9600);
}
void loop() {
// 发送数据到从机
Wire.beginTransmission(SLAVE_ADDR);
Wire.write("请求数据");
Wire.endTransmission();
// 请求从机返回数据
Wire.requestFrom(SLAVE_ADDR, 3); // 请求3字节
Serial.print("从机响应: ");
while(Wire.available()) {
Serial.print(Wire.read(), HEX);
Serial.print(" ");
}
Serial.println();
delay(2000);
}
5. 调试技巧与常见问题解决
通讯不稳定排查清单 :
- 检查所有物理连接是否牢固
- 确认SDA和SCL线都正确接有上拉电阻
- 确保主从机共地
- 检查从机地址是否冲突
- 降低通讯速率测试(修改Wire库的时钟频率)
Wire库时钟频率调整 :
void setup() {
Wire.begin();
Wire.setClock(100000); // 设置为标准100kHz模式
// Wire.setClock(400000); // 快速模式
// Wire.setClock(10000); // 低速模式用于调试
}
常见错误代码解析 :
| 错误代码 | 含义 | 解决方案 |
|---|---|---|
| 0 | 成功 | - |
| 1 | 数据量过大 | 减少单次传输数据量 |
| 2 | 地址NACK | 检查从机地址是否正确 |
| 3 | 数据NACK | 检查从机是否准备好接收数据 |
| 4 | 其他错误 | 检查硬件连接和电源 |
在实际项目中,我发现最常遇到的问题是不正确的上拉电阻值。虽然理论上4.7kΩ是标准值,但在长导线或高干扰环境中,可能需要尝试2.2kΩ甚至1kΩ的电阻来改善信号质量。另一个容易忽视的细节是电源稳定性——当使用面包板供电时,确保电源能提供足够的电流,否则可能导致通讯异常。
更多推荐


所有评论(0)