返回

使用Arduino的I2C扫描仪:发现总线上的每个设备

引言

你是否曾经连接了一个新的I2C传感器或显示器到Arduino,然后疑惑:“这个设备到底用什么地址?”你不是一个人!I2C协议是一种强大的通信方式,仅用两根线就能与多个设备通信,但每个设备都有一个唯一的地址——有时这个地址在数据手册中并不明显。这时就需要I2C扫描仪登场了。它是一个简单的程序,会探测所有可能的地址,并报告哪些地址有响应。

在本教程中,我们将使用Arduino Uno和SSD1306 OLED显示屏(它本身也是总线上的一个已知设备)来构建一个I2C扫描仪。我们将在Velxio的浏览器内模拟器中运行所有内容,因此你无需任何硬件即可跟随操作。最后,你将了解I2C总线的工作原理、如何连接设备,以及如何编写扫描它们的代码。

让我们开始吧!

电路讲解

Velxio模拟器画布上i2c扫描仪的电路图

这个电路非常简单。我们只需要两个组件:

接线细节

使用四根线将OLED连接到Arduino:

Arduino引脚OLED引脚线色
A4 (SDA)DATA蓝色
A5 (SCL)CLK橙色
GNDGND黑色
5VVIN红色

注意: 在Arduino Uno上,A4和A5是专用的I2C引脚。其他开发板可能使用不同的引脚(例如,ESP32上的SDA/SCL)。

在Velxio模拟器中,你可以从组件选择器中添加OLED,并通过点击引脚进行接线。画布工具栏可以帮助你缩放和平移,使连接更容易。

模拟视口上方的Velxio画布工具栏

代码详解

Velxio Monaco编辑器中i2c扫描仪的源代码

现在让我们看看代码。在Velxio中打开代码编辑器(你会看到带有语法高亮的Monaco编辑器)。该程序是用Arduino C++编写的,并使用内置的Wire库。

包含库文件

#include <Wire.h>

Wire库处理所有底层的I2C通信。它已包含在Arduino IDE中,因此无需额外安装。

设置函数

void setup() {
  Wire.begin();
  Serial.begin(9600);

  Serial.println("===========================");
  Serial.println("  I2C总线扫描仪 (TWI)");
  Serial.println("===========================");
  Serial.println("正在扫描...");
  Serial.println();

  int devicesFound = 0;

  for (byte addr = 1; addr < 127; addr++) {
    Wire.beginTransmission(addr);
    byte error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("  在0x");
      if (addr < 16) Serial.print("0");
      Serial.print(addr, HEX);

      // 识别已知地址
      switch (addr) {
        case 0x27: Serial.print("  (PCF8574 LCD背板)"); break;
        case 0x3C: Serial.print("  (SSD1306 OLED)"); break;
        case 0x48: Serial.print("  (温度传感器)"); break;
        case 0x50: Serial.print("  (EEPROM)"); break;
        case 0x68: Serial.print("  (DS1307 RTC)"); break;
        case 0x76: Serial.print("  (BME280传感器)"); break;
        case 0x77: Serial.print("  (BMP180/BMP280)"); break;
      }
      Serial.println();
      devicesFound++;
    }
  }

  Serial.println();
  Serial.print("扫描完成。共发现 ");
  Serial.print(devicesFound);
  Serial.println(" 个设备。");

  if (devicesFound == 0) {
    Serial.println("未找到I2C设备。请检查连接。");
  }
}

让我们分解一下:

  1. Wire.begin() – 将Arduino初始化为I2C主设备。
  2. Serial.begin(9600) – 以9600波特率启动串口通信,以便我们可以在串口监视器中看到结果。
  3. 循环 遍历地址1到126(0x01到0x7E)。地址0保留用于通用呼叫,地址127保留用于特殊用途。
  4. Wire.beginTransmission(addr) – 开始向给定地址发送数据。
  5. Wire.endTransmission() – 结束发送并返回一个错误代码。如果错误为0,表示设备已确认(ACK)——我们找到了一个设备!
  6. 打印地址 – 我们以十六进制格式打印,并添加前导零以提高可读性。
  7. Switch语句 – 通过已知地址识别常见设备。这是可选的,但很有帮助。
  8. 计数设备 – 每收到一个ACK,我们就递增devicesFound
  9. 最终报告 – 打印找到的设备总数。

循环函数

void loop() {
  // 每10秒重新扫描一次
  delay(10000);
  Serial.println("\n正在重新扫描...");
  setup();
}

loop()函数只是等待10秒,然后再次调用setup()进行重新扫描。这在系统运行时添加或移除设备时非常有用。

学生关键概念

什么是I2C?

I2C(Inter-Integrated Circuit),也称为TWI(Two-Wire Interface),是一种同步串行通信协议。它使用两条双向开漏线:SDA(数据)和SCL(时钟)。总线上的每个设备都有一个唯一的7位地址(尽管也存在10位地址)。主设备(Arduino)发起通信并生成时钟信号。

扫描仪的工作原理

扫描仪发送一个起始条件,后跟地址字节(7位地址 + 读/写位)。如果存在具有该地址的设备,它会通过在第9个时钟脉冲期间将SDA拉低来响应ACK(确认)。如果没有设备响应,则会收到NACK(未确认),并且endTransmission()返回一个非零错误代码。

为什么要使用扫描仪?

常见陷阱和调试技巧

  1. 接线错误 – 仔细检查SDA和SCL是否正确连接。交换它们将无法工作。
  2. 缺少上拉电阻 – I2C线路需要上拉电阻(通常为4.7kΩ)连接到VCC。许多扩展板已包含它们,但如果你从头开始构建,请不要忘记。
  3. 地址冲突 – 如果两个设备共享同一个地址,两者都无法可靠工作。使用扫描仪检测冲突。
  4. 波特率不匹配 – 扫描仪使用默认的100 kHz(标准模式)。某些设备可能需要更慢的速度。你可以使用Wire.setClock(400000)将其更改为快速模式。
  5. 串口监视器不显示输出 – 确保串口监视器中的波特率与9600匹配。

在Velxio中,你可以从编辑器工具栏打开串口监视器。如果没有看到输出,请检查代码是否编译成功(在控制台中查找错误)。

带有编译和运行控件的Velxio编辑器工具栏

建议的扩展功能

一旦基本扫描仪正常工作,可以尝试以下增强功能:

要在Velxio中添加库,请使用库管理器。

列出已安装Arduino库的Velxio库管理器对话框

结论

你已经构建了一个通用的I2C扫描仪,它适用于任何兼容Arduino的开发板。这个工具对于调试和探索新的I2C模块非常宝贵。最棒的是?你现在就可以在浏览器中通过Velxio运行它,无需任何硬件。

准备好亲自尝试了吗?打开实时示例并开始扫描:

👉 在Velxio上启动I2C扫描仪

祝你编程愉快!


分享到:
David Montero

作者

David Montero

Creator of Velxio, the open-source circuit and Arduino simulator.

GitHub velxio.dev

相关文章


上一篇
在 Velxio 上使用 DS1307 的 I2C RTC 时钟
下一篇
Arduino 串口控制 LED:发送指令实现闪烁