手把手教你用STM32和AFE芯片搭建简易锂电池BMS保护板
在电子DIY领域,锂电池管理系统(BMS)一直是热门话题。无论是电动滑板车、便携式储能设备还是自制机器人,锂电池的安全使用都离不开BMS的保护。本文将带你从零开始,用STM32微控制器和常见的模拟前端(AFE)芯片,打造一个具备基础保护功能的BMS系统。
1. 硬件选型与准备
1.1 核心组件选择
搭建BMS系统需要三大核心硬件:
主控MCU:STM32F103C8T6(蓝色药丸开发板)
- 72MHz主频,64KB Flash,20KB RAM
- 内置12位ADC,支持多通道采样
- 丰富的定时器和GPIO资源
AFE芯片:TI BQ76940(3-10串锂电池监控)
- 集成16位Σ-Δ ADC
- 支持电压、电流、温度监测
- 内置硬件保护功能(OV/UV/OC/SC)
- I2C通信接口
功率MOSFET:IRLML6244(双N沟道)
- Vds=20V,Id=6.4A
- 低导通电阻(Rds(on)=0.022Ω)
- 逻辑电平驱动(Vgs=1.8V)
1.2 辅助元件清单
| 类别 | 型号/参数 | 数量 | 备注 |
|---|---|---|---|
| 传感器 | NTC 10K B值3950 | 3 | 温度监测 |
| 电阻 | 0.005Ω/2W | 1 | 电流采样 |
| 电容 | 100nF 0805 | 10 | 去耦电容 |
| 二极管 | SS34 | 2 | 防反接保护 |
| 连接器 | XH2.54 | 若干 | 电池组接口 |
提示:BQ76940需要额外配置0.1μF去耦电容和10kΩ上拉电阻(I2C总线)
2. 电路设计与连接
2.1 电源与信号路径
完整的BMS电路包含以下几个关键部分:
电池接口电路
- 正负极输入端子
- 预充电阻(100Ω/2W)
- 保险丝(根据电池容量选择)
AFE外围电路
- 各节电池电压检测线(VC1-VC10)
- 电流采样差分输入(SRP/SRN)
- NTC温度传感器接口
MOS驱动电路
- 栅极驱动电阻(10Ω)
- 栅极下拉电阻(100kΩ)
- 续流二极管(防止电感反峰)
2.2 典型连接示意图
电池组+ ────┬─────── MOSFET ──── 负载+ │ (放电控制) ├── BQ76940 VCx引脚 │ 电池组- ────┼─────── 电流采样 ──── 负载- │ (0.005Ω) └── STM32 3.3V LDO注意:实际布线时,大电流路径(充放电回路)应使用足够粗的导线(建议≥18AWG)
3. 软件配置与编程
3.1 STM32开发环境搭建
- 安装STM32CubeIDE
- 创建新工程(STM32F103C8Tx)
- 配置外设:
- I2C1(标准模式,100kHz)
- USART1(调试输出,115200bps)
- ADC1(规则组,连续转换模式)
// I2C初始化示例 hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;3.2 BQ76940寄存器配置
AFE芯片需要初始化以下关键寄存器:
- SYS_CTRL1:使能ADC和保护功能
- PROTECT1/2/3:设置保护阈值
- OV_TRIP/UV_TRIP:过压/欠压触发点
- OCD_TRIP/SCD_TRIP:过流/短路保护值
#define BQ76940_I2C_ADDR 0x08 void BQ76940_Init(void) { uint8_t config[2]; // 使能CC测量和所有保护 config[0] = 0x19; // SYS_CTRL1地址 config[1] = 0x1B; // 使能ADC+保护 HAL_I2C_Master_Transmit(&hi2c1, BQ76940_I2C_ADDR, config, 2, 100); // 设置过压保护为4.2V config[0] = 0x51; // OV_TRIP地址 config[1] = 0xA5; // 4.2V对应的值 HAL_I2C_Master_Transmit(&hi2c1, BQ76940_I2C_ADDR, config, 2, 100); }4. 保护逻辑实现
4.1 电压保护策略
典型的电压保护实现流程:
- 读取所有单体电压(BQ76940的VCx寄存器)
- 计算总电压和平均电压
- 检查以下条件:
- 任何单体电压 > 过压阈值(如4.2V)
- 任何单体电压 < 欠压阈值(如2.8V)
- 总电压 > 过压阈值(n×4.2V)
- 总电压 < 欠压阈值(n×2.8V)
#define CELL_OV_THRESHOLD 4200 // 单位mV #define CELL_UV_THRESHOLD 2800 void CheckVoltageProtection(void) { uint16_t cell_voltages[10]; int total_voltage = 0; // 读取各节电压(伪代码) ReadAllCellVoltages(cell_voltages); for(int i=0; i<10; i++) { total_voltage += cell_voltages[i]; if(cell_voltages[i] > CELL_OV_THRESHOLD) { SetProtectionFlag(OV_PROTECT); DischargeMOS_Off(); } if(cell_voltages[i] < CELL_UV_THRESHOLD) { SetProtectionFlag(UV_PROTECT); ChargeMOS_Off(); } } }4.2 电流与温度保护
电流保护需要考虑两个方向:
- 充电过流:正电流超过阈值
- 放电过流:负电流超过阈值
温度保护实现要点:
温度传感器布局建议: 1. 靠近功率MOSFET 2. 电池组中间位置 3. 环境温度参考点保护恢复策略通常采用滞回比较:
| 保护类型 | 触发值 | 恢复值 | 延时时间 |
|---|---|---|---|
| 过压 | 4.20V | 4.15V | 500ms |
| 欠压 | 2.80V | 3.00V | 1s |
| 过流 | 5.0A | 4.0A | 100ms |
5. 系统调试与优化
5.1 常见问题排查
电压采样不准:
- 检查AFE基准电压(通常为3.3V)
- 确认分压电阻精度(建议1%)
- 注意PCB布局(模拟信号远离数字线)
MOSFET发热严重:
- 测量导通压降(Vds)
- 检查栅极驱动电压(Vgs)
- 确认PWM频率(建议10-20kHz)
5.2 性能优化技巧
- ADC采样优化:
- 使用DMA传输减少CPU开销
- 添加数字滤波(移动平均或IIR)
// 简单的移动平均滤波示例 #define FILTER_WINDOW 8 uint16_t MovingAverage(uint16_t new_sample) { static uint16_t buffer[FILTER_WINDOW] = {0}; static uint8_t index = 0; uint32_t sum = 0; buffer[index++] = new_sample; if(index >= FILTER_WINDOW) index = 0; for(int i=0; i<FILTER_WINDOW; i++) { sum += buffer[i]; } return (uint16_t)(sum / FILTER_WINDOW); }- 低功耗设计:
- 在空闲时进入STOP模式
- 降低ADC采样频率(保护状态下)
- 关闭不必要的外设时钟
6. 进阶功能扩展
6.1 被动均衡实现
通过BQ76940的CB_CTRL寄存器控制均衡MOS:
void EnableCellBalance(uint8_t cell_mask) { uint8_t cmd[2]; // 设置均衡目标(bit0对应第1节电池) cmd[0] = 0x31; // CB_CTRL地址 cmd[1] = cell_mask; HAL_I2C_Master_Transmit(&hi2c1, BQ76940_I2C_ADDR, cmd, 2, 100); // 超时检查(建议最长均衡2小时) balance_start_time = HAL_GetTick(); }均衡策略建议:
- 仅在充电时启动(SOC>80%)
- 电压差>50mV时触发
- 单次均衡不超过30分钟
6.2 数据记录与通信
添加UART或蓝牙模块实现:
- 实时电压/电流曲线显示
- 保护事件记录(带时间戳)
- 手机APP监控(通过HC-05模块)
typedef struct { uint32_t timestamp; uint16_t voltage[10]; int16_t current; uint8_t temp[3]; uint8_t status; } BMS_DataFrame; void SendDataToPC(BMS_DataFrame *data) { uint8_t buffer[sizeof(BMS_DataFrame)+2]; // 添加帧头帧尾 buffer[0] = 0xAA; memcpy(&buffer[1], data, sizeof(BMS_DataFrame)); buffer[sizeof(buffer)-1] = 0x55; HAL_UART_Transmit(&huart1, buffer, sizeof(buffer), 100); }在实际项目中,我发现最实用的调试手段是在保护触发时保存现场数据,这能大幅缩短故障排查时间。比如当发生过流保护时,记录触发前10秒的电流波形,对分析负载异常非常有帮助。