RPDuino-2040阶段2:从PCB设计到焊接调试,这块RP2040 UNO板跑通了

RPDuino2040阶段2从PCB设计到焊接调试这块RP2040UNO板跑通了
\n

Arduino UNO 用了快二十年,大家熟悉它的板形、接口和扩展板生态;但如果只停留在“换个更强 MCU”的 PCB 设计图上,这件事还只能算是一个不错的想法。

 

真正有意思的部分,是板子打回来之后——焊接能不能过?USB 能不能正常枚举?I2C、SPI、ADC、SD 卡、OLED、RGB 灯这些外设能不能跑起来?以及更现实一点:上电的时候,会不会踩坑?

 

在 Make Blocks 第一期阶段1中,作者鲜de芒果设计了一块基于 Raspberry Pi RP2040 的 Arduino UNO 板形开发板:RPDuino-2040。我们之前已经介绍过它的设计思路:UNO 外形不变、扩展板生态不变,核心换成双核 Cortex-M0+ 的 RP2040。

 

这次阶段2,作者把这块板子真正做出来,并完成了焊接、调试和功能验证。从“看起来不错的 PCB”,到“真的能跑的开发板”,中间隔着的,就是这篇阶段2记录。

图片

 

01

从设计图到实物板

RPDuino-2040 的定位依然很清晰:它是一块基于 Raspberry Pi RP2040 的 Arduino UNO R3 板形开发板。

核心不再是传统 Arduino UNO 上的 ATmega328P,而是换成了 RP2040:

项目 配置
主控 Raspberry Pi RP2040
内核 双核 Cortex-M0+
主频 133MHz
SRAM 264KB
Flash W25Q16,2MB QSPI Flash
时钟 12MHz 晶振
USB Type-C,供电 + 数据
调试 SWD 接口
板载资源 BOOT / RESET 按键、电源 LED、用户 LED、WS2812B RGB 灯珠
扩展接口 Arduino UNO R3 标准接口、I2C、SPI、UART、ADC、GPIO

图片

它保留了 Arduino UNO R3 的接口规范,也就是说,原本 UNO 生态里的很多扩展板、模块和接线习惯,都可以继续沿用。

 

这也是这个项目最核心的价值:不是单纯做一块 RP2040 开发板,而是把 RP2040 放进一个大家非常熟悉的 Arduino UNO 机械尺寸和接口体系里。

 

02

这次阶段2主要验证了什么?

阶段1更偏向“设计”:原理图、PCB、接口定义、板形兼容性。阶段2则进入“工程验证”:板子能不能焊,能不能上电,能不能正常跑程序。

 

作者这次完成的验证内容包括:

  • 板载 LED 控制

  • WS2812B RGB 灯珠控制

  • USB 串口通信

  • I2C 设备扫描

  • SD 卡初始化、文件写入和读取

  • 摇杆模拟信号采集

  • OLED 显示系统状态和 ADC 数据

 

这几个功能看起来分散,其实覆盖了嵌入式开发板最常见的几类基础能力:

功能 对应验证点
LED / WS2812B GPIO、时序控制、基础 IO 输出
串口通信 USB CDC、调试信息输出
I2C 扫描 I2C 总线、OLED 地址识别
SD 卡读写 SPI 总线、文件系统、外部存储
摇杆采集 ADC 模拟输入
OLED 显示 I2C 外设驱动、数据显示

也就是说,这不是简单地点个灯,而是把这块板作为一块“可用开发板”做了一轮比较完整的基础体检。

 

03

测试程序怎么跑?

图片

作者使用 PlatformIO 工程完成开发板功能测试,程序主要分成几部分:

  • 第一步,初始化串口、LED、摇杆输入、WS2812B 和 OLED。程序启动后,会打开 115200 波特率的串口,初始化板载 LED、摇杆 ADC 引脚和 WS2812B RGB 灯珠,同时启动 OLED,并设置 I2C 地址为 0x3C。

  • 接着,程序会执行 I2C 扫描,看看总线上是否能找到 OLED 等 I2C 设备。

  • 再往后,是 SD 卡测试,包括 SD 卡初始化、卡类型识别、容量读取、文件写入和文件读取。

  • 最后进入主循环:

    • 串口持续输出 “Hello!”

    • 读取摇杆 ADC 值

    • WS2812B 和板载 LED 交替闪烁

    • OLED 显示项目名称、作者信息、运行时间和 ADC 数值

这套测试程序的好处是,它把多个外设串在了一起。只要它能稳定运行,就能说明这块板的基本硬件链路是通的。

 

 

I2C:先扫描,再驱动

在调试开发板时,I2C 扫描是一个非常实用的小工具。

很多时候 OLED 不亮,不一定是代码逻辑问题,可能是:

  • I2C 地址不对

  • SDA / SCL 接反

  • 上拉电阻问题

  • 模块没供电

  • 焊接虚焊

  • 总线被其他器件拉住

作者的程序中先对 1 到 126 的 I2C 地址逐一扫描,如果设备响应,就通过串口打印出地址。

这一步很适合放在开发板 bring-up 的早期。不要一上来就写复杂显示逻辑,先确认总线上确实有设备,地址也正确,再进入 OLED 驱动部分。

核心初始化

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
void setup() {  Serial.begin(115200);               // 串口  delay(5000);  pinMode(LED_BUILDIN, OUTPUT);       // 板载LED引脚初始化  pinMode(JOYSTICK_PIN, INPUT);       // 摇杆引脚初始化  FastLED.addLeds(leds, NUM_LEDS);  // 板载WS2812B灯珠初始化  // 初始化OLED并设置其IIC地址为 0x3C  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // 扫描IIC地址  Serial.println("\nI2C Scanner");  scanI2c();  Serial.println("I2C Scanner done.");  readSDFile();}

I2C设备扫描

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
void scanI2c() {  byte error, address;  int nDevices = 0;  Serial.println("Scanning...");  for(address = 1; address < 127; address++ ) {    Wire.beginTransmission(address);    error = Wire.endTransmission();    if (error == 0) {      Serial.print("I2C device found at address 0x");      if (address<16)        Serial.print("0");      Serial.print(address,HEX);      Serial.println("  !");      nDevices++;    } else if (error==4) {      Serial.print("Unknown error at address 0x");      if (address<16)        Serial.print("0");      Serial.println(address,HEX);    }  }  if (nDevices == 0) {    Serial.println("No I2C devices found\n");  } else {    Serial.println("done\n");  }}

 

 

SD卡:看似简单,实际最容易踩坑

SD 卡测试是这次阶段2里很值得看的部分。

作者一开始使用的是 adafruit/SdFat - Adafruit Fork 库,但编译和读取都不顺利。后面改用 RP2040_SD 库,并通过逐步输出调试信息,才定位到 SD 卡格式相关的问题。

这其实是很多嵌入式项目都会遇到的情况:

硬件连接看起来没问题,SPI 引脚也配置了,代码也用了成熟库,但就是读不出来。

这时候最怕的是直接判断“板子坏了”或者“线连错了”。更好的方式是像作者这样,把初始化过程拆开:

  • 先打印当前使用的 CS、SCK、MOSI、MISO 引脚

  • 再检查 SD 卡是否能初始化

  • 再识别 SD 卡类型

  • 再读取容量

  • 最后再做文件写入和读取

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
void readSDFile() {  Serial.println(BOARD_NAME);  Serial.println(RP2040_SD_VERSION);  Serial.print("Initializing SD card with SS = ");  Serial.println(PIN_SD_SS);  Serial.print("\tSCK = ");   Serial.println(PIN_SD_SCK);  Serial.print("\tMOSI = ");  Serial.println(PIN_SD_MOSI);  Serial.print("\tMISO = ");  Serial.println(PIN_SD_MISO);  if(!card.init(SPI_HALF_SPEED, PIN_SD_SS)) {    Serial.println("initialization failed. Things to check:");    Serial.println("* is a card inserted?");    Serial.println("* is your wiring correct?");    Serial.println("* did you change the chipSelect pin to match your shield or module?");    return;  }  // 卡类型检测  switch (card.type()) {    case SD_CARD_TYPE_SD1:      Serial.println("SD1");      break;    case SD_CARD_TYPE_SD2:      Serial.println("SD2");      break;    case SD_CARD_TYPE_SDHC:      Serial.println("SDHC");      break;    default:      Serial.println("Unknown");  }  // 计算卡容量  uint32_t volumesize;  volumesize = volume.blocksPerCluster();  volumesize *= volume.clusterCount();  volumesize *= 512;  Serial.print("Volume size (bytes): ");  Serial.println(volumesize);  Serial.print("Volume size (Kbytes): ");  Serial.println(volumesize / 1024);  Serial.print("Volume size (Mbytes): ");  Serial.println((float)volumesize / 1048576.0);  // 初始化SD库  if (!SD.begin(PIN_SD_SS)) {    Serial.println("Initialization failed!");    return;  }  Serial.println("Initialization done.");  // 写入测试  char writeData[] = "Testing writing to " TEST_FILE_NAME;  myFile = SD.open(TEST_FILE_NAME, FILE_WRITE);  if (myFile) {    Serial.print("Writing to "); Serial.print(TEST_FILE_NAME);     Serial.print(" ==> "); Serial.println(writeData);    myFile.println(writeData);    myFile.close();    Serial.println("done.");  } else {    Serial.print("Error opening "); Serial.println(TEST_FILE_NAME);  }  // 读取测试  myFile = SD.open(TEST_FILE_NAME, FILE_READ);  if (myFile) {    Serial.print("Reading from "); Serial.println(TEST_FILE_NAME);    Serial.println("===============");    while (myFile.available()) {      Serial.write(myFile.read());    }    myFile.close();    Serial.println("===============");  } else {    Serial.print("Error opening "); Serial.println(TEST_FILE_NAME);  }}

 

 

OLED显示:把板卡状态可视化

OLED 部分的显示内容并不复杂,但很实用。

屏幕上显示:

  • RPDuino Test

  • Author 信息

  • 开机后的运行时间

  • 摇杆 ADC 采样值

这类小屏幕在调试开发板时很有帮助。因为很多时候,串口调试需要接电脑;而 OLED 可以让开发板自己把状态显示出来。

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
void words_display() {  // 清除屏幕  display.clearDisplay();  // 设置字体颜色,白色可见  display.setTextColor(WHITE);  //设置字体大小  display.setTextSize(1.5);  //设置光标位置  display.setCursor(0, 0);  display.print("RPDuino Test");  display.setCursor(0, 14);  display.print("Author: ");  display.print("Bunco");  display.setCursor(0, 28);  display.print("time: ");  //打印自开发板重置以来的秒数:  display.print(millis() / 1000);  display.print(" s");  display.setCursor(0, 42);  display.print("adc : ");  display.print(joystick_adc_value);}

 

04

测试结果:基础功能全部通过

作者整理了一张测试结果表,阶段2验证项基本都通过了。

测试项 预期结果 实际结果 状态
系统初始化 所有设备初始化成功 成功
串口通信 正常输出调试信息 成功
I2C 扫描 检测到 OLED 设备 成功
SD 卡初始化 识别 SD 卡类型和容量 成功
文件读写 成功创建和读取文件 成功
LED 控制 板载 LED 闪烁 成功
WS2812B 控制 RGB 灯珠点亮 成功
摇杆采集 正确读取模拟值 成功
OLED 显示 显示系统信息 成功

从这个结果看,RPDuino-2040 已经完成了作为一块基础开发板最关键的 bring-up。

它不只是“焊上电源灯亮了”,而是把数字 IO、模拟输入、I2C、SPI、USB 串口、外部存储和显示模块都做了验证。

图片

 

05

踩坑记录

相比“功能全部成功”,这篇项目记录里更值得工程师看的,其实是两个问题。

 

Type-C母座连焊,差点烧电脑USB口

作者在完成打板、贴片和焊接后,先测了一下 VIN、5V、3.3V,没有发现对地短路。于是直接插上 USB,结果差点把电脑 USB 口烧毁。最后排查发现,是 Type-C 母座焊脚存在连焊,造成 USB 短路。

 

这个问题非常典型。

USB Type-C 母座引脚密、焊盘小,如果是手焊或者回流后没有认真检查,很容易出现细小连焊。更麻烦的是,很多短路并不一定出现在 5V 到 GND 这种一眼能量到的位置,也可能藏在接口附近。

所以开发板首次上电时,建议多加几步:

  • 先检查 Type-C、LDO、主控、电源入口这些高风险区域

  • 用万用表量 VBUS、5V、3.3V、GND 之间是否短路

  • 条件允许时,用限流电源先上电

  • USB 接电脑前,最好先用电流表或 USB 测试表看一下输入电流

  • Type-C 座子焊完后,用放大镜或显微镜检查焊脚

很多板子的第一次 bring-up,真正危险的不是代码,而是电源和接口焊接。

 

 

SD卡读不出来

作者最开始使用 adafruit/SdFat - Adafruit Fork 库,结果编译和读取都不顺利。后面换成 RP2040_SD 库,并逐步打印调试信息,才发现问题与 SD 卡格式有关。

SD 卡相关问题通常会混在一起:

  • 库是否支持当前平台

  • SPI 引脚是否定义正确

  • CS 引脚是否配置正确

  • SD 卡格式是否兼容

  • 卡座焊接是否可靠

  • 供电是否稳定

  • 线长和速率是否合适

所以调 SD 卡时,不建议直接写复杂业务代码。先用最小测试程序确认初始化、类型识别、容量读取、写文件、读文件,再往上叠加应用逻辑。

 

06

这块板后续可以怎么玩?

RPDuino-2040 已经具备 Arduino UNO 板形、RP2040 性能和多种外设接口。后续可以继续扩展出很多玩法。

比如:

- 用作高性能 Arduino UNO 替代板

对于原本基于 UNO 的项目,如果遇到 Flash、SRAM 或主频不够的问题,可以考虑迁移到这类 RP2040 UNO 兼容板上。

尤其是需要更多缓存、更快采样、更复杂控制逻辑的应用,RP2040 会比 ATmega328P 宽裕很多。

 

- 用作学习套件主控

项目页里提到,这块板主要用于开发学习套件主控,常用于实时信号采集与处理场景。

这类场景下,Arduino UNO 的生态和 RP2040 的性能结合起来,确实比较适合教学和实验:

  • 初学者可以继续用 Arduino 风格开发

  • 进阶用户可以使用 RP2040 C/C++ SDK

  • 想玩脚本化开发的人可以使用 MicroPython / CircuitPython

  • 想研究底层时序的人可以继续探索 PIO

 

- 用作 Make Blocks 模块化系统的一块主控板

既然它兼容 UNO 板形,又引出了 GPIO、I2C、SPI、UART、ADC 等接口,那么后续很适合作为模块化硬件系统中的主控板使用。

传感器、显示、通信、存储、控制类模块,都可以围绕它继续扩展。

 

07

总结

很多开源硬件项目看起来都很顺:画板、打样、上电、成功。

但真正做过板子的人都知道,中间最磨人的地方,往往是那些不起眼的小问题:一个 Type-C 焊脚连焊、一张 SD 卡格式不对、一个库不适配、一个引脚定义没确认,都可能让你查半天。

 

RPDuino-2040 阶段2已经把“设计”推进到了“验证”。这块基于 RP2040 的 Arduino UNO 板形开发板,已经完成了焊接调试,并跑通了串口、I2C、SD 卡、LED、WS2812B、摇杆 ADC 和 OLED 显示等基础功能。

 

从结果看,它已经具备了一块学习型主控开发板的基本形态;从过程看,它也留下了不少值得参考的 bring-up 经验。

对于想学习 RP2040、Arduino 兼容板设计、开发板调试流程,或者正在做 Make Blocks 模块化硬件设计的同学来说,这个项目值得点进去看看。

 

完整项目查看:

项目链接:https://www.eetree.cn/project/detail/5135

附件:RPDuino-test.zip内容:开发板功能测试、PlatformIO 工程源码

图片

点击阅读原文查看项目

 

暂无评论,快来发表第一条评论吧!

📮 需求咨询