基于STM32F103与BQ76920的3-5串锂电池BMS开源方案详解
2026/9/2 6:01:09 网站建设 项目流程

简介:这是一套面向嵌入式开发初学者与电子信息类专业学生的锂电池管理系统实战项目,基于STM32F103主控与BQ76920专用电池监控芯片,完整实现电压/温度采集、均衡控制、SOC估算及保护逻辑等核心功能,适用于课程设计、毕业设计、竞赛原型开发及工程入门实践。资源包共1003个文件,涵盖576个C源码(含驱动、算法、应用层逻辑)、265个头文件(模块接口定义清晰)、28个IAR工程配置文件(.icf)及13个PCB光绘文件(.gbr),另含PDF文档说明、Kicad原理图与PCB工程,整体26.69MB,结构规范、模块解耦度高。已有941人学习下载,所有代码均经实机测试运行通过,配套README提供清晰启动指引;内容预览显示包含ARM CMSIS-DSP数学库相关初始化与插值函数,表明系统已集成基础信号处理能力,便于后续扩展滤波或状态估计算法。

1. 项目缘起:为什么选择STM32F103+BQ76920做BMS?

如果你正在为一个DIY的机器人、一个便携式储能设备,或者一个需要多节锂电池供电的项目寻找一个可靠、低成本且开源的电池管理系统(BMS)方案,那么你很可能已经搜索过“STM32F103 BMS”或者“BQ76920 开源”这类关键词了。市面上现成的BMS模块要么功能太简单(比如只有过充过放保护),要么价格昂贵且不开源,想自己定制功能几乎不可能。这正是我当初决定动手设计这个项目的原因。

这个项目基于经典的STM32F103C8T6(也就是大家常说的“蓝色药丸”Blue Pill)作为主控,搭配TI的BQ76920模拟前端(AFE)芯片,实现了一个功能相对完整的3-5串锂电池管理系统。我不仅会分享完整的C语言项目源码和硬件设计思路,更重要的是,我会把开发过程中那些数据手册里没写、网上也搜不到的“坑”和“技巧”都摊开来讲清楚。比如,BQ76920的I2C通信时序到底有多苛刻?STM32的ADC采样锂电池总电压时,分压电阻的精度和温漂该怎么选?软件上如何实现一个既稳定又省电的“库仑计”?

整个系统可以实现单体电压监测、总电压电流监测、温度监测、充放电MOSFET控制、被动均衡以及通过串口上报数据等功能。代码采用模块化设计,在Keil MDK和STM32CubeIDE上都能编译,注释详尽,特别适合有一定STM32和C语言基础,想深入理解BMS底层原理和实战开发的朋友参考甚至直接复用。

2. 核心芯片选型与电路设计要点

一套BMS的硬件核心就两块:负责逻辑控制和算法的主控MCU,以及负责高精度采集电池模拟信号的专用AFE芯片。选型决定了系统的性能天花板和开发难度。

2.1 主控MCU:为什么是STM32F103C8T6?

在众多STM32系列中,F103C8T6至今仍是入门和中小型项目的性价比之王。对于这个BMS项目,它有几个无法拒绝的优势:

  1. 充足的资源:72MHz主频、64KB Flash、20KB RAM,运行一个包含状态机、滤波算法、通信协议的BMS核心逻辑绰绰有余。
  2. 丰富的外设:我们需要至少一个I2C接口与BQ76920通信,一个UART用于调试和数据输出,一个ADC用于采集总电压和温度(BQ76920的ADC通道可能不够用,或者我们想用MCU的ADC做冗余校验),以及多个GPIO控制MOSFET和指示灯。F103C8T6完全满足。
  3. 极低的成本与极高的生态成熟度:芯片本身便宜,相关的开发板(Blue Pill)遍地都是,调试器(ST-Link)也便宜。无论是用标准外设库、HAL库还是直接寄存器开发,资料都多如牛毛,极大降低了学习和调试成本。

注意:F103的ADC基准电压是内部的3.3V,且精度相对一般(12位)。在测量电池总电压(可能高达21V)时,必须使用高精度、低温漂的分压电阻网络,并将分压后的电压严格限制在0-3.3V内,这是硬件设计的第一要务。

2.2 模拟前端(AFE):BQ76920的关键特性解析

BQ76920是TI旗下支持3-5串锂电池的AFE芯片。它不像一些简单的保护芯片,而是一个需要MCU通过I2C配置和读取的“智能传感器”。它的核心能力包括:

  • 高精度电压采集:每个电芯的电压通过内部多路复用器和16位ΔΣ ADC测量,精度很高,省去了外部一堆运放和ADC的麻烦。
  • 集成电流检测放大器:芯片支持外接一个毫欧级的采样电阻(Shunt Resistor),通过内部放大器测量充放电电流,这是实现库仑计(电量统计)的基础。
  • 内置温度传感器接口:可以连接2个外部热敏电阻(NTC),用于监测电池组和环境温度。
  • 硬件保护与均衡:内置可配置的过压(OV)、欠压(UV)、过流(OC)、短路(SC)保护比较器,触发后能直接关闭驱动MOSFET的驱动器。同时,每个电芯都可通过一个内置的开关电阻进行被动均衡(Bleeding)。

电路设计上的几个坑:

  • 采样电阻的选型与布局:电流采样电阻的精度和温漂直接影响电量计的准确性。建议选用至少1W功率、阻值在0.5-2毫欧之间的四线制(开尔文连接)精密采样电阻。PCB布局上,采样电阻的电压检测走线必须直接从电阻焊盘引出,采用差分走线方式回到BQ76920的SRP和SRN引脚,避免大电流路径上的压降干扰。
  • NTC热敏电阻的分压电阻:BQ76920的TS1/TS2引脚通过一个上拉电阻(通常10kΩ)到内部基准电压。外部NTC与另一个固定电阻串联到地。这个固定电阻的阻值必须等于NTC在25°C时的阻值(常见的是10kΩ B值3950)。这样在25°C时,分压点电压正好是基准电压的一半,测量线性度最好。
  • 电源与去耦:BQ76920的模拟部分(VC5x, VSS)供电必须干净。除了数据手册推荐的电容,在靠近芯片的VC5和VSS引脚之间,务必并联一个0.1μF和一个1μF的陶瓷电容。数字部分(VDD, REGOUT)同理。

3. 软件架构与核心模块实现

软件是BMS的“大脑”。一个好的架构能让代码清晰、易于维护和扩展。本项目采用典型的前后台(轮询+中断)架构,模块化程度高。

3.1 硬件抽象层(HAL)与驱动

这一层直接与STM32的硬件和外设打交道,为上层的BMS逻辑提供统一的接口。

  • bsp_i2c.c/.h: 封装STM32的I2C通信函数。这里有一个大坑:BQ76920的I2C时序要求比较特殊,尤其是在写入配置寄存器后,需要等待一定时间(Subcommand execution time)才能进行下一次读写。单纯的HAL_I2C_Mem_Write可能不够可靠。我的做法是,在每次写操作后,加入一个几毫秒的osDelay(如果用了RTOS)或HAL_Delay,并检查CC(化学配置)寄存器的SEC位是否清零,以确认上一条子命令执行完毕。
  • bsp_adc.c/.h: 配置STM32的ADC,用于采集电池组总电压(通过分压电阻)和可能额外的温度传感器。使用DMA循环采集,然后进行软件滤波(如滑动平均)。
  • bsp_uart.c/.h: 实现调试信息打印和数据帧上报。数据帧建议定义固定的结构,例如{帧头, 电压数组, 电流, 温度, 状态, 校验和, 帧尾},方便上位机解析。
  • bsp_gpio.c/.h: 控制充放电MOSFET的使能、状态LED、均衡MOSFET等。

3.2 BQ76920核心驱动与配置

这是与AFE芯片交互的核心,所有对电池参数的读取和对芯片行为的控制都通过这一层。

  • bq76920.c/.h: 这个文件里包含了所有寄存器的地址定义、初始化序列、数据读取和配置函数。
    • 初始化流程:这是最关键的步骤之一,顺序不能错。基本流程是:上电延时 -> 唤醒芯片(通过I2C发送特定序列)-> 配置保护阈值(OV/UV/OC/SC)-> 配置均衡参数 -> 使能ADC转换 -> 使能保护功能。
    // 伪代码示例:BQ76920初始化关键步骤 void BQ76920_Init(void) { // 1. 上电后等待至少1ms HAL_Delay(2); // 2. 发送唤醒命令(向0x08寄存器写入0x0095) BQ76920_WriteSubCmd(0x08, 0x0095); HAL_Delay(5); // 等待唤醒 // 3. 配置保护阈值,例如设置过压保护为4.20V // 寄存器OV_TRIP, 计算方式:阈值(V) / 0.0016 uint16_t ov_trip_value = (uint16_t)(4.20 / 0.0016); BQ76920_WriteRegister(OV_TRIP_REG, ov_trip_value); // 4. 使能电池电压测量和电流测量 BQ76920_WriteRegister(SYS_CTRL1_REG, 0x18); // 使能ADC和电流测量 // 5. 配置均衡控制,例如设置均衡开启电压阈值为4.15V // ... 其他配置 }
    • 数据读取:需要编写函数来读取单体电压(VC1_VC5等寄存器)、堆叠电压(BAT寄存器)、电流(CC寄存器,注意是有符号的16位补码)、温度(TS1,TS2寄存器)、芯片状态(SYS_STAT寄存器)等。读取的原始值需要根据数据手册提供的公式转换为实际的电压、电流和温度值。

3.3 BMS应用逻辑与状态机

这是项目的“业务逻辑”层,它基于驱动层提供的数据,做出决策。

  • bms_core.c/.h: 实现BMS的核心状态机。一个典型的BMS状态机可能包含以下几个状态:

    1. 初始化状态:上电自检,配置硬件。
    2. 休眠状态:低功耗模式,定时唤醒检测电压。
    3. 待机状态:电池静置,监测电压温度,允许充电或放电。
    4. 充电状态:检测到充电器接入,闭合充电MOSFET,监控充电过程,达到条件后转浮充或停止。
    5. 放电状态:负载接入,闭合放电MOSFET,监控放电过程。
    6. 故障状态:任何保护条件触发(过压、欠压、过流、过温、欠温),进入此状态,断开MOSFET,等待故障清除或手动复位。

    状态机的实现可以用一个switch-case结构,也可以用一个函数指针数组,根据当前状态执行相应的处理函数。

  • bms_protection.c/.h: 专门处理各种保护逻辑。它需要周期性检查BQ76920的状态寄存器(SYS_STAT)和读取的实时数据,判断是否触发保护。一旦触发,除了让芯片硬件关断MOSFET,软件上也应记录故障码,并阻止状态机进入充放电状态。

  • bms_balancing.c/.h: 被动均衡逻辑。常见的策略是:当电池组静止(无充放电电流)且最高单体电压与最低单体电压差值超过一定阈值(如30mV)时,对电压最高的电芯开启均衡。均衡需要控制时间和温度,避免电阻过热。注意:BQ76920的均衡电流很小(通常几十毫安),对于容量较大的电池,均衡效果很慢,主要用于消除长期存放导致的微小不一致。

3.4 电量计(库仑计)实现

这是BMS的“高级功能”,也是难点。BQ76920的CC寄存器提供了一个有符号的、累计的电流积分值(单位为mAh或mAh的倍数)。我们可以利用它来实现一个相对简单的电量计。

  1. 电流校准:这是最基础也最重要的一步。你需要一个精确的电子负载和电源,让电池以恒定的电流(如1A)放电,同时记录BQ76920报告的总安时数(CC寄存器值乘以一个系数)和实际放电时间。通过对比,计算出一个校准系数。
  2. 剩余容量(RM)计算:最朴素的方法是RM = FCC - |Accumulated_CC|。其中FCC(Full Charge Capacity)需要在一次完整的充放电循环中学习得到。例如,从满电放到截止电压,统计总的放电安时数,这个值就可以作为当前温度下的FCC。
  3. 状态of charge(SOC)估算SOC = (RM / FCC) * 100%。但实际情况复杂得多,因为FCC会随温度、老化、放电速率变化。一个实用的改进是引入温度补偿和速率补偿表,并根据电池的静置电压来定期修正SOC(称为“开路电压法”修正)。
  4. 实现要点
    • 安时积分必须在MCU端进行,不能只依赖BQ76920的CC寄存器,因为CC寄存器可能溢出或需要补偿偏移。
    • 需要定期(例如每分钟)将CC寄存器的增量累加到自己的软件累加器中,并清零CC寄存器(通过发送CC_CLEAR子命令)。
    • 将累加的电荷量(单位:库仑或mAh)与当前估算的RM进行同步。
    • 关键经验:SOC在电池静置(无电流)一段时间后,结合电池的开路电压(OCV)查表修正,是最有效的提高精度的方法。可以在代码中实现一个“静置计时器”,当静置超过2小时,且电压稳定,就用OCV-SOC对应表来修正当前的SOC值。

4. 通信协议与数据监控

一个“活”的BMS需要能与外界交互。本项目通过UART实现。

4.1 自定义串口数据帧设计

为了可靠地传输数据,我设计了一个简单的帧结构。上位机(比如电脑上的串口助手或者自己写的Qt/Python监控程序)可以根据这个结构解析数据。

字段字节数说明示例值(十六进制)
帧头2固定为0xAA 0x55AA 55
数据长度1后续数据域的长度0x20 (32字节)
命令字1标识数据包类型,如0x01为上报数据0x01
数据域N实际数据,内容由命令字定义...
CRC16校验2从帧头到数据域结束的CRC16校验和...
帧尾2固定为0x0D 0x0A(回车换行)0D 0A

在数据域中,可以打包所有需要的信息。例如,对于0x01命令,数据域可以这样组织:

  • 电池总电压(uint16_t, 单位0.01V)
  • 电池总电流(int16_t, 单位0.01A, 放电为正)
  • 单体电压数组(5个uint16_t, 单位0.001V)
  • 温度1, 温度2(int16_t, 单位0.1°C)
  • 电池SOC(uint8_t, 单位1%)
  • 系统状态字(uint16_t, 每一位代表一个状态或故障标志)

4.2 上位机监控软件(简易版)思路

你完全可以用Python的pyserial库和PyQt5Tkinter快速搭建一个监控界面。核心流程是:

  1. 打开串口,配置正确的波特率(如115200)。
  2. 循环读取数据,寻找帧头0xAA55
  3. 找到帧头后,根据“数据长度”字段读取后续字节。
  4. 计算CRC16校验,如果通过,则解析“数据域”。
  5. 将解析出的电压、电流、温度、SOC等数据实时显示在图形界面上,并可以绘制历史曲线。

这个自制的上位机不仅能用于调试,也是最终产品的一个简易监控终端。

5. 开发调试与实战避坑指南

理论设计是一回事,把板子焊出来,把代码烧进去能跑起来,又是另一回事。下面是我在调试这个项目时遇到的最典型的几个问题及解决方案。

5.1 I2C通信失败:从硬件到软件的排查

现象:STM32无法读取BQ76920的寄存器,HAL_I2C_Mem_Read函数返回超时或错误。

  • 硬件检查
    1. 上拉电阻:I2C总线(SDA, SCL)必须接上拉电阻,通常4.7kΩ到10kΩ。如果板子上没有,一定要在外部加上。
    2. 电源与电平:确保BQ76920的VDD(数字电源)和STM32的VDD都是3.3V,且共地。用示波器测量SDA/SCL线,看波形是否干净,上升沿是否陡峭。
    3. 地址确认:BQ76920的I2C地址由ADDR引脚决定。接地是0x08,接高是0x18。代码里必须写对。
  • 软件检查
    1. 初始化顺序:BQ76920上电后处于休眠模式,必须通过I2C发送特定的唤醒序列(向0x08地址写入0x0095)才能激活。忘记这一步是最常见的错误。
    2. 时序等待:BQ76920执行某些子命令(如CC_CLEAR)或配置后,需要时间。在写操作后直接读,很可能失败。必须在写操作后加入足够的延时(参考数据手册,通常是几毫秒),或者去读CC寄存器的SEC位等待它清零。
    3. STM32 I2C配置:确保时钟频率配置正确(BQ76920支持最高400kHz)。尝试降低速率到100kHz看是否能通。检查是否开启了I2C的时钟拉伸(Clock Stretching)支持。

5.2 电压/电流采样不准:校准的艺术

现象:读取的单体电压与万用表测量值有较大偏差(>10mV),电流测量值与精密电流表不符。

  • 电压校准
    1. 硬件保证:首先确认分压电阻的精度(用1%或0.1%精度的电阻),并确保PCB布局良好,避免噪声干扰模拟信号。
    2. 软件校准:BQ76920的ADC本身有增益和偏移误差。最有效的方法是两点校准法。准备一个可调精密电源,分别对每个电芯通道施加两个已知的精确电压(例如3.000V和4.200V),读取芯片报告的原始ADC值。建立线性方程V_real = k * ADC_raw + b,为每个通道计算出一组kb系数,存储在MCU的Flash中。上电初始化时加载这些系数进行换算。
  • 电流校准
    1. 采样电阻:这是精度的源头。必须使用低温漂、高精度的四线制采样电阻。
    2. 增益误差校准:让电池以一个稳定且精确的电流(如+1.000A放电, -1.000A充电)通过采样电阻,读取BQ76920的电流寄存器值。计算实际电流与读取值的比例系数,作为电流校准系数。
    3. 偏移校准:在电池完全静置(电流应为0A)时,连续读取多次电流值,求平均,这个平均值就是零点偏移(Offset)。在后续所有电流读数中减去这个Offset。

5.3 被动均衡不工作或发热严重

现象:代码开启了均衡,但电池电压似乎没变化,或者均衡MOSFET/电阻异常发热。

  • 检查配置:确认CELLBAL1CELLBAL2寄存器已正确配置,均衡阈值设置合理,并且SYS_CTRL2中的CB_EN位已被置位。
  • 检查条件:BQ76920的均衡有自动条件:电池必须处于放松状态(无大电流),且电压高于设定的阈值。确保你的代码逻辑满足了这些硬件条件。
  • 发热问题:被动均衡的本质是通过电阻放电,能量以热的形式耗散。BQ76920内部均衡MOSFET的导通电阻加上外部均衡电阻(如果有),决定了均衡电流。例如,在4.2V下,如果总电阻是100Ω,均衡电流就是42mA,功耗约176mW。如果PCB散热设计不好,或者均衡时间过长(比如连续均衡几小时),局部温度会很高。解决方案:在软件中实现间歇均衡,例如开启均衡5分钟,关闭1分钟,让热量散掉;同时,确保均衡电阻和走线有足够的铜皮面积散热。

5.4 系统功耗优化

对于便携设备,BMS本身的待机功耗很重要。

  • STM32低功耗模式:在电池静置且无任务时,让STM32进入Stop模式或Sleep模式,可以极大降低功耗。通过RTC或外部中断(比如BQ76920的ALERT引脚,当电压/温度异常时可产生中断)来唤醒MCU。
  • BQ76920的配置:不需要高频率采集时,可以调整SYS_CTRL1中的ADC_ENCC_EN位,关闭部分ADC通道,或者降低采样频率。
  • 外围电路断电:通过MOSFET或负载开关,在休眠时切断给显示屏、通信模块(如蓝牙/Wi-Fi)等非必要外设的供电。

这个项目从芯片选型、电路设计、驱动编写到应用逻辑实现,涵盖了一个完整BMS开发的核心流程。代码和文档我会整理好提供,但更重要的是理解每一步背后的原理和那些容易出错的地方。硬件设计上,多花点时间在电源、采样和信号完整性上;软件上,把状态机、保护逻辑和电量计算法做扎实。希望这份详细的拆解能帮你少走弯路,成功做出自己稳定可靠的锂电池管理系统。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询