
🎬 渡水无言 :个人主页渡水无言
❄专栏传送门 : 《linux专栏》《嵌入式linux驱动开发》《linux系统移植专栏》
❄专栏传送门 : 《freertos专栏》 《STM32 HAL库专栏》《linux裸机开发专栏》
❄专栏传送门 :《产品测评专栏》 《Ai智能体专栏) 《ROS开发专栏》
❄专栏传送门 :《BMS专栏》
⭐️流水不争先,争的是滔滔不绝
📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生
| 省级优秀毕业生获得者 | csdn新星杯TOP1 | 半导纵横专栏博主 | 211在读研究生
在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连
目录
[二、任务主入口 BMS_EnergyTaskEntry](#二、任务主入口 BMS_EnergyTaskEntry)
[三、充放电管理 BMS_EnergyChgDsgManage](#三、充放电管理 BMS_EnergyChgDsgManage)
[五、被动均衡管理 BMS_EnergyBalanceManage](#五、被动均衡管理 BMS_EnergyBalanceManage)
前言
在前几期博客中,我们依次讲解了电池监控任务(数据采集)、电池保护任务(安全防护)、电池分析任务(SOC / 容量计算)。本期博客聚焦整套BMS的能量管理任务(BMS_EnergyTask),这是整机业务执行层的核心任务。
该任务主要负责两大核心业务:充放电回路管理与电芯被动均衡管理,同时结合freertos信号量、单次定时器实现互斥、延时管控,完美适配 BQ76920 芯片的硬件限制。
一、任务整体定位与调用关系
BMS_EnergyTask(能量管理任务)是BMS 的业务执行中枢,基于监控、保护、分析任务输出的数据,执行最终硬件动作。
充放电管理:根据 SOC 电量、系统模式、故障状态,自动开启 / 关闭充放电 MOS 回路;
被动均衡管理:依据电芯电压、压差,自动启动单体被动均衡,严格遵循 BQ76920 硬件约束;
完整函数调用栈
cpp
BMS_EnergyInit // 能量任务、定时器、信号量初始化
├── osThreadNew // 创建能量管理主线程
├── osTimerNew // 创建均衡单次定时器
└── osSemaphoreNew // 创建均衡互斥信号量
BMS_EnergyTaskEntry // 任务主循环
├── BMS_EnergyChgDsgManage // 充放电逻辑管理
│ └── BMS_HalCtrlCharge / BMS_HalCtrlDischarge // 充放电硬件控制
└── BMS_EnergyBalanceManage // 电芯被动均衡管理
└── BMS_HalCtrlCellsBalance
└── BQ769X0_CellBalanceControl // BQ76920均衡寄存器控制
BMS_BalanceTimerEntry // 均衡定时器回调(定时关闭均衡)
二、任务主入口 BMS_EnergyTaskEntry
任务主循环周期 200ms,固定执行顺序:充放电管理 → 均衡管理。
cpp
static void BMS_EnergyTaskEntry(void *pvParameters)
{
(void)pvParameters;
// 备份上电初始充放电状态
BMS_CHGStateBackup = BMS_GlobalParam.Charge;
BMS_DSGStateBackup = BMS_GlobalParam.Discharge;
// 初始化电压防抖计时
BalanceVoltRiseTime = BALANCE_VOLT_RISE_DELAY + xTaskGetTickCount();
// 均衡总开关开启,先抢占信号量,禁止并发充电
if (BMS_GlobalParam.Balance == BMS_STATE_ENABLE)
{
xSemaphoreTake(BalanceSem, portMAX_DELAY);
}
// 周期主循环
for(;;)
{
BMS_EnergyChgDsgManage(); // 充放电逻辑控制
BMS_EnergyBalanceManage(); // 被动均衡逻辑控制
// FreeRTOS 毫秒级延时
vTaskDelay(pdMS_TO_TICKS(ENERGY_TASK_PERIOD));
}
}
三、充放电管理 BMS_EnergyChgDsgManage
根据故障标志、系统模式、SOC、手动开关实现充放电自动启停,故障优先、SOC 联动、信号互斥逻辑完全保留。
cpp
static void BMS_EnergyChgDsgManage(void)
{
// 存在保护告警,直接关闭所有充放电
if (BMS_ProtectAlert == FlAG_ALERT_NO)
{
switch(BMS_GlobalParam.SysMode)
{
// 充电模式:SOC达到上限,自动停充
case BMS_MODE_CHARGE:
{
if (BMS_AnalysisData.SOC >= BMS_EnergyData.SocStopChg)
{
BMS_HalCtrlCharge(BMS_STATE_DISABLE);
BMS_INFO("Stop Charge\r\n");
}
}break;
// 放电模式:SOC低于下限,自动停放防过放
case BMS_MODE_DISCHARGE:
{
if (BMS_AnalysisData.SOC <= BMS_EnergyData.SocStopDsg)
{
BMS_HalCtrlDischarge(BMS_STATE_DISABLE);
BMS_INFO("Stop Discharge\r\n");
}
}break;
// 待机模式:根据SOC + 信号量互斥启停充放电
case BMS_MODE_STANDBY:
{
// 充电使能 + SOC不足 + 无均衡 → 开启充电
if (BMS_GlobalParam.Charge == BMS_STATE_ENABLE)
{
if (BMS_AnalysisData.SOC < BMS_EnergyData.SocStartChg)
{
// 非阻塞获取信号量:均衡占用则不启动充电
if (xSemaphoreTake(BalanceSem, 0) == pdPASS)
{
BMS_HalCtrlCharge(BMS_STATE_ENABLE);
BMS_INFO("Start Charge\r\n");
xSemaphoreGive(BalanceSem);
}
}
}
// 放电使能 + SOC充足 → 开启放电
if (BMS_GlobalParam.Discharge == BMS_STATE_ENABLE)
{
if (BMS_AnalysisData.SOC > BMS_EnergyData.SocStartDsg)
{
BMS_HalCtrlDischarge(BMS_STATE_ENABLE);
BMS_INFO("Start Discharge\r\n");
}
}
}break;
default: break;
}
}
// 检测充放电手动开关状态变化
if (BMS_CHGStateBackup != BMS_GlobalParam.Charge)
{
if (BMS_GlobalParam.Charge == BMS_STATE_DISABLE)
{
BMS_HalCtrlCharge(BMS_STATE_DISABLE);
}
// 睡眠模式允许直接开启充电
else if (BMS_GlobalParam.SysMode == BMS_MODE_SLEEP)
{
BMS_HalCtrlCharge(BMS_STATE_ENABLE);
}
BMS_CHGStateBackup = BMS_GlobalParam.Charge;
}
if (BMS_DSGStateBackup != BMS_GlobalParam.Discharge)
{
if (BMS_GlobalParam.Discharge == BMS_STATE_DISABLE)
{
BMS_HalCtrlDischarge(BMS_STATE_DISABLE);
}
// 睡眠模式允许直接开启放电
else if (BMS_GlobalParam.SysMode == BMS_MODE_SLEEP)
{
BMS_HalCtrlDischarge(BMS_STATE_ENABLE);
}
BMS_DSGStateBackup = BMS_GlobalParam.Discharge;
}
}
步骤1:
只要 BMS 保护任务上报过压、欠压、过流、短路、高低温 任意故障(
BMS_ProtectAlert非空),直接跳过所有充放电判断,强制保持充放电回路断开。步骤2:按系统模式分类管控(正常工况)
系统分为充电、放电、待机、睡眠四种模式,不同模式执行差异化充放电逻辑:
充电模式(BMS_MODE_CHARGE)
持续监测 SOC 值,当电池电量达到充电停止阈值(SocStopChg) 时 ,调用 BMS_HalCtrlCharge 关闭充电 MOS ,打印Stop Charge。
应用场景:电池充满后自动停止充电,防止三元锂电池过充鼓包、起火。
放电模式(BMS_MODE_DISCHARGE)
持续监测 SOC 值,当电池电量低于放电停止阈值(SocStopDsg) 时 ,调用 BMS_HalCtrlDischarge 关闭放电 MOS ,打印Stop Discharge。
应用场景:防止电芯深度过放,避免电池永久性容量衰减。
待机模式(BMS_MODE_STANDBY)
待机为设备空载状态,同时支持手动开关 + SOC + 均衡互斥三重判断:
充电支路:若手动开启充电、且当前 SOC 低于启动阈值,会尝试获取均衡信号量。信号量可用(无均衡运行)则开启充电;信号被占用(正在均衡)则暂时不启动充电,实现充电与均衡互斥;
放电支路:若手动开启放电、且当前 SOC 高于放电启动阈值,直接开启放电(放电工况默认不与均衡互斥)。
睡眠模式(BMS_MODE_SLEEP)
睡眠为设备低功耗状态,充放电逻辑简化,支持直接响应手动开关指令。
步骤 3:手动开关状态实时监听
代码会备份上一轮充放电开关状态,实时对比当前全局开关:
若手动关闭充放电开关:立即切断对应回路;
睡眠模式下,允许通过手动开关直接唤醒并开启充放电;
作用:响应上位机、及硬件按键下发的人工控制指令,兼顾自动控制与手动干预。
3. 关键交互规则总结
优先级排序:硬件 / 软件故障 > 系统模式自动管控 > 手动开关控制;
互斥规则:待机充电与被动均衡互斥(信号量锁),避免均衡电流干扰充电采样与均衡效果;
阈值逻辑:采用滞回阈值设计,防止 SOC 小幅波动导致充放电回路频繁通断;
电流正负约定:充电电流为正、放电电流为负,与监控任务数据保持统一。
四、均衡的相关知识
4.1、为什么需要均衡?
不一致性会影响电池组的性能 。串联成组的电池组遵循木桶短板效应:在串联成组的电池组系统中,整个电池组系统的容量由容量最小的单体决定。
假如我们有一个3节电池构成的电池组:

我们知道过充过放对电池的伤害很大。所以当充电时电池B已经充满,或者放电时电池B的SOC已经很低,就需要停止充放电,保护电池B,电池A和电池C的电量就无法被充分利用。
这就导致:
**实际可用容量降低:**电池A和C本来可以使用的容量,现在为了照顾B而无处发力,就像二人三足把高个和矮个绑在一起,高个的步子就无法迈得很大。
**电池寿命降低:**步幅小了,需要走的步数就多了,腿就更累;容量降低了,需要充放电的循环次数就增加了,电池的衰减也更大。
4.2、被动均衡
被动均衡原理是在每串电池上并联一个可以开关的放电电阻 ,BMS控制放电电阻对电压较高的单体放电,电能以热的形式耗散掉。例如当电池B快充满时,打开开关让电池B上的电阻放热,让B多余的电能以热能形式耗散,再继续充电,直到A和C也充满。
被动均衡的优点是成本低和电路设计简单;而缺点为是以最低电池残余量为基准进行均衡,无法增加残量少的电池的容量,及均衡电量100%以热量形式被浪费。
注:均衡电量 = 高电芯多出的电量 → 电阻发热消耗,彻底浪费,不能给低电量电芯充电。
4.3、主动均衡
多串的电池之间可通过算法借助储能元器件将电压高的电芯的能量转移给低电压电芯,对电压较高的电池放电,放出的能量用来对电压较低的单体进行充电,能量主要是转移而不是耗散。

主动均衡需要配置相应电路和储能器件,体积大,成本上升,这两个条件一起决定了主动均衡不容易推广应用。
另外,主动均衡的充放过程,无形中增加了电池的循环次数,对于本身需要充放电才能实现均衡的电芯,额外的工作量可能造成其超越一般电芯的老化,进而造成与其他电芯更大的性能差距。
五、被动均衡管理 BMS_EnergyBalanceManage
本方案为被动均衡,高电压电芯通过并联泄放电阻以热能形式释放多余电量,拉平整组电芯电压。
提升电池组整体容量与循环寿命。
cpp
static void BMS_EnergyBalanceManage(void)
{
uint8_t index;
float MinVoltage;
// 未在均衡 + 均衡总开关开启
if (BalanceFlag == false && BMS_GlobalParam.Balance == BMS_STATE_ENABLE)
{
// 电压防抖延时,避免波动误触发
if (BalanceVoltRiseTime <= xTaskGetTickCount())
{
MinVoltage = BMS_MonitorData.CellData[0].CellVoltage;
// 仅待机、充电模式允许开启均衡
if ((BMS_GlobalParam.SysMode == BMS_MODE_STANDBY) || BMS_GlobalParam.SysMode == BMS_MODE_CHARGE)
{
// 最高电压达到均衡启动阈值
if (BMS_MonitorData.CellData[BMS_GlobalParam.Cell_Real_Number-1].CellVoltage > BMS_EnergyData.BalanceStartVoltage)
{
float CmpVoltage;
xSemaphoreTake(BalanceSem, 0); // 抢占信号量,锁住充电
// 遍历电芯,筛选压差超标单体 + 相邻互斥校验
for(index = 1; index < BMS_GlobalParam.Cell_Real_Number + 1; index++)
{
CmpVoltage = BMS_MonitorData.CellData[BMS_GlobalParam.Cell_Real_Number-index].CellVoltage;
if (CmpVoltage - MinVoltage > BMS_EnergyData.BalanceDiffeVoltage)
{
bool result = false;
uint8_t CellNumber = BMS_MonitorData.CellData[BMS_GlobalParam.CellNumber];
// 第一节电芯判断
if (CellNumber == 0)
{
if ((BMS_EnergyData.BalanceRecord & 0x02) == 0)
result = true;
}
// 最后一节电芯判断
else if (CellNumber + 1 == BMS_GlobalParam.Cell_Real_Number)
{
if ((BMS_EnergyData.BalanceRecord & (1 << (CellNumber - 1))) == 0)
result = true;
}
// 中间电芯:左右相邻均不可均衡
else
{
if (((BMS_EnergyData.BalanceRecord & (1 << (CellNumber - 1))) == 0) &&
((BMS_EnergyData.BalanceRecord & (1 << (CellNumber + 1))) == 0))
{
result = true;
}
}
if (result == true)
{
BMS_INFO("Balance Cell:%d\r\n", CellNumber + 1);
BMS_EnergyData.BalanceRecord |= 1 << CellNumber;
}
}
else
{
break;
}
}
// 存在需要均衡的电芯,启动硬件均衡+定时器
if (BMS_EnergyData.BalanceRecord != BMS_CELL_NULL)
{
BMS_HalCtrlCellsBalance(BMS_EnergyData.BalanceRecord, BMS_STATE_ENABLE);
BMS_BalanceStartTimer(BMS_EnergyData.BalanceCycleTime);
BalanceFlag = true;
BMS_INFO("Balance Start\r\n");
return;
}
// 无均衡需求,释放信号量
BMS_EnergyData.BalanceRecord = BMS_CELL_NULL;
xSemaphoreGive(BalanceSem);
}
}
// 刷新电压防抖计时
BalanceVoltRiseTime = BALANCE_VOLT_RISE_DELAY + xTaskGetTickCount();
}
}
}
执行均衡前会做多层过滤,避免误触发,前置条件依次为:
全局均衡总开关开启(BMS_GlobalParam.Balance == BMS_STATE_ENABLE);
当前无均衡正在运行(BalanceFlag == false);
电压防抖延时结束(BalanceVoltRiseTime ≤ xTaskGetTickCount):
充放电切换、负载突变会造成电压瞬时毛刺,延时等待电压稳定,杜绝电压抖动误触发均衡;
系统工况限制:仅待机、充电模式允许启动均衡;
放电模式下关闭均衡**,因为大负载工况均衡无意义,且会增加发热**。
核心均衡判断与执行流程
1**、读取监控任务采集的单体电压,判断两个核心阈值:**
单体最高电压 ≥ 均衡启动电压(BalanceStartVoltage):电压过低时电芯本身无压差,无需均衡;
电芯最大电压 - 最小电压 ≥ 均衡压差(BalanceDiffeVoltage):整组电芯压差超标,满足均衡触发条件。
2、严格遵循BQ76920芯片硬件限制,相邻两节电芯不能同时开启均衡。
代码通过位掩码变量 BalanceRecord 记录当前正在均衡的电芯编号,遍历每一节高压电芯时做校验:
第一节电芯:仅校验后一节电芯是否在均衡;
最后一节电芯:仅校验前一节电芯是否在均衡;
中间电芯:同时校验左右相邻两节电芯;
校验通过后,将当前电芯编号写入位掩码,标记为待均衡电芯。
3、一旦启动均衡,信号量( xSemaphoreTake**)被占用,待机模式下的充电逻辑将无法执行,实现均衡优先,避免充电电流影响均衡精度。**
4、均衡停止条件
单次均衡定时器计时结束(主动限时,防止电阻长时间发热、电量无谓损耗);
电芯压差缩小至均衡解除阈值以下;
人为关闭全局均衡总开关;
系统切换至放电模式;
BMS 触发任意保护故障,全局硬件回路切断。
总结
本期博客介绍了整套 BMS 的能量管理任务(BMS_EnergyTask)。