
简介本资源是一套基于STM32F103与BQ76920芯片的锂电池BMS电池管理系统完整工程实现面向高校电子信息、自动化、通信工程等专业师生及嵌入式初学者解决多节串联锂电组实时监测、多重保护与SOC估算等核心工程问题。压缩包共含若干文件具体数量未提供主体为Keil MDK工程源码.c/.h、配置头文件、硬件原理图说明及验证文档总大小26.76MB结构清晰模块划分明确便于理解底层驱动、I²C通信协议、保护逻辑判断与均衡控制流程。已有57人学习下载适用于课程设计、毕业设计或科研原型开发。使用者可直接编译烧录运行获得已实测稳定的电压/电流/温度采集、过压欠压过流过热保护触发、SOC估算及被动均衡功能并支持灵活修改阈值参数、扩展UART/RS485通信或优化卡尔曼滤波算法是深入掌握嵌入式BMS开发的高价值工程实例。1. 项目概述为什么选择STM32F103与BQ76920这对“黄金搭档”在嵌入式开发领域尤其是涉及电池管理系统BMS这类对实时性、可靠性和成本都极为敏感的项目时芯片选型往往是决定项目成败的第一步。我这次分享的“基于STM32F103与BQ76920的锂电池BMS系统”就是经过多次迭代和实际项目验证后筛选出的一个非常经典且实用的组合方案。这个方案特别适合中小功率的储能、电动工具、机器人或备用电源等应用场景。简单来说这个BMS的核心任务就是当好锂电池组的“智能管家”。它需要实时监控每一节电池的电压、温度精确计算剩余电量SOC均衡各节电池的电压并在过压、欠压、过流、过温等危险情况发生时果断切断回路以保护电池和整个系统的安全。STM32F103作为主控MCU扮演了“大脑”的角色负责复杂的逻辑判断、算法运行和通信调度而BQ76920作为专用的电池监控前端AFE则是敏锐的“感官系统”专门负责高精度、高可靠性地采集电池的模拟信号。选择它们是因为STM32F103拥有丰富的片上资源如多个定时器、ADC、通信接口和成熟的生态能轻松应对BMS的复杂控制逻辑而BQ76920则把最棘手的多节电池电压采集、硬件保护等任务用硬件完美解决大大降低了软件设计的复杂度和风险。接下来我就把这个从硬件设计到C语言软件实现的完整过程包括踩过的坑和总结的经验毫无保留地分享给大家。2. 系统整体架构与核心芯片选型解析一个完整的BMS系统远不止是两块芯片的简单连接。在设计之初就必须对系统整体架构有一个清晰的规划。我们的目标是设计一个支持3-5节串联锂电池常见于12V或18V系统的主动均衡BMS具备完整的监控、保护、通信和状态指示功能。2.1 硬件架构框图与信号流整个系统的硬件核心围绕STM32F103C8T6以下简称F103和BQ7692003支持3-5节下文统称BQ76920搭建。其信号流可以这样理解采集层电池组假设为4节串联的正极BAT连接至BQ76920的VC5引脚负极BAT-连接至VC0引脚。电池之间的连接点VC1-VC4则分别连接到BQ76920对应的VC引脚。BQ76920内部的多路复用器和ADC会自动巡回检测每两节电池之间的电压即单节电池电压。同时通过NTC热敏电阻分压电路将电池温度信号送入BQ76920的TS1/TS2引脚。保护与驱动层BQ76920根据配置的阈值实时进行硬件保护判断。一旦触发它会通过ALERT引脚向F103发出中断信号并可以自行控制其内置的放电DSG和充电CHGMOSFET驱动引脚来关断外部的MOSFET阵列实现硬件保护。这部分是系统的安全底线。控制与通信层F103通过I2C接口与BQ76920通信读取所有采集到的原始数据电压、温度、状态标志。F103的软件负责进行SOC估算、逻辑保护判断作为硬件的冗余、主动均衡控制并通过UART接口向上位机或显示屏发送数据或通过CAN接口与整车控制器等其他设备通信。此外F103的GPIO还用于控制均衡MOSFET、驱动状态LED、蜂鸣器等。电源与隔离整个BMS的供电来自电池组本身。需要一个宽输入范围的DC-DC降压芯片如LM2596为F103和周边电路提供稳定的3.3V或5V电源。这里有一个关键点由于BQ76920的通信接口I2C的地GND是连接在电池组负端BAT-的而F103的逻辑地通常是电源地。如果两者直接连接可能会形成地环路或共模干扰严重时可能损坏接口芯片。因此在I2C通信线上SDA SCL使用双向电平转换隔离芯片如TI的ISO1540或光耦隔离方案是强烈建议的尤其是在多节串联或大电流应用中。2.2 为什么是STM32F103C8T6市面上MCU那么多为何独爱F103这源于BMS的几大需求实时性需求BMS需要定时例如每秒读取电池数据、执行均衡算法。F103的72MHz主频和丰富的中断资源足以保证任务的实时性。外设需求多个ADC除了BQ76920系统可能还需要用MCU的ADC来检测总电压、总电流通过采样电阻和运放作为冗余或校准参考。多个定时器需要PWM输出控制主动均衡的电流大小需要基础定时器来产生系统心跳SysTick和软件定时如果需要高精度电流积分可能用到定时器输入捕获。通信接口至少需要一个I2C与BQ76920通信一个UART用于调试和通信一个CAN用于车规级应用。F103的USART和CAN外设非常成熟。开发成本与生态F103是STM32系列中最为经典的型号资料、例程、社区支持极其丰富。无论是用标准库虽然已停止维护但存量项目多还是HAL库都能快速上手。其Flash64KB和RAM20KB容量对于中等复杂度的BMS软件也绰绰有余。成本在满足性能的前提下F103具有极高的性价比。2.3 为什么是BQ76920电池监控前端芯片也有很多选择如ADI的LTC系列、TI的BQ系列。BQ76920在中小规模串联应用中优势明显集成度与安全性它集成了高精度ADC、电压基准、电平转换、MOSFET驱动以及可配置的硬件保护功能。硬件保护是独立于MCU运行的即使MCU程序跑飞在过压、欠压等情况下BQ76920依然能依靠自身硬件切断回路这是安全设计的核心。精度其电压测量精度典型值在±5mV以内足以满足绝大多数锂电池管理需求。菊花链能力虽然BQ76920本身只支持最多5节但TI的该系列芯片支持通过SPI或I2C进行菊花链连接方便扩展至更多节数为未来升级留有余地。开发支持TI提供了详细的数据手册、应用笔记以及评估板软件大大降低了硬件设计门槛。注意BQ76920的I2C地址是固定的0x087位地址。在设计PCB时其VC0-VC5引脚连接电池的走线需要特别注意等长和对称以减少测量误差并且要远离大电流路径以防止干扰。3. 硬件设计关键细节与避坑指南画原理图和PCB是硬件工程师的活儿但搞嵌入式软件的如果不懂这些关键点调试时会非常痛苦。这里我分享几个最容易出问题的地方。3.1 电源电路设计BMS的电源必须极其可靠因为它本身由被管理的电池供电。输入范围假设电池组是4节三元锂电满电电压16.8V欠压保护点12V。那么DC-DC降压芯片的输入电压范围必须覆盖12V-17V并留有一定余量如10V-20V。LM2596-ADJ可调版是经典选择。上电时序与浪涌电池接通的瞬间可能有浪涌电流。需要在电源输入端并联一个大容量电解电容如100uF/25V和若干小容量陶瓷电容如100nF进行缓冲。同时在DC-DC输入端串联一个缓启动电路或使用带使能引脚的芯片由F103控制上电可以避免热插拔带来的冲击。模拟部分供电如果系统使用了独立的运放来放大电流采样信号那么给运放供电的电源最好是从DC-DC输出后再经过一个LDO如AMS1117-3.3得到以获得更干净的模拟电源提高电流采样精度。3.2 采样电路设计电压、电流与温度电压采样这部分主要由BQ76920完成我们只需按数据手册要求在VC引脚到电池连接点之间串联一个合适的电阻如100Ω并加上滤波电容如100nF。关键点在于PCB布局这些RC滤波电路应尽可能靠近BQ76920的引脚且走线要细而短避免引入噪声。电流采样采样电阻通常采用毫欧级如1mΩ或2mΩ的贴片分流电阻。其功率一定要计算充足。例如系统最大持续电流50A采样电阻2mΩ则电阻功耗为 P I²R 50² * 0.002 5W必须选择足够大封装的电阻如2512甚至更大并考虑散热。运放电路一般采用差分放大电路将采样电阻两端的微小压差放大到MCU ADC可测量的范围如0-3.3V。运放要选择低失调电压、低温漂的型号如MCP6002。放大倍数G Vout / (I * Rshunt)。设计时需保证在最大电流时Vout不超过ADC量程3.3V并留有一定余量。双向电流检测为了区分充电和放电电流通常将运放参考电平设置在1.65V半量程。这样零电流时输出1.65V充电时高于1.65V放电时低于1.65V。F103的ADC需要配置为单端模式测量这个电压。温度采样使用NTC热敏电阻如10kΩ B值3435与一个固定精密电阻如10kΩ分压分压点连接至BQ76920的TS引脚。BQ76920内部会用一个电流源来测量这个电压从而换算出温度。注意热敏电阻需要与电池表面保持良好的热接触通常用导热胶固定在电池上。建议在同一个电池模组上布置至少两个温度探头监测最高和最低温度点。3.3 均衡电路设计被动与主动均衡是为了消除电池组内各单体电池的容量差异是延长电池组寿命的关键。被动均衡耗散式最简单的方式在每节电池两端并联一个电阻和MOSFET。当某节电池电压过高时MCU控制对应的MOSFET导通通过电阻放电。优点是电路简单成本低。缺点是能量以热的形式耗散均衡电流小通常100mA效率低。适用于小容量或对均衡速度要求不高的场景。主动均衡非耗散式我们这里设计一个基于电感/变压器的主动均衡电路。其原理类似于一个Buck-Boost变换器可以将电量从高的电池转移到低的电池或者转移到整组。效率高均衡电流大可达1A以上。但电路复杂成本高控制算法也复杂。实现方案使用F103的一个定时器产生PWM信号控制一个MOSFET开关。通过电感、二极管和MOSFET构成一个开关电路。软件需要实现电压检测、目标电池选择、PWM占空比控制控制均衡电流以及防止环流的逻辑。这是软件设计的难点之一。实操心得在第一次设计时我为了省事将均衡MOSFET的驱动直接连到F103的GPIO。结果发现当多节电池同时开启均衡时GPIO电流骤增导致MCU电压被拉低甚至复位。后来改为通过专用的MOSFET驱动芯片如TC4427来驱动问题迎刃而解。硬件上省的小钱会在调试时加倍奉还。4. 软件架构设计与C语言实现要点硬件是骨架软件才是灵魂。一个稳健的BMS软件架构应该是模块化、实时性强且易于维护的。4.1 软件整体框架与任务调度我们不使用复杂的RTOS而是采用一个基于SysTick中断的时间片轮询架构足够应对BMS的需求。这样结构清晰可靠性高。// 系统主循环示例 int main(void) { // 1. 硬件初始化 HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_I2C1_Init(); MX_ADC1_Init(); MX_TIM2_Init(); // PWM定时器用于均衡 MX_USART1_UART_Init(); // ... 其他外设初始化 // 2. BMS专用模块初始化 BQ76920_Init(); // 配置AFE参数 BMS_State_Init(); // 初始化BMS状态结构体 Current_Sensor_Calibrate(); // 电流传感器零点校准 // 3. 启动定时器中断 HAL_TIM_Base_Start_IT(htim2); // 用于PWM生成 // SysTick中断已在HAL_Init中配置每1ms触发一次 while (1) { // 主循环不进行耗时操作只处理标志位和低优先级任务 if (flag_1ms) { flag_1ms 0; Task_1ms(); // 非常快速的任务如IO状态扫描 } if (flag_10ms) { flag_10ms 0; Task_10ms(); // 关键任务保护逻辑判断、通信处理 } if (flag_100ms) { flag_100ms 0; Task_100ms(); // 主要任务读取电压/温度、计算SOC、执行均衡 } if (flag_1000ms) { flag_1000ms 0; Task_1000ms(); // 慢速任务数据记录、状态上报 } // 处理串口接收到的命令 UART_Command_Handler(); } } // 在SysTick中断服务函数中设置标志位 void SysTick_Handler(void) { HAL_IncTick(); static uint16_t tick_count 0; tick_count; if (tick_count % 1 0) flag_1ms 1; if (tick_count % 10 0) flag_10ms 1; if (tick_count % 100 0) flag_100ms 1; if (tick_count % 1000 0) { flag_1000ms 1; tick_count 0; } }4.2 BQ76920驱动层实现I2C通信与BQ76920的通信是整个数据采集的基础必须稳定可靠。我们使用STM32的HAL库I2C函数但需要增加错误处理和重试机制。#define BQ76920_I2C_ADDR (0x08 1) // HAL库使用7位地址左移1位 typedef enum { BQ76920_OK 0, BQ76920_ERROR_I2C, BQ76920_ERROR_CRC, // 如果启用CRC校验 } BQ76920_Status_t; // 读取多个寄存器 BQ76920_Status_t BQ76920_ReadRegisters(uint8_t reg_addr, uint8_t *data, uint8_t len) { HAL_StatusTypeDef hal_status; int retry 3; while (retry--) { hal_status HAL_I2C_Mem_Read(hi2c1, BQ76920_I2C_ADDR, reg_addr, I2C_MEMADD_SIZE_8BIT, data, len, 100); if (hal_status HAL_OK) { // 可选在这里进行CRC校验 return BQ76920_OK; } // 如果失败稍作延迟后重试 HAL_Delay(1); } // 重试多次后仍然失败 BMS_Error_Handler(BMS_ERROR_AFE_COMM); // 记录错误 return BQ76920_ERROR_I2C; } // 写入单个寄存器 BQ76920_Status_t BQ76920_WriteRegister(uint8_t reg_addr, uint8_t value) { // ... 类似读取使用HAL_I2C_Mem_Write } // 初始化BQ76920配置保护阈值、警报等 void BQ76920_Init(void) { uint8_t cfg[5]; // 1. 解除子电路模式如果之前处于该模式 BQ76920_WriteRegister(0x00, 0x19); HAL_Delay(10); // 2. 配置保护阈值例如 // 过压保护(OVP): 4.25V, 欠压保护(UVP): 2.80V // 这些值需要根据具体电池类型三元锂/磷酸铁锂计算并写入对应寄存器 cfg[0] 0x15; // OVP阈值高字节 (示例值需计算) cfg[1] 0x00; // OVP阈值低字节 cfg[2] 0x0E; // UVP阈值高字节 (示例值) cfg[3] 0x00; // UVP阈值低字节 BQ76920_WriteRegisters(0x14, cfg, 4); // 从OVP_THRESH寄存器开始写 // 3. 配置警报和中断 cfg[0] 0x03; // 使能OV和UV警报 BQ76920_WriteRegister(0x27, cfg[0]); // 更多配置... }注意事项BQ76920的许多配置寄存器是易失性的断电即丢失。因此每次上电初始化时都必须重新配置。最好将关键的配置参数保存在F103的Flash中上电后读取并写入BQ76920。4.3 核心算法实现SOC估算与主动均衡控制这是BMS软件的“大脑”。1. 安时积分法Coulomb Counting实现SOC估算这是最常用也最基本的方法。原理很简单SOC 初始SOC (∫I dt) / 电池总容量。typedef struct { float total_capacity_Ah; // 电池总容量单位Ah float current_soc; // 当前SOC 0.0 ~ 1.0 float current_A; // 当前电流充电为正放电为负 float coulomb_count_mAh; // 安时积分累计值单位mAh uint32_t last_update_tick; // 上次更新时间戳 } SOC_Estimator_t; void SOC_Update(SOC_Estimator_t *estimator) { uint32_t current_tick HAL_GetTick(); float delta_time_h (current_tick - estimator-last_update_tick) / 3600000.0f; // 转换为小时 if (delta_time_h 0) { // 安时积分ΔQ I * Δt float delta_capacity_mAh estimator-current_A * 1000.0f * delta_time_h; estimator-coulomb_count_mAh delta_capacity_mAh; // 计算SOC estimator-current_soc estimator-current_soc (delta_capacity_mAh / (estimator-total_capacity_Ah * 1000.0f)); // 边界保护 if (estimator-current_soc 1.0f) estimator-current_soc 1.0f; if (estimator-current_soc 0.0f) estimator-current_soc 0.0f; estimator-last_update_tick current_tick; } }但安时积分法有致命缺点依赖电流测量精度和初始SOC且会随时间累积误差。因此必须结合电压修正和开路电压法OCV。电压修正在电池静置或小电流时根据当前电压查表电压-SOC对应表需通过电池实验获得来修正SOC。开路电压法在系统长时间静置后如每次上电时如果电池处于“松弛”状态其开路电压与SOC有较确定的对应关系可以用来校准初始SOC。2. 主动均衡控制算法主动均衡的策略是“削峰填谷”。软件需要周期性地如在Task_100ms中执行以下逻辑void Active_Balance_Control(void) { float cell_voltages[CELL_NUM]; float max_v 0.0f, min_v 5.0f; uint8_t max_idx 0, min_idx 0; // 1. 读取所有单体电压 BQ76920_ReadCellVoltages(cell_voltages); // 2. 找出电压最高和最低的单体 for (int i 0; i CELL_NUM; i) { if (cell_voltages[i] max_v) { max_v cell_voltages[i]; max_idx i; } if (cell_voltages[i] min_v) { min_v cell_voltages[i]; min_idx i; } } // 3. 判断是否需要均衡最高与最低电压差大于阈值如20mV float diff_v max_v - min_v; if (diff_v BALANCE_START_THRESHOLD) { // 4. 安全条件检查电池温度正常、不在充电/放电保护状态等 if (BMS_IsSafeForBalance()) { // 5. 启动均衡从max_idx向min_idx转移能量 // 这里需要控制PWM占空比并开启对应的MOSFET开关 Balance_Start(max_idx, min_idx); // 可以设置一个最大均衡时间或电流积分量防止过均衡 } else { Balance_Stop(); } } else if (diff_v BALANCE_STOP_THRESHOLD) { // 电压差小于停止阈值关闭均衡 Balance_Stop(); } // 如果电压差在区间内维持原有状态 }均衡控制函数Balance_Start需要根据硬件电路精确控制哪两个MOSFET导通并设置PWM的占空比来限制均衡电流的大小。5. 系统调试、问题排查与实测心得理论设计得再完美也要经过调试的洗礼。下面是我在调试这个BMS系统时遇到的一些典型问题及解决方法。5.1 通信不稳定与数据异常现象I2C读取BQ76920数据时偶尔失败或读取到的电压值跳变很大。排查检查硬件首先用示波器看I2C的SDA和SCL波形。是否出现毛刺上升沿/下降沿是否陡峭上拉电阻是否合适通常4.7kΩ-10kΩ如果使用了电平转换芯片其方向控制是否正确检查电源和地测量BQ76920的VCC引脚和F103的VDD电压是否稳定。两地之间的压差是否在电平转换芯片的允许范围内确保电源去耦电容0.1uF紧靠芯片引脚。检查软件是否在I2C通信过程中被高优先级中断打断增加重试机制和超时判断。适当降低I2C时钟频率如从400kHz降到100kHz看是否改善。解决我的案例中问题根源是地线噪声。大电流充放电时电池负端到BQ76920 GND的路径上存在微小压降这个波动被耦合到了I2C信号里。通过在BQ76920的GND引脚附近增加一个到大电容地的短而粗的走线并确保I2C信号线远离功率回路问题得到显著改善。5.2 SOC估算不准漂移严重现象电池充满电后SOC显示100%但用一会儿就掉到80%或者放电末期SOC还有20%却突然没电了。排查与解决电流采样校准这是安时积分误差的主要来源。在系统无电流时静止状态读取电流ADC值这个值就是“零点偏移”。在后续所有电流读数中减去这个偏移。需要定期如每次上电或长时间静置时自动校准零点。电池容量标定total_capacity_Ah这个参数不能简单看电芯规格书必须通过完整的充放电循环来实际测量。进行一次从满电保护板切断到欠压保护保护板切断的完整放电用精确的安时积分得到的总电量才是电池组在当前状态下的真实可用容量。引入电压修正在代码中实现一个“电压-SOC”查找表。当电池电流小于某个阈值如0.05C并持续一段时间后认为电压趋于稳定此时用查表法得到的SOC对安时积分的结果进行加权融合例如SOC_new 0.95 * SOC_cc 0.05 * SOC_ocv逐步修正误差。考虑老化因子电池容量会随着循环次数增加而衰减。可以设计一个简单的学习算法每次完整的充放电循环后根据实际放出的电量微调total_capacity_Ah。5.3 主动均衡效率低或发热严重现象开启主动均衡后电感或MOSFET发热很快均衡效果却不明显。排查PWM频率与电感选型主动均衡电路本质上是一个开关电源。PWM频率、电感值和负载电池内阻需要匹配。频率太低如1kHz电感体积大纹波大频率太高如500kHz开关损耗大。通常选择50kHz-200kHz。电感需要计算其额定电流和饱和电流确保在均衡电流下不会饱和。MOSFET选型与驱动要选择低导通电阻Rds(on)的MOSFET并确保驱动电压足够通常需要10V以上栅极电压才能完全导通。使用专用的MOSFET驱动芯片如TC4427可以提供快速的充放电能力减少开关损耗。死区时间如果电路是H桥或类似结构必须设置死区时间防止上下管同时导通造成短路。这个可以在F103的定时器高级控制寄存器中配置。均衡策略不要一味地追求大均衡电流。对于容量不大的电池组200-500mA的均衡电流已经足够。过大的电流会导致效率急剧下降热量都耗散在电路上了。软件上可以加入温度监控当电感或MOSFET温度过高时降低PWM占空比或暂停均衡。5.4 保护功能误触发或不触发现象电池电压明明正常却报过压保护或者已经过放了保护板还没切断。排查硬件保护阈值检查仔细核对写入BQ76920寄存器的保护阈值数据是否正确。注意寄存器的格式通常是毫伏为单位的数值。确保在写入后重新读取寄存器验证。软件保护逻辑冗余硬件保护是最后防线软件应实现一套并行的保护逻辑。在Task_10ms中软件根据读取的电压、电流、温度值进行判断。软件保护可以设置更保守的阈值和更复杂的条件如延时判断并可以通过控制额外的继电器或MOSFET作为保护执行机构。软硬件双重保护能极大提高可靠性。信号滤波电压、电流采样信号可能存在噪声导致瞬时值超标触发保护。在软件中实现一个数字滤波器如滑动平均滤波或一阶低通滤波。#define FILTER_ALPHA 0.1f // 滤波系数越小越平滑响应越慢 float filtered_voltage 0.0f; float raw_voltage read_voltage(); filtered_voltage filtered_voltage * (1 - FILTER_ALPHA) raw_voltage * FILTER_ALPHA; // 使用filtered_voltage进行保护判断延时判断真正的故障通常是持续的。实现一个“故障计数器”只有当信号连续多次如5次超过阈值才判定为故障并执行保护动作。这能有效避免毛刺引起的误动作。经过这些细致的调试和优化最终的系统能够稳定可靠地工作。实测下来电压采集误差可以控制在±5mV以内SOC估算在电池整个放电过程中误差能保持在3%以内主动均衡能将4节电池的电压差在1小时内从100mV降低到10mV以内保护功能响应迅速且准确。这个基于STM32F103和BQ76920的BMS方案以其出色的性价比和可靠性已经成功应用于多个小型储能和动力设备项目中。本文还有配套的精品资源点击获取