回到首页 返回首页
回到顶部 回到顶部
返回上一页 返回上一页

【Beetle 树莓派RP2350】基于Beetle RP2350的锂电池电压监测系统设计与实现 简单

头像 十四号花店 2025.05.24 9 0

一、项目背景

在移动设备、无人机、可穿戴设备等领域,锂电池电压的实时监测是保障设备安全运行的核心需求。传统监测方案往往存在体积大、功耗高、显示单一等问题。本文基于Beetle RP2350开发板设计了一套嵌入式电压监测系统,通过高精度ADC采样、数据滤波算法和OLED动态显示,实现了电压曲线的实时绘制、三位小数精度显示及可视化电量提示,为便携设备开发提供了高集成度解决方案。
二、硬件平台

1. 核心优势

双核架构:Arm Cortex-M33与RISC-V双核设计,150MHz主频,满足高速数据处理需求。

超小体积:25×20.5mm硬币尺寸,适合空间敏感型应用。

低功耗设计:休眠电流仅25μA,搭配锂电池可长期工作。

丰富接口:支持11个GPIO、UART、I2C、SPI及模拟输入,扩展性强。

2. 关键功能引脚

ADC输入:GPIO27(A1)用于电压采样,12位分辨率(代码中通过超采样提升至等效14位)。

OLED驱动:GPIO4(SDA)、GPIO5(SCL)硬件I2C接口,支持128×64像素显示。

电池管理:集成充电电路与电压监测(BAT引脚),支持3.7V锂电池。

三、系统设计思路

1. 电压测量原理

分压电路:通过电阻R1=100kΩ(实测99.2kΩ)和R2=200kΩ(实测197.2kΩ)将锂电池电压(3.7~4.2V)分压至ADC量程(0~3.3V)。

计算公式

1748024786359.png

其中校准系数(CAL_FACTOR)用于补偿硬件误差。

2. 数据处理优化

滑动窗口滤波:32次采样窗口均值滤波,有效抑制噪声。

超采样技术:单次测量8次ADC采样取平均,提升分辨率。

3. 显示界面设计

动态曲线:108像素宽度曲线区域,每秒5次刷新,展示电压波动趋势。

三位小数精度:左上角显示电压值(如3.745V),满足精密监测需求。

电池图标:右上角四格电量指示,对应4.0V/3.8V/3.6V/3.4V阈值。

左侧刻度:标注4.2V、4.0V、3.8V、3.6V关键电压值,便于快速比对。
本次设计同样支持上位机蓝牙通讯

1748024924791.png

四、代码解析(关键模块)

1. 滤波结构实现

代码
struct {
  float buffer[SAMPLE_WINDOW] = {0};
  uint8_t index = 0;
  float sum = 0;
} filter;
通过循环队列管理采样窗口,每次更新时移除最旧数据并加入新数据,计算均值时复杂度仅为O(1)。

2. 坐标映射算法

代码
int mapVoltageToY(float voltage) {
  const float minV = 3.4, maxV = 4.2;
  voltage = constrain(voltage, minV, maxV);
  return 20 + static_cast<int>((maxV - voltage)/(maxV - minV)*GRAPH_HEIGHT);
}
将电压值线性映射到OLED的Y轴坐标(20~60像素),确保曲线在指定区域绘制。

3. OLED绘制逻辑
 

a426cfc06ff12123bf6fcdd9a829e43.jpg

代码
void updateOLED(float voltage) {
  // 绘制刻度、曲线、数值、电池图标
  drawVoltageScale();
  for (int x = 1; x < GRAPH_WIDTH; x++) {
    u8g2.drawLine(x+GRAPH_X_OFFSET-1, prevY, x+GRAPH_X_OFFSET, currY);
  }
  u8g2.print(voltage, 3); // 三位小数显示
  drawBatteryIndicator(voltage);
}
通过U8g2库的链式调用,优化绘制效率,避免屏幕闪烁。

五、实际应用场景

便携医疗设备:精确监测供电电压,保障设备稳定性。

智能穿戴设备:紧凑设计可直接集成至手表/手环,提升用户体验。

太阳能储能系统:动态跟踪电池充放电曲线,优化能量管理。

六、优化与扩展方向

低功耗优化

利用RP2350休眠模式,按键唤醒采样,进一步降低功耗。

动态调整采样频率(如高负载时加速采样)。

功能扩展

增加蓝牙/Wi-Fi模块,实现手机端远程监控。

安全增强

设置电压阈值报警(蜂鸣器/LED闪烁)。

充放电循环计数,评估电池寿命。

七、结语

本项目充分挖掘了Beetle RP2350的高性能与小体积优势,结合滤波算法与可视化设计,打造了一套高精度、低成本的电压监测系统。代码已开源并适配Arduino生态,开发者可快速复现或二次开发。

附件

代码
#include <Arduino.h>
#include <SoftwareSerial.h>
#include <U8g2lib.h>

// 硬件配置
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显示参数
#define GRAPH_WIDTH 108       // 曲线实际宽度
#define GRAPH_X_OFFSET 30     // 左侧刻度区宽度
#define GRAPH_HEIGHT 40       // 曲线区域高度
float voltageHistory[GRAPH_WIDTH] = {0};

U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(
  U8G2_R0, 
  /* reset=*/ U8X8_PIN_NONE, 
  /* clock=*/ 5,  // SCL-D5
  /* data=*/ 4    // SDA-D4
);

SoftwareSerial hardwareSerial(9, 8);  // RX-D9, TX-D8

// 滤波结构
struct {
  float buffer[SAMPLE_WINDOW] = {0};
  uint8_t index = 0;
  float sum = 0;
} filter;

// 坐标映射(Y轴范围:20-60)
int mapVoltageToY(float voltage) {
  const float minV = 3.4, maxV = 4.2; // 刻度范围3.4-4.2V
  voltage = constrain(voltage, minV, maxV);
  return 20 + static_cast<int>((maxV - voltage)/(maxV - minV)*GRAPH_HEIGHT);
}

// 绘制左侧电压刻度
void drawVoltageScale() {
  u8g2.setFont(u8g2_font_5x7_tr);
  
  // 绘制刻度线和标签
  const float voltages[] = {4.2, 4.0, 3.8, 3.6};
  for(int i=0; i<4; i++){
    int y = mapVoltageToY(voltages[i]);
    u8g2.drawLine(GRAPH_X_OFFSET-8, y, GRAPH_X_OFFSET-2, y); // 刻度线
    u8g2.setCursor(0, y+3); // 文本位置微调
    u8g2.print(voltages[i],1);
  }
}

// 右上角电池图标(保持原始位置)
void drawBatteryIndicator(float voltage) {
  int level = 0;
  if (voltage >= 4.0)       level = 4;
  else if (voltage >= 3.8) level = 3;
  else if (voltage >= 3.6) level = 2;
  else if (voltage >= 3.4) level = 1;

  u8g2.drawFrame(106, 2, 20, 12);    // 电池外框
  u8g2.drawBox(126, 5, 2, 6);        // 正极触点
  
  for (int i = 0; i < 4; i++) {
    if (level > i) {
      u8g2.drawBox(108 + i*4, 4, 3, 8);
    }
  }
}

float readPrecisionVoltage() {
  filter.sum -= filter.buffer[filter.index];
  
  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();
  
  // 绘制左侧电压刻度
  drawVoltageScale();
  
  // 绘制电压曲线
  for (int x = 1; x < GRAPH_WIDTH; x++) {
    int prevY = mapVoltageToY(voltageHistory[x-1]);
    int currY = mapVoltageToY(voltageHistory[x]);
    u8g2.drawLine(x+GRAPH_X_OFFSET-1, prevY, x+GRAPH_X_OFFSET, currY);
  }

  // 左上角电压显示(三位小数)
  u8g2.setFont(u8g2_font_ncenB14_tr);
  u8g2.setCursor(0, 15);
  u8g2.print(voltage, 3);
  u8g2.print("V");

  // 绘制电池图标
  drawBatteryIndicator(voltage);
  
  u8g2.sendBuffer();
}

void setup() {
  Serial.begin(115200);
  hardwareSerial.begin(115200);
  analogReadResolution(12);

  u8g2.begin();
  u8g2.setContrast(150);
  u8g2.clearDisplay();
  u8g2.setFlipMode(0);
}

void loop() {
  static uint32_t lastUpdate = 0;
  
  if (millis() - lastUpdate >= 200) {
    float voltage = readPrecisionVoltage();
    
    // 更新历史数据
    memmove(voltageHistory, voltageHistory + 1, sizeof(float)*(GRAPH_WIDTH-1));
    voltageHistory[GRAPH_WIDTH-1] = voltage;

    // 串口输出
    char report[40];
    snprintf(report, sizeof(report), "VOL:%.3f RAW:%.1f", voltage, filter.sum/SAMPLE_WINDOW);
    Serial.println(report);
    hardwareSerial.println(report);

    updateOLED(voltage);
    lastUpdate = millis();
  }
}

评论

user-avatar