引言
欢迎来到 Pico 多协议演示!在本教程中,我们将探索如何让 Raspberry Pi Pico (RP2040) 同时通过多种协议进行通信:串行、I2C、SPI 和 ADC。这是一个高级示例,汇集了嵌入式开发的几项关键技能。我们将在 Velxio 模拟器中搭建电路、编写代码,并逐步讲解每个部分。

电路讲解
让我们来看看组件和接线。该电路使用了一块 Raspberry Pi Pico (Nano RP2040 Connect) 和几个外设:
- 蓝色 LED (led-i2c):通过限流电阻连接到 D12 (GP12) 引脚。阳极 (A) 连接 D12,阴极 (C) 连接 GND。
- 黄色 LED (led-spi):类似地连接到 D7 (GP7) 引脚。
- 电位器 (pot-adc):信号引脚 (SIG) 连接到 A0 (GP26)。VCC 接 3.3V,GND 接 GND。
- 绿色 LED (led-gpio):连接到 D2 (GP2) 引脚。
所有 LED 共享一个公共地。电位器由 3.3V 电源轨供电。接线很简单,但请注意我们使用了 Pico 内置的 I2C 和 SPI 外设(虽然在原理图中没有明确显示,但代码假定它们位于默认引脚上)。
代码讲解
代码使用 Arduino C++ 编写,并使用了 Wire 和 SPI 库。让我们逐节分析。
设置
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
delay(500);
Serial.println("==============================");
Serial.println(" Pico Multi-Protocol Demo");
Serial.println("==============================");
Serial.println();
我们首先初始化内置 LED 和串行通信,波特率设为 115200。延迟是为了给串行监视器连接留出时间。
1. I2C 扫描器
Wire.begin();
Serial.println("[I2C] Scanning bus...");
int found = 0;
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
found++;
Serial.print(" Found device at 0x");
if (addr < 16) Serial.print('0');
Serial.println(addr, HEX);
}
}
Serial.print(" Total devices: "); Serial.println(found);
Serial.println();
这段代码扫描所有可能的 I2C 地址(1-126),并报告任何响应的设备。在模拟器中,你可能会看到地址为 0x50 (EEPROM) 和 0x68 (RTC) 的虚拟设备。
2. I2C EEPROM 读写
Serial.println("[I2C] EEPROM test at 0x50...");
Wire.beginTransmission(0x50);
Wire.write(0x00); // register 0
Wire.write(0x42); // data
Wire.endTransmission();
delay(5);
Wire.beginTransmission(0x50);
Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom(0x50, 1);
if (Wire.available()) {
byte val = Wire.read();
Serial.print(" Wrote 0x42, Read 0x");
Serial.print(val, HEX);
Serial.println(val == 0x42 ? " — OK" : " — FAIL");
}
Serial.println();
我们将值 0x42 写入地址为 0x50 的 EEPROM 的寄存器 0,然后读回以验证。这测试了基本的 I2C 读写功能。
3. I2C RTC
Serial.println("[I2C] Reading DS1307 RTC at 0x68...");
Wire.beginTransmission(0x68);
Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom(0x68, 3);
if (Wire.available() >= 3) {
byte sec = ((Wire.read() & 0x7F) >> 4) * 10 + (Wire.read() & 0x0F);
byte min2 = Wire.read();
(void)sec; (void)min2;
Serial.println(" RTC responded OK");
}
Serial.println();
这里我们尝试从地址为 0x68 的 DS1307 RTC 读取时间寄存器。代码读取了三个字节(秒、分钟、小时),但只检查了设备是否响应。
4. SPI 回环
Serial.println("[SPI] Loopback test...");
SPI.begin();
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
byte tx = 0xAB;
byte rx = SPI.transfer(tx);
Serial.print(" TX: 0x"); Serial.print(tx, HEX);
Serial.print(" RX: 0x"); Serial.println(rx, HEX);
SPI.endTransaction();
Serial.println();
这执行了一个简单的 SPI 回环测试:发送 0xAB 并读回接收到的数据。在模拟器中,MISO 和 MOSI 没有外部连接,因此接收到的值可能是 0xFF 或其他值。这测试了 SPI 硬件。
5. ADC
Serial.println("[ADC] Reading analog channels...");
analogReadResolution(12);
int a0 = analogRead(A0);
Serial.print(" A0 (GP26): "); Serial.println(a0);
Serial.println();
我们将 ADC 分辨率设置为 12 位,并从 A0 (GP26) 读取值。读取电位器滑臂的电压,得到一个介于 0 到 4095 之间的值。
6. GPIO 闪烁
Serial.println("[GPIO] Blinking LED...");
for (int i = 0; i < 3; i++) {
digitalWrite(LED_BUILTIN, HIGH);
delay(200);
digitalWrite(LED_BUILTIN, LOW);
delay(200);
}
Serial.println(" 3 blinks done");
Serial.println();
Serial.println("=== All protocol tests complete ===");
}
最后,我们让内置 LED 闪烁三次,表示设置完成。
主循环
void loop() {
// Heartbeat
static unsigned long last = 0;
if (millis() - last >= 3000) {
last = millis();
Serial.print("[Heartbeat] ");
Serial.print(millis() / 1000);
Serial.println("s");
}
}
主循环每 3 秒 打印一次心跳消息,以显示程序仍在运行。
关键概念
- 多协议集成:在一个 sketch 中结合串行、I2C、SPI 和 ADC,教你如何管理多个外设而不产生冲突。
- I2C 寻址:理解如何扫描并与特定地址的设备通信。
- SPI 配置:设置时钟速度、位序和模式。
- ADC 分辨率:使用
analogReadResolution()获得更高精度。 - 串行调试:使用串行监视器输出实时数据。
常见陷阱与调试技巧
- I2C 上拉电阻:Pico 有内部上拉,但较长的导线可能需要外部上拉。在模拟器中,这由系统自动处理。
- SPI 回环:如果没有物理回环,接收到的数据可能是垃圾数据。使用跳线连接 MOSI 和 MISO 进行真正的回环测试。
- ADC 噪声:电位器可能有噪声;在滑臂和地之间添加一个电容有助于抑制噪声。
- 串行波特率不匹配:确保你的串行监视器设置为 115200 波特。
建议的扩展
- 添加一个外部 I2C 传感器(例如温湿度传感器)并显示其读数。
- 使用 SPI 控制 OLED 显示屏。
- 实现一个简单的数据记录器,读取 ADC 值并存储在 EEPROM 中。
- 添加一个按钮来触发不同的测试模式。
亲自尝试!
准备好实验了吗?在 Velxio 中打开实时示例并运行模拟。你可以修改代码、添加组件,并立即看到结果。



祝你编程愉快!