引言
Beetle RP2350是DFRobot推出的一款高性能微型开发板,尺寸仅25×20.5mm(硬币大小),搭载树莓派RP2350芯片,支持双核架构(Cortex-M33或RISC-V),主频150MHz,配备520KB RAM和2MB Flash,适合嵌入式开发。其高度集成的特性(如锂电池管理、低功耗设计)和丰富的通信接口(UART、I2C、SPI、PWM等),使其成为创客项目的理想选择。
本文将通过一个高精度电压测量项目,结合OLED显示与串口通信,展示Beetle RP2350的硬件能力与编程技巧,并简要解析其多协议通信的实现方法。
一、硬件准备
核心硬件:
Beetle RP2350开发板(SKU: DFR1188)
0.96英寸OLED显示屏(I2C接口,SSD1306驱动)
分压电阻:R1=99.2kΩ、R2=197.2kΩ(用于扩展电压测量范围)
被测电压源(0-30V直流)
连接方式:
OLED:SCL接D5,SDA接D4
分压电路:输入电压接R1与R2串联节点,R2另一端接地,分压输出接A1引脚(D27)
串口:UART2(D8-TX、D9-RX)用于调试输出
关键引脚:
ADC_PIN = 27:模拟输入引脚A1,12位ADC分辨率
I2C引脚:D4(SDA)、D5(SCL)用于驱动OLED
硬件串口:D8(TX2)、D9(RX2)用于数据输出
二、代码深度解析
1. 核心算法:高精度电压测量
readPrecisionVoltage()函数
float readPrecisionVoltage() {
// 超采样:8次采样取平均,降低噪声
uint32_t rawSum = 0;
for(int i=0; i<8; i++) {
rawSum += analogRead(ADC_PIN);
delayMicroseconds(150); // 避免采样间隔过短
}
float newSample = rawSum / 8.0;
// 滑动窗口滤波:32点移动平均
filter.sum -= filter.buffer[filter.index];
filter.buffer[filter.index] = newSample;
filter.sum += newSample;
filter.index = (filter.index + 1) % SAMPLE_WINDOW;
// 电压计算
float dividerVoltage = (filter.sum / SAMPLE_WINDOW * VREF_ACTUAL) / 4095.0;
return dividerVoltage * (R1 + R2) / R2 * CAL_FACTOR;
}
超采样(Oversampling):8次采样取平均,利用统计特性抑制随机噪声。
滑动窗口滤波:32个样本的移动平均,消除短期波动,提升稳定性。
分压计算:通过电阻分压公式 V_actual = V_adc * (R1+R2)/R2,结合校准因子 CAL_FACTOR 修正硬件误差。
2. OLED显示实现
updateOLED()函数
void updateOLED(float voltage) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.setCursor(0, 30);
u8g2.print("VOL:");
u8g2.print(voltage, 2); // 显示2位小数
u8g2.print("V");
// 状态栏显示项目名称
u8g2.setFont(u8g2_font_6x10_tr);
u8g2.setCursor(0, 63);
u8g2.print("RP2350 ADC");
u8g2.sendBuffer();
}
使用U8g2库驱动128×64 OLED,支持自定义字体和动态刷新。
双字体设计:主界面大字体突出电压值,底部状态栏显示项目标识。
3. 系统初始化与主循环
setup()函数
配置串口波特率(115200)和ADC分辨率(12位)。
初始化OLED并设置对比度,确保显示清晰。
loop()函数
每秒读取一次电压,通过串口输出原始数据和计算结果。
调用updateOLED()刷新显示,避免频繁刷屏导致的闪烁。
三、硬件连接与参数配置
1. 分压电路设计
电阻值:R1=99.2kΩ,R2=197.2kΩ,分压比约1:3。
校准因子:CAL_FACTOR=1.063,用于补偿电阻精度误差或PCB阻抗。
2. OLED连接
Beetle RP2350引脚 | OLED引脚 |
---|---|
D4 (SDA) | SDA |
D5 (SCL) | SCL |
3V3 | VCC |
GND | GND |
四、优化与改进方向
关键实现
1.超采样与滤波:通过8次ADC采样取平均,降低噪声;结合32点移动平均滤波,提升稳定性。
2.分压计算:根据公式 V_actual = V_adc × (R1+R2)/R2,扩展测量范围至30V。
3.校准优化:通过实测调整CAL_FACTOR(代码中为1.063),修正硬件误差。
动态滤波窗口:根据噪声环境自动调整窗口大小(如SAMPLE_WINDOW值)。
温度补偿:添加温度传感器,修正电阻温漂对分压精度的影响。
数据持久化:利用RP2350的Flash存储历史数据,实现离线记录功能。
无线传输:通过Wi-Fi/蓝牙模块将数据发送至手机或云端。
五、实物展示
功能演示
1.串口输出
每秒输出一次电压值及原始数据:VOL:12.345 RAW:2048.3
2.OLED显示
实时显示电压值,精度保留两位小数(如VOL:12.34V)。

实际测量精度

#include <Arduino.h>
#include <SoftwareSerial.h>
#include <U8g2lib.h> // 添加OLED库
// 硬件配置
const int ADC_PIN = 27;
const float R1 = 99200.0;
const float R2 = 197200.0;
const float VREF_ACTUAL = 3.312;
const int SAMPLE_WINDOW = 32;
const float CAL_FACTOR = 1.063;
// 初始化OLED对象(硬件I2C,D5=SCL, D4=SDA)
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(
U8G2_R0,
/* reset=*/ U8X8_PIN_NONE,
/* clock=*/ 5, // D5引脚作为SCL
/* data=*/ 4 // D4引脚作为SDA
);
SoftwareSerial hardwareSerial(9, 8); // 硬件串口D8/D9
// 改进型滤波结构
struct {
float buffer[SAMPLE_WINDOW] = {0};
uint8_t index = 0;
float sum = 0;
} filter;
float readPrecisionVoltage() {
// 移除旧样本
filter.sum -= filter.buffer[filter.index];
// 超采样采集(8次采样取平均)
uint32_t rawSum = 0;
for(int i=0; i<8; i++){
rawSum += analogRead(ADC_PIN);
delayMicroseconds(150);
}
float newSample = rawSum / 8.0;
// 更新滤波窗口
filter.buffer[filter.index] = newSample;
filter.sum += newSample;
filter.index = (filter.index + 1) % SAMPLE_WINDOW;
// 计算分压电压
float dividerVoltage = (filter.sum / SAMPLE_WINDOW * VREF_ACTUAL) / 4095.0;
// 计算实际电压
return dividerVoltage * (R1 + R2) / R2 * CAL_FACTOR;
}
void updateOLED(float voltage) {
u8g2.clearBuffer(); // 清除缓冲区
u8g2.setFont(u8g2_font_ncenB14_tr);
// 绘制电压值
u8g2.setCursor(0, 30);
u8g2.print("VOL:");
u8g2.print(voltage, 2);
u8g2.print("V");
// 绘制状态栏
u8g2.setFont(u8g2_font_6x10_tr);
u8g2.setCursor(0, 63);
u8g2.print("RP2350 ADC");
u8g2.sendBuffer(); // 发送显示数据
}
void setup() {
Serial.begin(115200);
hardwareSerial.begin(115200);
analogReadResolution(12);
// 初始化OLED
u8g2.begin();
u8g2.setContrast(150); // 设置对比度
u8g2.clearDisplay();
u8g2.setFlipMode(0); // 根据需要调整显示方向
}
void loop() {
static uint32_t lastPrint = 0;
if(millis() - lastPrint >= 1000) {
float voltage = readPrecisionVoltage();
// 串口输出
char report[40];
snprintf(report, sizeof(report), "VOL:%.3f RAW:%.1f", voltage, filter.sum/SAMPLE_WINDOW);
Serial.println(report);
hardwareSerial.println(report);
// 更新OLED显示
updateOLED(voltage);
lastPrint = millis();
}
}
评论