两轮BMS 防打火策略详解

BMS 防打火策略详解

本文基于量产 60V BMS(STM32F0 + SH367306 AFE),拆解"防打火"的原理与软件实现。


一、为什么会打火?

电动两轮车的电机控制器输入端有大容量电解电容(通常几千 µF)。BMS 输出端与控制器之间通过接插件连接。

复制代码
BMS 电池 ────[接插件]──── 电机控制器
                             │
                          ┌──┴──┐
                          │ 电容  │  ← 几千 µF,初始电压 0V
                          └─────┘

当接插件插入瞬间,电池电压(60V+)直接加到 0V 的电容上。电容充电电流 I = C × dV/dt,在 µs 级时间内电流可达几百安------接插件触点处产生电弧(打火),长期会烧蚀触点、熔毁接插件、甚至击穿 MOS。


二、怎么防?------预充电路

在放电主回路之外加一条预充支路:一个小功率 MOS 串一个 22Ω 限流电阻。

复制代码
电池+ ──┬── 放电 MOS ──────────┬── 负载+
        │                      │
        └── 预充 MOS ── 22Ω ──┘

流程:

复制代码
1. 预充 MOS 导通,放电 MOS 断开 → 电流经 22Ω 电阻给负载电容充电
   最大电流 = 60V / 22Ω ≈ 2.7A(安全)

2. 等待电容电压充到接近电池电压(数百 ms)

3. 放电 MOS 导通,预充 MOS 断开 → 正常放电,无打火

三、软件实现:Idle 状态机

PMM 模块在空闲模式下有一个四级状态机(PMM.c:35-39):

复制代码
kIdleInit ──→ kIdleFirePrevent ──→ kIdlePowerOn ──→ 放电模式

3.1 kIdleInit ------ 初始化防打火

  • 防打火使能:闭合充电 MOS + 预充 MOS,断开放电 MOS
  • 检测到放电电流 > 30mA(用户拧电门)→ 进入 kIdleFirePrevent
  • 防打火未使能:直接闭合放电 MOS,跳过预充

3.2 kIdleFirePrevent ------ 执行预充时序

PMM.c:156-175

c 复制代码
// 阶段1:预充 MOS 断开 200ms
s_TimeDelay < 200ms → PreMos_Ctrl = 0

// 阶段2:预充 MOS 闭合 500ms(电流经 22Ω 给电容充电)
200ms ≤ s_TimeDelay < 900ms → PreMos_Ctrl = 1

// 阶段3:预充完成,标记防打火结束,跳转
s_TimeDelay ≥ 900ms → FirePreventEnd_flg = 1 → 进入 kIdlePowerOn

整个预充时序持续约 900ms:200ms 断开(等待) + 500ms 导通(充电容) + 200ms 余量。

3.3 kIdlePowerOn ------ 闭合主回路

预充完成后,放电 MOS 闭合,预充 MOS 断开,进入正常放电模式。

3.4 防打火重新开启

放电结束后电流归零超过 2 秒s_FirePreventEnd_flg 自动清零,下次再有电流时重新走防打火流程。

3.5 什么时候触发防打火?

防打火只在 "负载电容可能已经放空了" 的时候触发,不是每次有电流都走。

触发(三种情况):

场景 触发原因
BMS 上电启动 默认 IdleStMachine = kIdleInit,直接走防打火
空闲时无电流 > 2s 电容可能通过漏电流放空,FirePreventEnd_flg 自动清零
充电 > 3 分钟后回空闲 IdleStMachine 重置为 kIdleInit

不触发:

从放电模式退出回空闲时,跳过防打火 ,直接跳到 kIdlePowerOn

c 复制代码
// 从放电退出到idle模式,先到poweron模式
// (退到init中立刻拧电门有掉电的风险)
IdleStMachine_enum = kIdlePowerOn;

因为刚放完电,负载电容还满着------不需要预充。而且用户可能是等红灯松油门,马上又要拧------如果每次都走 700ms 防打火,体验会很差。


四、短路保护联动

短路发生后,即使 AFE 已经在 50µs 关断了放电 MOS,MCU 端的 PreChgIf 也会主动闭合预充 MOS(PreChgIf.c:122-125):

c 复制代码
// 发生短路,强制进行预充
if(g_Afe1SampleRawData.ShortCircuitPreChg_Cnt != 0)
{
    g_PreChg_cmd = 1U;  // 强制闭合预充 MOS
}

短路可能是负载电容充电的瞬时大电流导致的误判------强制预充相当于给电容一次"温柔充电"的机会,如果充满后不再触发短路,说明是容性负载而非真短路。


五、预充电路故障检测

预充支路通过 ADC 检测预充电阻上的电流(PreChgIf.c:137):

c 复制代码
// 22Ω 采样电阻,ADC 读数换算为电流
g_PreChgCurrent_mA = ADC值 × 33 × 10 × 1000 / (4095 × 22);

如果预充电流 > 2A 持续 1s ,判定预充电路故障(电阻短路或 MOS 击穿),上报 PreChgCircuitFlt,同时禁用防打火功能。


六、关键参数

参数 说明
预充电阻 22Ω 限流,60V 电池最大预充电流 ~2.7A
预充等待 200ms 断开等待
预充导通 500ms 电容充电时间
预充总时长 ~900ms
防打火重开 无电流 2s 自动重置
预充故障判定 >2A 持续 1s 电阻短路/MOS 击穿

七、200ms 断开阶段的作用

用户拧电门 → kIdleInit 检测到电流 → 进入 kIdleFirePrevent → 预充 MOS 先断开 200ms。

这 200ms 不是充电容,而是状态复位

  • 进入 kIdleFirePrevent 之前,预充 MOS 在 kIdleInit 里已经是闭合的。先断开才有明确的预充起点
  • 给电路一个静默窗口,等残余电流或接触抖动消退
  • 如果 200ms 内电流归零,说明用户只是碰了一下油门而非真要骑车,不需要后续流程

八、为什么用时间预估而不是检测外侧电压?

更好的方案是:外侧加电压采集 → 读到 V_out ≈ V_bat 就切主 MOS。但这需要一路 ADC + 两个分压电阻(60V → 3.3V),板子上没做纯粹是省成本。

时间预估为什么够用?最差情况:

复制代码
预充电阻 22Ω,负载电容 5000µF:
  τ = 22 × 0.005 = 110ms
  5τ = 550ms → 充到 99%

实际给 500ms,电容再大也充到 95%,压差仅 ~3V,残余电流 < 140mA → 不打火

如果有外侧电压采集,逻辑可以简化成两个状态,还能额外获得:自适应电容、预充超时故障检测、MOS 粘连检测、接插件松动检测------但代价就是那两个电阻。


本文基于量产 BMS 代码分析。

相关推荐
听取WA声一片(无恶意)4 分钟前
CSP-J/S 初赛图论完全讲义
算法·csp-s初赛·csp-j初赛
Brilliantwxx16 分钟前
【算法从零到千】【51-54】逆序对(分治+递归算法)
数据结构·算法·排序算法
小星星闪亮登场25 分钟前
2026河南萌新联赛第四场--南阳理工学院
数据结构·算法·贪心算法·动态规划·哈希算法·广度优先
土司大王27 分钟前
LeetCode hot100——两数之和
数据结构·算法·leetcode
luj_17681 小时前
大航海时代:沉浸式财商实战沙盒
c语言·开发语言·网络·经验分享·算法
Navigator_Z1 小时前
LeetCode //C - 1200. Minimum Absolute Difference
c语言·算法·leetcode
wabs6661 小时前
关于字符串【力扣344.反转字符串的思考】
数据结构·算法·leetcode
民乐团扒谱机2 小时前
【微实验】谐波乘积谱(HPS)算法深度解析:原理、数学与代码实现
开发语言·人工智能·python·算法·语音识别·音乐
Nil2082 小时前
leetcode 73矩阵置0
算法·leetcode·矩阵
luj_17684 小时前
财商游戏三大核心维度设计
c语言·开发语言·网络·经验分享·算法