两轮BMS 限流机制详解:放电 PWM 斩波与充电通信协议
BMS 限流机制详解放电 PWM 斩波与充电通信协议本文基于某量产两轮车 60V BMS 源码STM32F0 SH367306 AFE完整拆解其放电限流和充电限流两条路径的硬件链路、软件策略与数据流。一、两种降功率路径BMS 限制功率时放电和充电的手段根本不同放电降功率充电降功率手段MOSFET PWM 斩波通信协议命令充电柜执行者BMS 自身充电柜物理层MCU GPIO → AFE CTLD 引脚 → MOS 栅极Modbus / HD20 数字指令能否 PWM✅ 负载端有母线电容平滑❌ 充电柜是恒流源切 MOS 会过压响应延迟1µs硬件直连百 ms 级通信 充电柜响应上篇放电限流PWM 斩波二、硬件链路STM32F0 PB1 (GPIO) ──→ SH367306 AFE CTLD 引脚 ──→ 放电 MOS 栅极驱动 │ │ │ 100µs 翻转 硬件逐周期响应 MOSFET 直接开关 不经过 I2C 不经过 MCU为什么不走 I2C 通信一次 I2C 读写约 1ms而 PWM 频率约 303Hz周期 3.3msI2C 根本跟不上。CTLDCurrent Limit Discharge是 SH367306 专门设计的硬件限流引脚——拉低即关断 MOS拉高即恢复导通响应时间 1µs。GPIO 控制Sh367306_1_Drv.h// 拉低 → AFE 关断放电 MOSstaticvoidCURRENTLIMITPIN_GPIO_OUT_L(){Afe1_LimitCurrPwm_GPIO_Port-BSRRAfe1_LimitCurrPwm_Pin;}// 拉高 → AFE 恢复导通staticvoidCURRENTLIMITPIN_GPIO_OUT_H(){Afe1_LimitCurrPwm_GPIO_Port-BRRAfe1_LimitCurrPwm_Pin;}AFE 初始化时使能 CTLD 功能g_Afe1Sconfig1.B.CTLD_EN1;// CTLD enable三、三层时间结构┌──────────────────────────────────────────────────────┐ │ 层级 3占空比调节 每 200ms 改一次 │ │ 运行在PMM 10ms 任务循环 │ │ 做的事判断电流 → DisChrgLimitDutyCycle 1 或 -1 │ │ 相当于油门深浅的决策层 │ ├──────────────────────────────────────────────────────┤ │ │ 修改变量 │ │ ▼ │ │ DisChrgLimitDutyCycle (0~32) │ │ DisChrgLimitCycle (33) │ │ │ 被读取 │ │ ▼ │ ├──────────────────────────────────────────────────────┤ │ 层级 2PWM 波形生成 每 100µs 一次 │ │ 运行在TIM16 硬件中断 │ │ 做的事TimeCnt 和 duty 比较 → GPIO 翻转 │ │ 相当于踩油门的执行层 │ ├──────────────────────────────────────────────────────┤ │ │ │ │ 层级 1PWM 周期 33 × 100µs 3.3ms (≈303Hz) │ │ TimeCnt 从 0→32 循环每次归零开始新周期 │ │ 这是 CTLD 引脚上看到的实际波形 │ └──────────────────────────────────────────────────────┘3.1 100µs ISR —— PWM 波形的每次翻转TIM16 每 100µs 触发一次中断AfeIf_Sample.cvoidAfeIf_CurrentLimit()// TIM16 中断每 100µs{staticuint16_tTimeCnt0;if(duty_pct!0){if(TimeCntduty_pct)CURRENTLIMITPIN_GPIO_OUT_L();// 前 N 个 tick关断 MOSelseCURRENTLIMITPIN_GPIO_OUT_H();// 后 (33-N) 个 tick导通TimeCnt;if(TimeCntcycle)// 33 tick 到 → 归零开始下一周期TimeCnt0;}}TimeCnt是一个 0→32 循环的计数器。duty_pct决定了一个周期里低电平占几个 tickduty 1 ┌─┐──────────────────────────────── 3% 关断 duty 10 ┌─────┐──────────────────────────── 30% 关断 duty 20 ┌──────────┐─────────────────────── 60% 关断 duty 32 ┌────────────────┐───────────────── 97% 关断 高电平 MOS 导通 低电平 MOS 关断 周期 33 × 100µs 3.3ms3.2 3.3ms 周期 —— PWM 载波频率DSG_CURRENT_LIMIT_CYCLE 3300/100 33。为什么是约 303Hz太低如 100Hz电流纹波大电机控制器输入端电容扛不住负载感受到明显抖动太高如 10kHzMOSFET 开关损耗剧增发热严重且 EMI 问题恶化303Hz在 15~30A 级别放电场景下兼顾纹波和损耗的折中3.3 200ms 调节 —— 占空比决策频率PMM 任务每 10ms 跑一次DSG_CURRENT_LIMIT_REGULATE_CYCLE 20即 20 × 10ms 200ms 改一次占空比。200ms 内 PWM 已跑了约 60 个完整周期。调节太快系统振荡太慢响应迟钝。配合 ±2A 死区200ms 是经过调试的稳定参数。四、占空比调节策略kPMMMode_CurLimitRegulate()实现了一个带死区的积分滞环调节器。4.1 状态机┌─────────┐ │ 未开启 │ duty 0 └────┬────┘ │ 电流 限流值2A积分达 10s ▼ ┌─────────┐ │ 已开启 │ duty 1起始值 └────┬────┘ │ ┌──────────────┼──────────────┐ │ │ │ ▼ ▼ ▼ 电流 限值2A 电流在死区 电流 限值-2A duty (200ms) duty 不变 duty-- (200ms) │ │ ▼ ▼ 上限 32 (97%) 下限 1 → 0 (关闭) │ └──────────────┬──────────────┘ │ 开启 10s 后 │ ▼ ┌─────────┐ │ 强制关闭 │ duty 0 └─────────┘4.2 开启条件积分器不是简单延时if(duty0){if(电流限流值2A)delay_cnt;// 持续超标 → 累加elsedelay_cnt--;// 偶尔回落 → 回退if(delay_cnt10s等效值){duty1;// 开启cycle33;}}delay_cnt是一个可增可减的积分器——电流必须持续超标才能累到阈值偶尔掉下来就退回去。避免负载瞬间波动如起步、爬坡瞬间误触发。4.3 死区±2A电流 ▲ │ 限值2A ─ ─ ─ ─ ─ ← 超过duty 递增 │ │ 限值 ─ ─ ─ ─ ─ ← 死区duty 不动 │ │ 限值-2A ─ ─ ─ ─ ─ ← 低于duty 递减没有死区的话电流被限到目标值附近时会在 ±0.1A 范围内反复触发 duty / duty–PWM 占空比来回振荡负载侧会听到明显啸叫。4.4 强制关闭10s 超时close_cnt;if(close_cnt10s等效值){duty0;close_cnt0;}安全兜底——传感器故障或限流逻辑异常时10s 后自动关闭限流恢复放电。限流不是长期状态只是一个缓冲窗口。4.5 限流目标值来源uiCurLimitValue_mA根据单体最低电压动态调整条件限流目标开启延时关闭超时最低单体 欠压告警阈值35A3s5s正常运营SOP 查表值钳位在 35A ~ 1.5C10s30s未运营下线检测1.5C30Ah 电池约 45A10s30s五、完整实例以 30Ah 电池、限流值 35A、实测电流 45A 为例时刻 电流 duty 说明 ──────────────────────────────────────────── 0.0s 45A 0 电流超标积分器开始累加 0.0~10s 45A ↑ 持续超标积分值单调递增 10.0s 45A 1 积分到阈值限流开启每周期关 100µs 10.2s 44A 2 仍超 35237A每 200ms 递增 10.4s 43A 3 ... 11.8s 37A 9 12.0s 36A 10 ← 进入死区 (35±2A)duty 停在 10 12.2s 36A 10 维持电流稳定在 ~36A ... 20.0s — 0 10s 超时强制关闭限流 // 如果又飙了 25.0s 42A 0 积分器重新累加... 35.0s 42A 1 再次开启六、放电限流数据流总览PMM 10ms任务 RTE 数据路由 TIM16 中断(100µs) ───────────── ───────────── ──────────────── DisChrgLimitDutyCycle ────→ g_AfeIfCtrl_Cmd ──────────→ AfeIf_CurrentLimit() DisChrgLimitCycle ────→ .AfeIf_CurrentLimitDuty_pct │ .AfeIf_CurrentLimitCycle ├─ TimeCnt duty → GPIOLOW └─ TimeCnt ≥ duty → GPIOHIGH │ ▼ SH367306 CTLD 引脚 │ ▼ 放电 MOS 栅极全程无 I2C、无 SPI。PMM 计算 → 写全局变量 → ISR 读全局变量 → GPIO 翻转 → AFE 硬件响应。七、放电限流关键参数参数值定义位置PWM 频率~303HzDSG_CURRENT_LIMIT_CYCLE 33PWM 周期3.3ms33 × 100µs分辨率~3% (1/33)每步 100µs中断周期100µsTIM16调节间隔200msDSG_CURRENT_LIMIT_REGULATE_CYCLE开启延时10s积分式CURRENT_LIMIT_OPEN_START_TIME_S强制关闭10sCURRENT_LIMIT_OPEN_2_CLOSE_TIME_S死区±2A占空比范围1~32 (3%~97%)八、一个被注释掉的约束// 如果断开放电mos的占空比不能大于35%, 否则长时间运行会断电.// if(100*s_PMMOutput.DisChrgLimitDutyCycle/DSG_CURRENT_LIMIT_CYCLE 50)// {// s_PMMOutput.DisChrgLimitDutyCycle (50*DSG_CURRENT_LIMIT_CYCLE)/100;// }占空比不能太高——关断时间占比过大时负载端母线电容无法维持系统供电电压会导致 BMS 自身掉电复位。这也就是限流只能短期运行、必须超时关闭的根本原因。下篇充电限流通信协议九、为什么充电不能 PWM充电柜是一个恒流源。如果 BMS 用 PWM 切换充电 MOSFET充电电流 ▲ │ ┌──┐ ┌──┐ ┌──┐ │ │ │ │ │ │ │ MOS 导通 → 电流涌入 │ │ │ │ │ │ │ MOS 关断 → 电流强行截断 │──┘ └────┘ └────┘ └── │ └──────────────────────────→ 时间MOS 关断瞬间充电柜的电感会在输出端产生极高的感应电压V L × di/dt足以击穿 MOS 或充电柜输出电容。所以充电限流的方式是直接告诉充电柜降低输出而不是自己动手切 MOS。十、充电限流的三层结构SOP (功率预测) CHM (充电策略) FDR (故障保护) 通信 (Modbus/HD20) 充电柜 ───────────── ──────────── ──────────── ──────────────── ────── 温度×SOC 查表 → MaxAllowChrgCurr_mA ──→ CHM 输出钳位 ──→ forbidCharge? → ApplyChgCur_mA → 充电柜按指令输出 │ │ │ CHM 自己也算出 如果 forbidCharge 通过 Modbus 寄存器 ApplyChgCur_mA 直接清 0 或 HD20 协议下发10.1 第一层SOP —— 温度 × SOC 二维查表根据电芯温度和 SOC查表得最大允许充电电流SOP.c// 充电电流二维表15 个温度点 × 21 个 SOC 点 315 个数据maxAllowCurr1LookUp2D(最高温度,SOCmax,充电限流表);maxAllowCurr2LookUp2D(最低温度,SOCmax,充电限流表);MaxAllowChrgCurr_mAmin(maxAllowCurr1,maxAllowCurr2)×(实际容量/标称容量)×(SOH/1000);温度 -5°C 时直接输出 0禁止充电。10.2 第二层CHM —— 充电策略CHM 根据单体电压和温度分区间给出请求电流CHM.c温度区间单体电压请求电流0~5°C 或 60~65°C任意0.1C5~10°C 或 55~60°C❤️.55V0.25C5~10°C 或 55~60°C≥3.55V0.1C10~55°C正常❤️.0V0.25C10~55°C正常3.0~3.45V0.5C最大10~55°C正常3.45~3.55V0.25C10~55°C正常≥3.55V0.1C即将满充0°C 或 65°C—禁止充电最终由 SOP 的MaxAllowChrgCurr_mA钳位——策略算出来的值不能超过 SOP 表的上限if(ApplyChgCur_mAMaxAllowChrgCurr_mA)ApplyChgCur_mAMaxAllowChrgCurr_mA;10.3 第三层FDR —— 故障时强制清零当forbidCharge_flg true2级过流保护触发、MOS 故障等if(ForbitChgFlt_Flg!0){ApplyChgCur_mA0;ApplyChgVolt_mV0;ApplyChgEnable_flg0;}十一、电流请求如何到达充电柜CHM 最终输出的ApplyChgCur_mA写入 Modbus 保持寄存器RteDataM.c// 正常时ss_MbHoldReg[41]ApplyChgCur_mA/10;// 请求电流 (0.01A)ss_MbHoldReg[42]ApplyChgVolt_mV/1;// 请求电压 (mV)ss_MbHoldReg[43]0x55;// 命令正常充电// 故障时ss_MbHoldReg[43]0xAA;// 命令禁止充电ApplyChgCurrent_Value0;充电柜通过 Modbus RTU 或 HD20 协议轮询这些寄存器按指令调整输出电流。等效于BMS ──请输出 10A──→ 充电柜调整 PWM/LLC → 输出 10A BMS ──降为 5A ──→ 充电柜调整 PWM/LLC → 输出 5A BMS ──禁止充电 ──→ 充电柜关闭输出十二、两条路径对比总结放电限流充电限流物理手段MCU GPIO → AFE CTLD → MOS 栅极Modbus/HD20 数字指令执行层100µs 硬件中断通信协议百 ms 级PWM 频率~303Hz不适用充电柜内部调节占空比范围1~32 (3%~97%)0~MaxAllowChrgCurr_mA连续可调调节粒度3% 步长 (100µs)充电柜精度通常 0.1A限流层级PMM 状态机调节 dutySOP 查表 → CHM 策略 → FDR 钳位死区±2ACHM 策略天然分段开启延时积分式 10s即时持续时间最多 10s 强制关闭可持续直到满充或故障失败保护超时关闭恢复全功率forbidCharge 硬件关断一句话自己动手切 MOS告诉充电柜降流两者的共同点是都不是过流保护自动触发的——当前代码中过流保护Warn/Pro1/Pro2与限流是两条独立路径。Pro2 直接forbidCharge true是一刀切而非降功率。如果要在过流场景下做降功率需要在 FDR 触发 Warn/Pro1 时联动设置CurLimit_flg或调低 CHM 的电流请求值——目前版本没有这个逻辑。本文基于 STM32F0 SH367306 两轮车 BMS 量产代码分析所有代码引用均可追溯到源文件对应行号。