返回

在 Velxio 上精通 Arduino 的串口、I2C 和 SPI

引言

通信协议是嵌入式系统的核心。无论你是读取传感器、与 SD 卡通信,还是打印调试信息,你都在使用三大主要协议之一:串口 (UART)I2C (TWI)SPI。在本教程中,我们将使用 Velxio 浏览器内模拟器,在 Arduino Uno 上同时运行这三种协议。你将看到如何扫描 I2C 总线、读写 EEPROM、执行 SPI 传输,并通过串口记录所有信息。

Velxio 模拟器画布上的多协议电路图

电路讲解

本示例仅使用 Arduino Uno 板本身——无需任何外部元件。Arduino 的内置外设提供了我们所需的一切:

由于所有连接都在 Arduino 内部,因此无需接线。Velxio 画布会显示带有引脚标签的 Uno,你可以通过打开元件选择器来检查虚拟元件。

Velxio 元件选择器模态框,按类别浏览元件

代码讲解

让我们逐步分析这段代码。

库和定义

#include <Wire.h>
#include <SPI.h>

#define DS1307_ADDR 0x68
#define EEPROM_ADDR 0x50
#define SS_PIN 10

我们包含了用于 I2C 的 Wire 库和用于 SPI 的 SPI 库。定义了两个 I2C 地址:0x68 用于 DS1307 RTC(虽然物理上不存在,但我们会扫描它),0x50 用于 24Cxx EEPROM。SPI 从机选择引脚设置为数字引脚 10。

辅助函数

byte bcdToDec(byte val) {
  return ((val >> 4) * 10) + (val & 0x0F);
}

void readRTC(byte &hr, byte &min, byte &sec) {
  Wire.beginTransmission(DS1307_ADDR);
  Wire.write(0x00);
  Wire.endTransmission();
  Wire.requestFrom(DS1307_ADDR, 3);
  sec = bcdToDec(Wire.read() & 0x7F);
  min = bcdToDec(Wire.read());
  hr  = bcdToDec(Wire.read() & 0x3F);
}

bcdToDec 函数将二进制编码的十进制 (BCD) 转换为十进制。readRTC 从 DS1307 RTC 读取秒、分和时。& 0x7F& 0x3F 掩码用于清除未使用的位。

void writeEEPROM(byte reg, byte value) {
  Wire.beginTransmission(EEPROM_ADDR);
  Wire.write(reg);
  Wire.write(value);
  Wire.endTransmission();
  delay(5);
}

byte readEEPROM(byte reg) {
  Wire.beginTransmission(EEPROM_ADDR);
  Wire.write(reg);
  Wire.endTransmission();
  Wire.requestFrom(EEPROM_ADDR, 1);
  return Wire.available() ? Wire.read() : 0xFF;
}

这些函数用于向地址为 0x50 的 I2C EEPROM 写入和读取单个字节。delay(5) 确保 EEPROM 有足够时间完成写入周期。

byte spiTransfer(byte data) {
  digitalWrite(SS_PIN, LOW);
  byte result = SPI.transfer(data);
  digitalWrite(SS_PIN, HIGH);
  return result;
}

该函数执行全双工 SPI 传输:拉低 SS,发送一个字节,接收响应,然后拉高 SS。

初始化

void setup() {
  Serial.begin(9600);
  Wire.begin();
  pinMode(SS_PIN, OUTPUT);
  digitalWrite(SS_PIN, HIGH);
  SPI.begin();

  Serial.println("===================================");
  Serial.println(" 多协议演示");
  Serial.println(" 串口 (USART) + I2C (TWI) + SPI");
  Serial.println("===================================");
  Serial.println();

  // ── I2C: 扫描总线 ──
  Serial.println("[I2C] 正在扫描总线...");
  int found = 0;
  for (byte addr = 1; addr < 127; addr++) {
    Wire.beginTransmission(addr);
    if (Wire.endTransmission() == 0) {
      Serial.print("  在地址 0x");
      if (addr < 16) Serial.print("0");
      Serial.println(addr, HEX);
      found++;
    }
  }
  Serial.print("  ");
  Serial.print(found);
  Serial.println(" 个设备在 I2C 总线上。");
  Serial.println();

  // ── I2C: 写入/读取 EEPROM ──
  Serial.println("[I2C] EEPROM 写入/读取测试:");
  writeEEPROM(0, 42);
  writeEEPROM(1, 99);
  byte v0 = readEEPROM(0);
  byte v1 = readEEPROM(1);
  Serial.print("  写入 42,读取 ");
  Serial.print(v0);
  Serial.println(v0 == 42 ? " [成功]" : " [失败]");
  Serial.print("  写入 99,读取 ");
  Serial.print(v1);
  Serial.println(v1 == 99 ? " [成功]" : " [失败]");
  Serial.println();

  // ── SPI: 传输测试 ──
  Serial.println("[SPI] 传输测试:");
  byte spiData[] = {0xAA, 0x55, 0x42};
  for (int i = 0; i < 3; i++) {
    byte rx = spiTransfer(spiData[i]);
    Serial.print("  发送=0x");
    if (spiData[i] < 16) Serial.print("0");
    Serial.print(spiData[i], HEX);
    Serial.print(" 接收=0x");
    if (rx < 16) Serial.print("0");
    Serial.println(rx, HEX);
  }
  Serial.println();

  Serial.println("初始化完成。正在读取 RTC...");
  Serial.println();
}

setup() 中,我们初始化了所有三种协议。然后我们:

  1. 扫描 I2C 总线,查找地址从 1 到 126 的设备。
  2. 测试 EEPROM,将值 42 和 99 写入地址 0 和 1,然后读回并验证。
  3. 测试 SPI,发送三个字节(0xAA0x550x42)并打印接收到的字节。

主循环

void loop() {
  // ── 串口:每 2 秒打印 RTC 时间 ──
  byte hr, min, sec;
  readRTC(hr, min, sec);

  Serial.print("[RTC] ");
  if (hr < 10) Serial.print("0");
  Serial.print(hr);
  Serial.print(":");
  if (min < 10) Serial.print("0");
  Serial.print(min);
  Serial.print(":");
  if (sec < 10) Serial.print("0");
  Serial.print(sec);

  Serial.print("  |  运行时间:");
  Serial.print(millis() / 1000);
  Serial.println("s");

  delay(2000);
}

每两秒,主循环读取 RTC 时间并连同系统运行时间一起打印。这演示了在 I2C 和 SPI 空闲时持续进行串口输出。

关键概念

常见陷阱与调试技巧

建议扩展

  1. 添加真实传感器:将 DS1307 RTC 和 24Cxx EEPROM 连接到 I2C 总线,并通过 SPI 连接 SD 卡模块。
  2. 将数据记录到 EEPROM:存储传感器读数并在复位后检索。
  3. 使用中断:通过按钮触发 SPI 传输。
  4. 实现命令接口:解析串口命令以控制 I2C 和 SPI 操作。

亲自尝试

准备好看到这三种协议的实际运行了吗?在 Velxio 中打开实时示例,编译并运行。观察串口监视器输出,Arduino 将扫描 I2C 总线、测试 EEPROM、执行 SPI 传输,然后持续打印 RTC 时间。

在 Velxio 上启动多协议演示

Velxio 编辑器工具栏,包含编译和运行控件 Velxio 画布工具栏,位于模拟视口上方

祝你编程愉快!


分享到:
David Montero

作者

David Montero

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

GitHub velxio.dev

相关文章


上一篇
在 Velxio 模拟器中让树莓派 Pico 闪烁 LED
下一篇
SPI 回环测试:使用 Velxio 掌握协议