BMS系统专栏:电池状态监控任务

🎬 渡水无言个人主页渡水无言

专栏传送门 : 《linux专栏》《嵌入式linux驱动开发》《linux系统移植专栏》

专栏传送门 : 《freertos专栏》 《STM32 HAL库专栏》《linux裸机开发专栏

专栏传送门《产品测评专栏》 《Ai智能体专栏) 《ROS开发专栏

专栏传送门 :《BMS专栏

⭐️流水不争先,争的是滔滔不绝

📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生

| 省级优秀毕业生获得者 | csdn新星杯TOP1 | 半导纵横专栏博主 | 211在读研究生

在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连

目录

前言

一、任务整体概述

1.1、任务主循环

1.2、完整函数调用栈

二、电池数据采集模块(BMS_MonitorBattery)

[2.1 单体电芯电压采集](#2.1 单体电芯电压采集)

2.2、电池总电压采集

2.3、电池温度采集

2.4、充放电电流采集

三、系统模式与低功耗管理模块(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 系统中的电池状态监控任务。

相关推荐
XTIOT6662 小时前
多形态护照 OCR 读取器传输机制、识别算法与行业落地技术对比
大数据·人工智能·嵌入式硬件·物联网·ocr
欢乐熊嵌入式编程2 小时前
选型避坑:ESP32 vs STM32+模组 vs NB-IoT,不同场景怎么选
stm32·单片机·嵌入式硬件·物联网·esp32·嵌入式iot
拎得清n4 小时前
寄存器点灯
单片机·嵌入式硬件
破晓单片机14 小时前
067、STM32项目分享:语音儿童学习书桌系统
stm32·单片机·嵌入式硬件
10WTW0114 小时前
微机原理 8259A 可编程中断控制器
单片机·嵌入式硬件
番茄灭世神16 小时前
RTC授时时间戳转换工具
c语言·单片机·嵌入式
破晓单片机16 小时前
068、STM32项目分享:智能小区门禁系统
stm32·单片机·嵌入式硬件
望眼欲穿的程序猿19 小时前
Hello World
嵌入式硬件·rust
ACP广源盛1392462567319 小时前
GSV5600@ACP#多接口协议转换芯片,物理 AI 便携终端的互联核心
大数据·人工智能·分布式·嵌入式硬件·spark