
🎬 渡水无言 :个人主页渡水无言
❄专栏传送门 : 《linux专栏》《嵌入式linux驱动开发》《linux系统移植专栏》
❄专栏传送门 : 《freertos专栏》 《STM32 HAL库专栏》《linux裸机开发专栏》
❄专栏传送门 :《产品测评专栏》 《Ai智能体专栏) 《ROS开发专栏》
❄专栏传送门 :《BMS专栏》
⭐️流水不争先,争的是滔滔不绝
📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生
| 省级优秀毕业生获得者 | csdn新星杯TOP1 | 半导纵横专栏博主 | 211在读研究生
在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连
目录
二、电池数据采集模块(BMS_MonitorBattery)
[2.1 单体电芯电压采集](#2.1 单体电芯电压采集)
三、系统模式与低功耗管理模块(BMS_MonitorSysMode)
前言
在基于 STM32F103 + BQ76920 的锂电 BMS 系统中,电池状态监控任务(BMS_MonitorTask) 是整个软件架构的数据源头与调度核心。BMS 所有功能,包括过压 / 欠压保护、电芯均衡、SOC 电量计算、故障告警、低功耗管理等,都依赖该任务采集的电压、电流、温度等原始数据。
本期博客就来实现并详解一下 BMS 系统中的电池状态监控任务。
一、任务整体概述
BMS_MonitorTask 是 BMS 系统基础核心任务,优先级较高,采用周期性轮询运行机制。
核心两大职能:
全维度电池数据采集:周期性读取 5 串单体电压、电池总电压、电池环境温度、回路电流;
采集完的数据丢给全局变量,由监控任务周期性刷新的共享全局数据缓存(全局变量),供分析、保护、能量管理等任务读取使用。
系统工作模式判定与低功耗管理:依据充放电电流自动切换充电、放电、待机、睡眠四大模式,实现设备闲置时自动进入低功耗睡眠。
1.1、任务主循环
cpp
static void BMS_MonitorTaskEntry(void *paramter)
{
while (1)
{
BMS_MonitorBattery(); // 第一步:采集电池各项参数
BMS_MonitorSysMode(); // 第二步:判断系统模式 + 低功耗管理
osDelay(MONITOR_TASK_PERIOD); // 延时250ms,释放CPU
}
}
数据采集 → 模式判断 → 延时挂起,周而复始。
1.2、完整函数调用栈
自上而下梳理全调用关系,清晰展示 应用任务层 → HAL 抽象层 → BQ76920 驱动层 → 底层 I2C/GPIO 驱动 的执行链路:
cpp
BMS_MonitorInit // 任务初始化(系统上电调用)
└── BMS_MonitorTaskEntry // 监控任务主入口(死循环周期执行)
├── BMS_MonitorBattery // 电池全参数采集函数
│ ├── Bms_HalMonitorCellVoltage // 采集5串单体电芯电压
│ │ ├── BQ769X0_UpdateCellVolt // BQ76920驱动:更新单体电压数据
│ │ │ ├── BQ769X0_ReadBlockWithCRC // 带CRC校验的块读取(抗干扰)
│ │ │ │ ├── I2C_TransferMessages // I2C底层报文传输
│ │ │ │ │ ├── I2C_Restart // I2C重启时序
│ │ │ │ │ │ └── SDA_H // 拉高SDA引脚
│ │ │ │ │ │ └── HAL_GPIO_WritePin // 操作GPIO寄存器
│ │ └── BubbleSort // 冒泡排序:求电芯最大/最小电压、压差
│ ├── Bms_HalMonitorBatteryVoltage // 采集电池整组总电压
│ │ ├── BQ769X0_UpadteBatVolt // BQ76920驱动:更新总电压
│ │ │ └── BQ769X0_ReadRegisterWordWithCRC // 带CRC单寄存器读取
│ │ │ └── I2C_TransferMessages
│ ├── Bms_HalMonitorCellTemperature // 采集电池环境温度
│ │ ├── BQ769X0_UpdateTsTemp // BQ76920驱动:更新温度
│ │ │ └── BQ769X0_ReadBlockWithCRC
│ │ └── BubbleFloat // 浮点排序:筛选温度极值
│ └── Bms_HalMonitorBatteryCurrent // 采集充放电电流(硬件中断触发)
│ └── BQ769X0_UpdateCurrent
│ └── BQ769X0_ReadRegisterWordWithCRC
└── BMS_MonitorSysMode // 系统模式监控与切换(充电/放电/待机/睡眠)
二、电池数据采集模块(BMS_MonitorBattery)
该函数负责采集单体电压、总电压、温度、电流 四大类参数,采用基础周期 + 分频采样架构,兼顾实时性与系统负载。
2.1 单体电芯电压采集
上层监控任务仅做周期调度与使能判断,不直接操作硬件与芯片寄存器。
相关代码如下:
cpp
static void BMS_MonitorBattery(void)
{
// 1. 5串单体电芯电压采集
CountCellVoltage += MONITOR_TASK_PERIOD;
if (FlagCellVoltage == true && CountCellVoltage >= UPDATE_CELL_VOLTAGE_CYCLE)
{
Bms_HalMonitorCellVoltage();
CountCellVoltage = 0;
}
else if (FlagCellVoltage == false)
{
CountCellVoltage = 0;
}
每轮任务累加计时,达到 250ms 且采集使能开启时,执行采集;
底层通过 I2C+CRC 校验读取 BQ76920 寄存器,抗工业干扰;
采集完成后执行BubbleSort冒泡排序,得到电芯最大、最小电压与压差,为均衡、过压 / 欠压判断提供依据。
完整调用链路
结合之前的调用栈,自上而下拆解每一层角色,清晰区分调度层、业务抽象层、芯片驱动层、底层外设层:
cpp
BMS_MonitorTaskEntry(FreeRTOS监控任务主循环)
↓
BMS_MonitorBattery(采集总调度函数)
↓ 【周期+使能判断,满足条件才执行采集】
Bms_HalMonitorCellVoltage 【单体电压采集核心入口 / HAL抽象层】
↓
BQ769X0_UpdateCellVolt 【BQ76920芯片驱动层】
↓
BQ769X0_ReadBlockWithCRC 【带CRC校验的寄存器块读取】
↓
I2C_TransferMessages 【MCU底层I2C报文收发】
↓
I2C_Restart → HAL_GPIO_WritePin 【GPIO电平操作,硬件时序】
↓
BubbleSort 【数据后处理:电压排序、计算最大/最小、压差】
上层:BMS_MonitorBattery(调度层):只负责调用。
中层:Bms_HalMonitorCellVoltage 是单体电压采集的真正主体,承担核心业务逻辑:
屏蔽底层芯片、I2C 细节,向上层任务提供统一调用接口;
调用 BQ76920 驱动接口,发起多串电芯电压批量读取;
接收芯片返回的原始 ADC 数据,完成原始值 → 实际电压的换算;
调用 BubbleSort 冒泡排序函数,计算 5 节电芯的最大电压、最小电压、单体压差;
将最终有效电压数据存入全局结构体 BMS_MonitorData,供给均衡、保护、通信等其他任务使用。
下层:BQ76920 驱动 + I2C 底层:
属于硬件驱动支撑层,只负责硬件通信时序:
BQ769X0_UpdateCellVolt:指定 BQ76920 电压寄存器地址,组织读取指令;
BQ769X0_ReadBlockWithCRC:增加 CRC 校验,提升工业环境下通信可靠性;
I2C / GPIO 函数:纯粹实现 I2C 起始、停止、读写时序。
这部分是硬件通道,不参与业务逻辑判断。
2.2、电池总电压采集
同理,总电压的采集和单体电芯电压采集逻辑完全一致。
电池总电压采集的核心执行函数为 Bms_HalMonitorBatteryVoltage。上层监控任务仅负责周期调度与开关控制,实际的数据读取、解析、换算工作,全部由该函数完成。
完整调用链路
cpp
BMS_MonitorTaskEntry(FreeRTOS监控主任务)
↓
BMS_MonitorBattery(采集总调度函数)
↓ 周期+使能条件判断
Bms_HalMonitorBatteryVoltage 【电池总电压核心采集入口 / HAL层】
↓
BQ769X0_UpadteBatVolt 【BQ76920芯片驱动层】
↓
BQ769X0_ReadRegisterWordWithCRC 【单寄存器CRC校验读取】
↓
I2C_TransferMessages 【底层I2C报文传输】
↓
I2C_Restart → HAL_GPIO_WritePin 【I2C/GPIO硬件时序】
2.3、电池温度采集
同理,电池温度采集也是一样的。
电池温度采集的核心执行函数为 Bms_HalMonitorCellTemperature。
上层 BMS_MonitorBattery 仅做周期调度与使能判断,实际读取、换算、数据处理全部由该 HAL 层函数完成。
完整调用链路
cpp
BMS_MonitorTaskEntry(FreeRTOS主任务)
↓
BMS_MonitorBattery(采集总调度)
↓ 周期+使能判断
Bms_HalMonitorCellTemperature 【温度采集核心入口 / HAL层】
↓
BQ769X0_UpdateTsTemp 【BQ76920驱动层】
↓
BQ769X0_ReadBlockWithCRC 【带CRC块读取】
↓
I2C_TransferMessages 【底层I2C传输】
↓ I2C_Restart → HAL_GPIO_WritePin 硬件时序
↓
BubbleFloat 【浮点排序:筛选最高/最低温度】
核心业务层:Bms_HalMonitorCellTemperature
HAL 层核心函数,温度采集全流程闭环:
屏蔽 BQ76920、I2C 底层细节,提供统一调用接口;
调用驱动函数,读取 BQ76920 内部温度原始 ADC 数据;
结合 NTC 热敏电阻参数,完成原始值 → 实际温度换算;
调用BubbleFloat浮点排序函数,筛选最高温、最低温;
数据存入全局结构体 BMS_MonitorData,供给过温 / 低温保护、热管理模块使用。
底层驱动层
BQ769X0_UpdateTsTemp:指定温度寄存器地址,发起读取;
BQ769X0_ReadBlockWithCRC:CRC 校验抗干扰,工业场景必备;
I2C/GPIO:仅实现硬件通信时序,无业务逻辑。
2.4、充放电电流采集
同理,充放电电流采集也是一样的,该模块和电压、温度最大区别:放弃定时分频采样,改为硬件中断触发采集。
充放电采集的核心执行函数为 Bms_HalMonitorBatteryCurrent。
完整调用链路
cpp
外部中断(BQ76920电流异常/ALERT中断)
↓ 中断服务函数调用
BMS_MonitorHwCurrent() // 置位触发标志 FlagSampleIntCur
↓
BMS_MonitorTaskEntry(FreeRTOS主任务)
↓
BMS_MonitorBattery(采集总调度)
↓ 判断标志位
Bms_HalMonitorBatteryCurrent 【电流采集核心入口 / HAL层】
↓
BQ769X0_UpdateCurrent 【BQ76920驱动层】
↓
BQ769X0_ReadRegisterWordWithCRC 【单寄存器CRC读取】
↓
I2C_TransferMessages 【底层I2C传输】
↓ I2C_Restart → HAL_GPIO_WritePin 硬件时序
触发流程:
BQ76920 检测到电流突变、过流、短路,触发外部中断;
中断服务函数中调用 BMS_MonitorHwCurrent(),置位 FlagSampleIntCur = true;
下一轮监控任务轮询检测到标志位,立即执行电流采集;
采集完成后自动清空标志位,等待下一次触发。
核心业务层:Bms_HalMonitorBatteryCurrent
HAL 层核心函数,完成电流采集全流程:
调用驱动接口,读取 BQ76920 电流寄存器数据;
根据采样电阻阻值(本项目 5mΩ),将差分原始值换算为实际充放电电流;
约定规则:电流正数 = 充电,电流负数 = 放电;
数据存入全局结构体,供给过流保护、短路保护、SOC 安时积分、系统模式判断使用。
底层驱动层
BQ769X0_UpdateCurrent:读取电流专用寄存器;
BQ769X0_ReadRegisterWordWithCRC:单寄存器 CRC 读取;
I2C 底层:负责硬件通信时序。
三、系统模式与低功耗管理模块(BMS_MonitorSysMode)
本模块基于电流阈值划分四大工作模式,结合 FreeRTOS 信号量实现互斥,完成自动唤醒、待机转睡眠的低功耗逻辑。
cpp
系统模式监控 电流为正 = 充电,电流为负 = 放电
BatteryCurrent > 20mA || BatteryCurrent < -20mA 处于非睡眠模式
BatteryCurrent < 20mA || BatteryCurrent > -20mA 处于待机模式或者睡眠模式
BatteryCurrent <= -20mA 处于放电模式
BatteryCurrent >= 20mA 处于充电模式
模式切换完整逻辑代码:
cpp
static void BMS_MonitorSysMode(void)
{
static BMS_SysModeTypedef SysModeBackup = BMS_MODE_NULL;
static uint32_t StandbyCount = 0;
// 睡眠模式唤醒逻辑
if (BMS_GlobalParam.SysMode == BMS_MODE_SLEEP)
{
if ((BMS_MonitorData.BatteryCurrent >= 0.02f) || (BMS_MonitorData.BatteryCurrent <= -0.02f))
{
BMS_GlobalParam.SysMode = BMS_MODE_STANDBY;
BMS_INFO("Wake Up\r\n");
}
return;
}
// 待机模式判断
if (BMS_MonitorData.BatteryCurrent < 0.02f && BMS_MonitorData.BatteryCurrent > -0.02f)
{
BMS_GlobalParam.SysMode = BMS_MODE_STANDBY;
StandbyCount += MONITOR_TASK_PERIOD;
// 待机超时,尝试进入睡眠
if (StandbyCount >= (uint32_t)BMS_ENTRY_SLEEP_TIME * 60000U)
{
// FreeRTOS 信号量互斥:正在均衡则禁止进入睡眠
if (xSemaphoreTake(BalanceSem, 0) == pdPASS)
{
StandbyCount = 0;
BMS_GlobalParam.SysMode = BMS_MODE_SLEEP;
BMS_INFO("Entry Sleep Mode\r\n");
xSemaphoreGive(BalanceSem);
}
}
// 仅模式变化时打印日志,避免串口刷屏
if (SysModeBackup != BMS_MODE_STANDBY)
{
SysModeBackup = BMS_MODE_STANDBY;
BMS_INFO("Entry Standby Mode\r\n");
}
}
// 充电模式
else if (BMS_MonitorData.BatteryCurrent >= 0.02f)
{
StandbyCount = 0;
BMS_GlobalParam.SysMode = BMS_MODE_CHARGE;
if (SysModeBackup != BMS_MODE_CHARGE)
{
SysModeBackup = BMS_MODE_CHARGE;
BMS_INFO("Entry Charge Mode\r\n");
}
}
// 放电模式
else if (BMS_MonitorData.BatteryCurrent <= -0.02f)
{
StandbyCount = 0;
BMS_GlobalParam.SysMode = BMS_MODE_DISCHARGE;
if (SysModeBackup != BMS_MODE_CHARGE)
{
SysModeBackup = BMS_MODE_DISCHARGE;
BMS_INFO("Entry Discharge Mode\r\n");
}
}
}
自动唤醒:睡眠状态下检测到充 / 放电电流,立即切换为待机模式;
信号量互斥:使用 FreeRTOS 二值信号量BalanceSem,若当前正在执行电芯均衡,禁止进入睡眠,防止均衡流程被打断;
日志优化:备份上一次模式,仅状态切换时打印信息,减少串口资源占用。
外部控制接口函数
提供一组通用接口,可在其他任务、中断、命令行中动态启停采样,灵活性极强:
cpp
/**
* @brief 单体电压采集开关
*/
void BMS_MonitorStateCellVoltage(BMS_StateTypedef NewState)
{
FlagCellVoltage = (NewState == BMS_STATE_ENABLE) ? true : false;
}
/**
* @brief 电池总电压采集开关
*/
void BMS_MonitorStateBatVoltage(BMS_StateTypedef NewState)
{
FlagBatVoltage = (NewState == BMS_STATE_ENABLE) ? true : false;
}
/**
* @brief 温度采集开关
*/
void BMS_MonitorStateCellTemp(BMS_StateTypedef NewState)
{
FlagCellTemp = (NewState == BMS_STATE_ENABLE) ? true : false;
}
/**
* @brief 电流采集开关
*/
void BMS_MonitorStateBatCurrent(BMS_StateTypedef NewState)
{
FlagBatCurrent = (NewState == BMS_STATE_ENABLE) ? true : false;
}
/**
* @brief 硬件中断触发电流采集
*/
void BMS_MonitorHwCurrent(void)
{
FlagSampleIntCur = true;
}
四、任务数据流与整机联动
整体数据流图如下:

总结
本期博客完成了详解 BMS 系统中的电池状态监控任务。