1. 项目背景与核心价值
在新能源和储能领域,电池管理系统(BMS)堪称电池组的"大脑"。我最近完整拆解了一套基于STM32F103和uC/OS-II的工业级BMS源代码,这套系统曾实际应用于电动叉车储能项目,稳定运行超过3年无故障。不同于学术demo,这套代码展现了真实工程中的架构设计取舍和细节处理技巧。
选择STM32F103作为主控颇具代表性——虽然现在有更新款的MCU,但这款经典Cortex-M3芯片在成本敏感型工业场景中仍占有一席之地。配合uC/OS-II这个经受过时间考验的RTOS,构成了一个既保证实时性又兼顾开发效率的技术组合。在剖析过程中,我发现开发者对以下三个工业痛点的处理尤为精妙:
- 电压采样精度与速度的平衡(16通道锂电芯监测)
- 多任务间的数据同步机制(SOC估算与故障检测的实时性要求)
- 非易失性参数的存储策略(循环擦写次数与数据可靠性的矛盾)
2. 硬件架构设计解析
2.1 主控芯片选型依据
STM32F103C8T6的选择体现了典型的工程权衡:
- 72MHz主频足够处理10ms周期的电池均衡算法
- 内置12位ADC(实际有效位ENOB约10.5位)满足±20mV电压检测精度
- 多达5个USART接口兼容多种通信协议(项目中用到了CAN 2.0B和Modbus RTU)
经验提示:工业现场优先选择LQFP封装而非BGA,便于售后维护时的芯片级维修。我们曾遇到BGA虚焊导致采样异常的问题,最终在PCB上增加了热风枪维修焊盘。
2.2 关键外围电路设计
电压采样链路的三个设计亮点:
- 二级RC滤波(10Ω+100nF)配合ADC内置采样保持电容,有效抑制PWM干扰
- 光耦隔离的MOSFET驱动电路,栅极电阻并联18V稳压管防止击穿
- 电流检测采用75mV分流器+INA282放大方案,软件端做温度漂移补偿
电池均衡拓扑值得特别注意:
c复制// 被动均衡电阻计算示例(假设均衡电流300mA)
#define BALANCE_R ((3.7V * 4) / 0.3A) ≈ 49Ω // 实际选用51Ω/5W电阻
这种设计在2A均衡电流下会产生约7W热耗,因此PCB上均衡电阻的布局必须考虑热岛效应。
3. 实时系统任务划分
3.1 uC/OS-II任务调度策略
系统创建了6个任务,优先级安排如下表:
| 任务名称 | 优先级 | 周期(ms) | 堆栈深度 | 关键操作 |
|---|---|---|---|---|
| BMS_Monitor | 5 | 10 | 256 | 电压/温度采样、故障判断 |
| SOC_Estimator | 6 | 1000 | 512 | 安时积分+开路电压法融合估算 |
| CAN_Comm | 7 | 50 | 128 | CAN报文收发与协议解析 |
| Balance_Ctrl | 8 | 100 | 128 | 动态均衡策略执行 |
| Data_Logger | 9 | 500 | 512 | 关键参数存储到EEPROM |
| System_Watchdog | 10 | 1000 | 64 | 喂狗及系统状态上报 |
任务间通信采用两种机制:
- 采样数据通过事件标志组同步(OSFlagPend/OSFlagPost)
- 控制命令通过消息队列传递(OSQPend/OSQPost)
3.2 中断服务例程优化
ADC采样中断的临界区处理很有参考价值:
c复制void ADC_IRQHandler(void) {
OS_ENTER_CRITICAL(); // 关中断
raw_adc_buf[ch_idx] = ADC1->DR;
if(++ch_idx >= 16) {
OSFlagPost(adc_flag, ADC_COMPLETE_FLAG);
ch_idx = 0;
}
ADC1->CR2 |= ADC_CR2_SWSTART;
OS_EXIT_CRITICAL(); // 开中断
}
这种设计将ADC转换时间从软件触发方式的约25μs缩短到硬件触发方式的7μs,确保在10ms任务周期内完成所有通道采样。
4. 核心算法实现细节
4.1 电池SOC估算方案
采用改进型安时积分法,关键校正策略包括:
- 充电末端OCV校正(当检测到ΔV<2mV/min时触发)
- 温度补偿系数(每5℃一个区间的查表法)
- 容量衰减模型(基于循环次数的线性修正)
实际代码中的滑动平均滤波实现:
c复制#define FILTER_DEPTH 8
uint16_t soc_filter_buf[FILTER_DEPTH];
uint16_t moving_average(uint16_t new_val) {
static uint8_t idx = 0;
uint32_t sum = 0;
soc_filter_buf[idx++] = new_val;
if(idx >= FILTER_DEPTH) idx = 0;
for(uint8_t i=0; i<FILTER_DEPTH; i++) {
sum += soc_filter_buf[i];
}
return (sum + FILTER_DEPTH/2) / FILTER_DEPTH; // 四舍五入
}
4.2 主动均衡控制逻辑
动态阈值均衡算法流程图解:
- 检测单体电压极差 > 50mV 触发均衡
- 计算各单体与平均值的偏差
- 偏差>30mV的电池开启均衡
- 每10秒重新评估均衡状态
均衡MOSFET的占空比控制代码:
c复制void set_balance_duty(uint8_t cell_num, uint8_t duty) {
if(duty > 90) duty = 90; // 安全限制
TIM1->CCR1 = (duty * TIM1->ARR) / 100; // 使用TIM1 CH1
GPIO_SetBits(BALANCE_EN_PORT, 1<<cell_num);
}
5. 工程经验与故障排查
5.1 EEPROM存储优化技巧
针对STM32内部Flash模拟EEPROM的痛点,开发者采用了以下策略:
- 关键参数分三区存储(主备+临时区)
- 采用XOR校验而非CRC16以节省计算资源
- 写操作前检查数据是否变化,减少擦写次数
存储结构体定义示例:
c复制#pragma pack(push, 1)
typedef struct {
uint32_t cycle_count;
uint16_t max_volt_cell;
uint16_t min_volt_cell;
uint8_t xor_check; // 结构体所有字节异或值
} BMS_Params;
#pragma pack(pop)
5.2 典型故障处理实录
问题现象:CAN通信偶发丢帧
排查过程:
- 用示波器捕捉CANH/CANL波形,发现总线阻抗不匹配
- 测量终端电阻实际为100Ω而非标准的120Ω
- 检查PCB发现电阻封装错误(0805焊盘贴了1206元件)
- 更换正确电阻后通信稳定
问题现象:低温环境下SOC跳变
解决方案:
- 在-20℃实验室复现问题
- 发现温度传感器响应速度慢于电芯实际温度变化
- 增加温度变化率预测算法
- 修改NTC安装位置贴近电芯极柱
6. 代码架构设计亮点
6.1 分层式软件架构
系统采用经典的硬件抽象层(HAL)设计:
code复制/Drivers
/BSP // 板级支持包
/LIB // 通用库
/Modules
/ADC // 采样模块
/CAN // 通信模块
/BAL // 均衡模块
/App // 应用层任务
6.2 状态机设计模式
充电过程状态机实现示例:
c复制typedef enum {
CHG_IDLE,
CHG_PRECHARGE,
CHG_CC,
CHG_CV,
CHG_COMPLETE
} ChargeState;
void charge_state_machine(void) {
static ChargeState state = CHG_IDLE;
switch(state) {
case CHG_IDLE:
if(plug_detected()) state = CHG_PRECHARGE;
break;
case CHG_PRECHARGE:
if(voltage > precharge_thresh) state = CHG_CC;
break;
// ...其他状态转换
}
}
这套代码最值得借鉴的是其异常处理机制——所有关键操作都有超时判断和回退策略。例如在EEPROM写入失败时,系统会自动切换备份区并记录错误计数,这种防御性编程思维正是工业级代码的核心特征。
