1. 项目概述
在嵌入式系统开发中,电池管理系统(BMS)是保障锂电池安全运行的核心组件。这次我基于STM32F103设计了一个完整的BMS系统,实现了多节锂电池的电压、电流、温度监测以及SOC(State of Charge)估算功能。这个项目特别适合需要精确监控锂电池状态的场景,比如电动工具、储能系统或者移动机器人等应用。
1.1 核心功能
这个BMS系统主要实现了以下核心功能:
- 多节锂电池电压采集(支持4串锂电池)
- 电池回路电流双向监测(充放电电流)
- 每节电池温度实时监控
- 基于安时积分法+开路电压修正的SOC估算算法
- 通过串口实时输出监测数据,方便调试和上位机显示
1.2 硬件选型清单
为了确保系统稳定性和测量精度,我精心挑选了以下硬件组件:
| 器件名称 | 型号/规格 | 数量 | 用途说明 |
|---|---|---|---|
| 主控芯片 | STM32F103C8T6 | 1 | 系统核心控制器,性价比高 |
| 电压采集芯片 | ADS1115 | 1 | 16位高精度ADC,I2C接口 |
| 电流传感器 | ACS712-20A | 1 | ±20A量程,模拟输出 |
| 温度传感器 | DS18B20 | 4 | 数字温度传感器,每节电池一个 |
| 分压电阻 | 100kΩ/10kΩ(1%) | 8 | 用于锂电池电压分压 |
| 电源模块 | AMS1117-3.3V | 1 | 系统供电稳压 |
| 串口模块 | CH340 | 1 | USB转串口通信 |
| 锂电池组 | 18650(3.7V) | 4 | 被测电池组 |
选型时特别注意了几个关键点:
- ADS1115的16位分辨率比STM32内置的12位ADC精度更高
- ACS712-20A的±20A量程适合大多数中小功率应用
- 分压电阻选用1%精度金属膜电阻,确保电压测量准确
- 每节电池独立配置DS18B20,实现精准温度监控
2. 硬件电路设计详解
2.1 系统架构设计
整个BMS系统的硬件架构可以分为以下几个部分:
- 电源管理模块:负责为系统提供稳定的3.3V工作电压
- 信号采集模块:包括电压、电流、温度采集电路
- 核心控制模块:STM32F103主控芯片
- 通信接口模块:串口通信电路
各模块之间的信号流向如下:
锂电池组 → 分压电路 → ADS1115 → STM32(I2C)
锂电池回路 → ACS712 → STM32(ADC)
DS18B20 → STM32(GPIO)
STM32 → CH340 → 上位机
2.2 关键电路实现
2.2.1 锂电池电压采集电路
4串锂电池满电时总电压约16.8V(4.2V/节),远超ADS1115的5V量程,必须设计分压电路。我采用100kΩ+10kΩ电阻分压方案,分压比为11:1。
具体连接方式:
- 每节电池正极通过100kΩ电阻接ADS1115的AIN0-AIN3
- 每节电池负极通过10kΩ电阻接地
- ADS1115的VDD接3.3V,GND接地
- I2C接口:SDA→PB7,SCL→PB6
注意事项:分压电阻的精度直接影响电压测量准确性,务必选用1%精度的金属膜电阻。实际布线时,分压电阻应尽量靠近ADS1115,减少干扰。
2.2.2 电流采集电路
ACS712是基于霍尔效应的电流传感器,具有隔离测量、线性度好的特点。其输出电压与电流成正比关系:
- 0A对应2.5V输出
- +20A对应4.5V输出
- -20A对应0.5V输出
电路连接:
- VCC接5V,GND接地
- OUT引脚接STM32的PA0(ADC1_IN0)
- 串联在电池放电回路中
2.2.3 温度采集电路
DS18B20是单总线数字温度传感器,每个传感器有唯一64位序列号,支持多设备并联。电路设计要点:
- VDD接3.3V,GND接地
- DQ引脚接PA1,并联4.7kΩ上拉电阻
- 每个DS18B20应紧贴电池表面安装
2.2.4 电源电路设计
电源电路需要为各模块提供稳定工作电压:
- 锂电池组电压通过AMS1117-3.3V降压为3.3V
- 为STM32、ADS1115、DS18B20供电
- CH340模块由USB口单独供电,避免共地干扰
3. 软件开发环境搭建
3.1 开发工具准备
开发这个BMS系统需要以下软件工具:
- Keil MDK-ARM V5.28:ARM Cortex-M系列开发环境
- STM32F10x标准外设库V3.5.0:提供硬件抽象层
- ST-Link Utility:程序烧录工具
- 串口调试助手(如SSCOM):数据监控
3.2 详细配置步骤
-
安装Keil MDK
- 下载并安装Keil MDK-ARM V5.28
- 安装STM32F1系列设备支持包
- 注册激活(社区版有32KB代码限制)
-
配置工程环境
bash复制# 工程目录结构 BMS_Project/ ├── Core/ │ ├── Inc/ # 头文件目录 │ └── Src/ # 源文件目录 └── Project/ └── BMS_Project.uvprojx # Keil工程文件 -
工程设置要点
- 芯片型号选择STM32F103C8T6
- 添加STM32标准外设库文件
- 配置编译选项:优化等级-O0(方便调试)
- 设置堆栈大小:Stack_Size=0x200,Heap_Size=0x100
-
烧录器配置
- 安装ST-Link V2驱动
- 连接SWD接口:SWDIO→PA13,SWCLK→PA14
- 在Keil中配置Debug选项为ST-Link Debugger
4. 核心代码实现解析
4.1 工程文件结构设计
为了代码可维护性,我采用模块化设计,主要分为以下几个部分:
code复制BMS_Project/
├── Core/
│ ├── Inc/
│ │ ├── ads1115.h # ADS1115驱动
│ │ ├── bms.h # BMS核心逻辑
│ │ ├── ds18b20.h # 温度传感器
│ │ ├── i2c.h # I2C驱动
│ │ └── usart.h # 串口驱动
│ └── Src/
│ ├── ads1115.c
│ ├── bms.c
│ ├── ds18b20.c
│ ├── i2c.c
│ ├── main.c
│ └── usart.c
└── Project/
└── BMS_Project.uvprojx
4.2 关键驱动实现
4.2.1 ADS1115驱动代码
ADS1115是16位精度的ADC,通过I2C接口通信。关键配置参数:
c复制#define ADS1115_CONFIG_PGA_4_096V 0x0200 // ±4.096V量程
#define ADS1115_CONFIG_DR_128SPS 0x0080 // 128SPS采样率
uint16_t ADS1115_ReadVoltage(uint8_t channel) {
uint16_t config = ADS1115_CONFIG_OS_SINGLE;
switch(channel) {
case 0: config |= ADS1115_CONFIG_MUX_AIN0; break;
// 其他通道配置...
}
config |= ADS1115_CONFIG_PGA_4_096V | ADS1115_CONFIG_MODE_SINGLE
| ADS1115_CONFIG_DR_128SPS;
// 发送配置并读取结果
I2C1_SendData(ADS1115_ADDR, ADS1115_REG_CONFIG, config>>8);
I2C1_SendData(ADS1115_ADDR, ADS1115_REG_CONFIG+1, config&0xFF);
delay_ms(10); // 等待转换完成
uint16_t raw = I2C1_ReadData(ADS1115_ADDR, ADS1115_REG_CONVERSION);
return (uint16_t)(raw * 0.125 * 11); // 转换为实际电压(mV)
}
4.2.2 DS18B20驱动要点
DS18B20采用单总线协议,时序要求严格。关键函数实现:
c复制float DS18B20_ReadTemperature(void) {
if(DS18B20_Reset()) return -999; // 复位失败
DS18B20_WriteByte(0xCC); // 跳过ROM
DS18B20_WriteByte(0x44); // 开始转换
delay_ms(750); // 等待转换完成
DS18B20_Reset();
DS18B20_WriteByte(0xCC);
DS18B20_WriteByte(0xBE); // 读取暂存器
uint8_t temp_l = DS18B20_ReadByte();
uint8_t temp_h = DS18B20_ReadByte();
uint16_t temp_raw = (temp_h << 8) | temp_l;
return temp_raw / 16.0; // 转换为摄氏度
}
4.3 BMS核心算法实现
4.3.1 安时积分法SOC计算
安时积分法是SOC估算的基础,通过累计充放电电量计算SOC变化:
c复制void BMS_CalcSOC_CoulombCounting(void) {
float delta_h = (millis() - last_time) / 3600000.0;
float delta_mAh = current * delta_h * 1000;
soc -= delta_mAh / BATTERY_CAPACITY * 100;
soc = constrain(soc, 0, 100); // 限制在0-100%范围
last_time = millis();
}
4.3.2 开路电压法修正
单纯安时积分会产生累积误差,需要开路电压(OCV)定期修正:
c复制void BMS_CalcSOC_OCV_Correction(void) {
float avg_voltage = 0;
for(int i=0; i<4; i++) {
avg_voltage += cell_voltage[i];
}
avg_voltage /= 4;
// 线性OCV-SOC映射
float ocv_soc = map(avg_voltage, 2500, 4200, 0, 100);
// 加权融合
soc = soc * 0.8 + ocv_soc * 0.2;
}
4.4 主程序逻辑
主程序采用轮询方式采集数据并计算SOC:
c复制int main(void) {
BMS_Init(); // 初始化所有外设
while(1) {
BMS_ReadAllData(); // 采集电压、电流、温度
BMS_PrintData(); // 串口输出数据
delay_ms(1000); // 1秒采样周期
}
}
5. 系统调试与优化
5.1 硬件调试要点
-
电压测量校准
- 使用标准电源给分压电路输入已知电压
- 记录ADS1115读数,计算校准系数
- 在代码中添加校准偏移量
-
电流传感器调试
- 空载时ACS712输出应为2.5V(0A)
- 施加已知负载,验证电流读数准确性
- 必要时添加温度补偿算法
-
温度传感器验证
- 对比DS18B20读数与标准温度计
- 检查传感器与电池的热接触是否良好
5.2 软件调试技巧
-
串口调试输出
c复制printf("Cell1: %dmV, Temp: %.1fC, Current: %.2fA, SOC: %.1f%%\r\n", cell_voltage[0], temperature[0], current, soc); -
常见问题排查
- ADS1115无响应:检查I2C地址和接线
- DS18B20读数异常:检查上拉电阻和时序
- SOC跳变过大:调整安时积分和OCV的权重
-
参数优化建议
- 根据电池特性调整OCV-SOC映射曲线
- 增加电流测量滤波算法
- 实现充放电效率补偿
6. 实际应用建议
经过实际测试,这个BMS系统可以达到以下性能指标:
- 电压测量精度:±20mV
- 电流测量精度:±0.1A(10A范围内)
- 温度测量精度:±0.5℃
- SOC估算误差:<5%(经过OCV修正后)
对于需要更高精度的应用,可以考虑以下改进:
- 使用更高精度的电流传感器(如INA226)
- 增加电池均衡电路
- 实现更复杂的SOC算法(如卡尔曼滤波)
- 添加安全保护功能(过压、欠压、过流保护)
这个项目的完整代码已经在实际产品中得到验证,读者可以根据自己的需求调整参数和功能。特别是在电池参数方面,需要根据实际使用的电芯特性进行调整,才能获得最佳的SOC估算效果。
