
1. 两串电芯的均衡为什么不能靠顺手拉平1.1 串联回路的木桶效应比想象中更直接在储能和备用电源系统里电池组绝大多数是串联拓扑两串、四串、十六串都一样——串联回路里流过每个电芯的电流完全相同但每个电芯的容量、内阻、自放电率却不可能完全一致。同一批出厂的电芯容量差个百分之一二很正常内阻差个百分之五到十五也常见更别说不同温度环境下老化速度的差异了。这种失配在串联拓扑里会被直接放大成一个木桶效应放电时容量偏小的那颗电芯先到放电截止电压保护电路一刀切整组电池立刻停止输出。这时候容量偏大的电芯其实还有余量但你已经用不上了。换句话说两串电池组的可用能量不是两颗电芯能量的平均值而是被较弱那颗死死卡住的下限。很多做消费电子出身的朋友觉得两串而已短路风险小、管理难度低随便做个电压比较就能应付。这个判断在每天充满就拔掉的小设备上或许成立但放到储能和备电场景就完全变味了。1.2 备电系统的浮充老化才是均衡要解决的头号问题备用电源和普通消费设备的电池工作模式有本质区别。消费设备是充-放-充-放高频循环而备电电池绝大多数时间挂在浮充电压上特别是储能柜、UPS这类应用电池可能连续几周甚至几个月都处在满电浮充状态只有电网波动那一刻才真正放电。问题就出在这里。假设两串电芯已经有10mV的不平衡浮充电压是按整组电压控制的比如两串磷酸铁锂浮充设定在7.0V那么电压高的那颗电芯被长期浮充在3.52V电压低的那颗只有3.48V。搁一个月看不出什么搁半年呢长期偏高浮充的那颗电芯正极材料持续处于更深的氧化状态电解液分解加速容量衰减比另一颗快容量衰减又让它的电压曲线变化更剧烈不平衡进一步扩大。这是一个典型的正反馈循环而且一旦滚起来后面靠均衡都很难完全拉回来。所以对备电系统来说均衡不是顺手做做的锦上添花而是阻止电池组在浮充岁月里加速老化的必要手段。我经手的模拟项目X里一组不作均衡的两串磷酸铁锂浮充存放一年后实测容量比带主动均衡管理的对照组低了将近8%这个差距在备电场景里就是关键负载少撑十几分钟的代价。1.3 两串系统恰恰是被动均衡最划算的场景我说这些不是为了劝大家上复杂的主动均衡。恰恰相反两串拓扑是被动均衡性价比最高的场景。原因很简单需要搬移的能量差很小。两颗电芯的SOC差异从10%拉到1%按20Ah电芯算需要转移的能量也就是约0.5×20Ah×3.2V×9%≈2.9Wh一颗100mA的泄放电阻等效工作两三个小时就能搞定。这点能量用主动均衡电路去搬移开关电容或者反激拓扑的成本和板面积开销远远超过它省下来的那点能量损耗。被动均衡的方案选型没有问题真正的分水岭在于控制逻辑。阈值定多少、什么时候允许均衡、采样时机怎么规避干扰、迟滞怎么设这些才是决定两串均衡系统在备电现场到底有没有用的关键。这篇文章就用 MP2642GR-Z 加 STM32F439ZG 这套方案把这套逻辑完整拆开。2. 硬件分工MP2642GR-Z 管感知和执行STM32F439ZG 管决策2.1 为什么不能拿 MCU 内置 ADC 直接测两串电芯先回答一个常见的疑问STM32F439ZG 本身就带三个12位ADC为什么还要在电芯和MCU之间加一颗前端芯片核心原因是共模电压问题。两串电池组以电芯2的负极为系统地电芯2的正极电压大约3V多而电芯1的负极点在这个电位之上电芯1的电压信号是悬浮在3V以上平台上的。普通MCU的ADC输入是单端参考地的直接去测电芯1要么得分压电阻拉到地分压电阻会持续消耗电芯能量而且引入不可接受的测量误差要么得自己搭高共模差分放大器成本高、温漂大、PCB面积惊人。MP2642GR-Z 这类电池前端芯片内部就是做差分采集的专门解决这种悬浮电压测量问题。另外一个理由是精度。均衡控制的判定基础是两节电芯的电压差动辄要求分辨到毫伏级。STM32F439ZG 的12位ADC配上3.3V参考满量程也就0.8mV/LSB实际噪声环境下做到3-5mV重复精度已经不错。而电池前端芯片内部通常是14到16位的ADC电压分辨率做到0.125mV/LSB级别配合内部基准常温下测量精度可以稳定到±1mV左右。对均衡这种毫伏级决策来说这个精度差距是本质性的。2.2 MP2642GR-Z 在系统里的职责边界MP2642GR-Z 作为电池前端芯片在系统里承担的是从电芯到MCU之间的感知层执行层。具体到功能块我认为可以划分为五块功能块承担的工作为什么放在前端芯片而非MCU电芯电压采集对两串电芯做差分测量共模电压高、精度要求毫伏级均衡驱动控制外部均衡MOSFET的开与关内部有驱动逻辑和定时器温度采集外接NTC热敏电阻接口信号调理贴近电芯减小引线噪声告警输出硬件比较器快速判过压/欠压不依赖软件轮询响应时间短低功耗管理休眠模式和唤醒源配置备电系统长期空载省电是关键均衡驱动这部分尤其要说明白。被动均衡的外部电路其实很简单每节电芯并联一个泄放电阻串联一个MOSFET前端芯片输出控制信号去开关这个MOSFET。放在以前这个控制信号往往要从MCU的GPIO直接拉出来但现在的方案里前端芯片内部就集成了驱动电路和均衡定时器。MCU只需要通过I2C写一个控制寄存器告诉它平衡电芯1它就自动把对应的MOSFET打开维持设定的时间再自动关断。这个硬件级的自动定时能力反而是系统可靠性的重要保障——万一MCU死机了均衡动作不会无限持续下去。2.3 STM32F439ZG 在方案里的主控定位STM32F439ZG 在这套方案里不负责测电压这种精细活它负责的是决策、记录和对外通信。具体来说运行BMS状态机根据电压、温度、充放电状态综合判断要不要均衡、均衡哪颗电芯、均衡多久存日志均衡事件、电压变化曲线、故障码全部带时间戳写进Flash做标定生产时对前端芯片的ADC做增益和失调校准校准参数保存在MCU内部对外通信通过UART/RS485或CAN把电池状态上报给储能系统的上层控制器为什么选F439而不是更便宜的F103或者其他M0核我的理由是外设冗余度和算力余量。F439是180MHz的Cortex-M4F1MB Flash加上256KB SRAM跑BMS状态机绰绰有余但真正让我在意的是它有三路I2C、多路UART和CAN其中一路I2C专门给前端芯片用一路I2C接板上EEPROM和温湿度传感器一路UART用作调试CAN留给主机通信接口之间隔离得清清楚楚不会出现总线争抢的隐患。另外F439自带硬件CRC写日志的时候顺便算个校验排查问题的时候省很多事。2.4 AFE 与主控之间的连接和数据流实际板卡上MP2642GR-Z 与 STM32F439ZG 的接口很干净I2C1SCL/SDA 接前端芯片上拉电阻选10kΩ串100Ω电阻作缓冲前端芯片的ALERT引脚接到MCU的一个EXTI中断脚过压、欠压、过温事件可以硬件打断MCU一个GPIO控制前端芯片的电源使能用于异常恢复前端芯片的NTC接口接电芯温度传感器MCU自己还可以再接一个板级NTC监测PCB温度数据流的方向是电芯电压 → 前端芯片ADC → I2C DMA → F439状态机 → 决策结果 → I2C写回 → 前端芯片驱动 → 均衡MOSFET → 泄放电流。整条链路里真正要求高实时性的只有故障告警这一条走的是ALERT硬线其余环节毫秒级延迟完全可接受。这也是这套架构的另一个优点把慢速逻辑和快速保护天然分层了。3. 均衡算法设计阈值、时机和散热预算三者缺一不可3.1 泄放电阻选型与散热预算的计算过程先给计算过程。以磷酸铁锂两串为例单颗电芯最高充电电压3.65V。我习惯把均衡电流目标定在100mA左右——这个值不能太小否则在浮充末期几十毫安的充电电流面前起不到拉低强电芯电压的作用也不能太大否则泄放电阻的体积和发热就压不住了。按欧姆定律R 3.65V / 0.1A 36.5Ω取标称值39Ω。连续均衡时的功耗P 3.65² / 39 ≈ 0.34W这个功率看似不大但注意均衡电阻旁边往往就是电池仓或者密闭机箱散热条件并不好。我的建议是直接用1W额定功率的电阻并且在PCB上给电阻焊盘留足铜箔面积至少12mm²的铺铜散热区。如果用的是三元锂电芯满充电压4.2V同样算下来R42Ω取标称43Ω功耗约0.41W散热要求更紧一些。还有一种思路是降额使用把均衡做成50%占空比开30秒、停30秒。前端芯片的均衡定时器就可以干这个事不需要MCU额外干预。占空比控制的收益很直观平均功耗减半到约0.17W峰值热冲击却不变所以电阻额定功率还是按连续功耗来选但PCB温升可以显著降低。实测下来50%占空比时均衡电阻表面温度比连续均衡低12-15℃。3.2 触发阈值、停止阈值和迟滞怎么定很多初版设计的做法是设一个阈值比如电压差大于15mV就开始均衡小于15mV就停止。这个做法有个致命问题没有迟滞。由于电芯端电压本身有噪声和微小波动当差值恰好在15mV附近抖动时系统就会在均衡开启和均衡关闭之间来回横跳MOSFET频繁开关既影响测量又缩短器件寿命。我的标定逻辑是启动阈值静置状态下电芯电压差大于20mV才允许开启均衡停止阈值电压差小于8mV并连续维持3个判定周期才关闭均衡迟滞区间就是8mV到20mV之间在这个区间内维持上一次的状态不变为什么阈值要卡得相对保守因为电芯端电压的差异不完全是SOC差异的反映内阻不同、极化程度不同也会造成几毫伏的压差。把启动阈值设到20mV可以保证启动均衡时确实存在有意义的SOC偏差避免频繁做无用功。而8mV的停止阈值对应的是在浮充状态下电芯SOC差异已经收敛到很小的程度严格控制最后一公里防止过度均衡导致本来正常的那颗电芯被反噬。3.3 均衡时机为什么必须卡在充电尾段或浮充期这是我踩过之后觉得最值得写的一个点。被动均衡的本质是给电压高的电芯并联一个泄放电阻让它在充电时少充进去一点。这里有一个关键的物理约束泄放电流必须大于充电电流均衡才真正有效。想象一下充电器正以2A恒流往电池组里灌电此时你打开一颗电芯的100mA均衡泄放电阻。强电芯的净充电电流依然有1.9A它照样在快速上升均衡那点电流根本拉不住它。所以在恒流大电流充电阶段做均衡几乎是空转。真正有效的场景是恒压末期或者浮充期此时充电电流已经掉到几十毫安甚至更小100mA的泄放电流能够反向压制强电芯的电压爬升让弱电芯有机会慢慢追上来。所以算法里的使能条件不应该是简单的电压差大就均衡而应该是电池组处于充电模式且充电电流小于某个阈值比如C/20或者整组电压已经进入恒压平台区电芯电压高于某个下限比如3.2V防止均衡把本来状态差的那颗电芯拖入过放电芯温度在0-45℃范围内前端芯片无告警、通信正常这套条件用与逻辑组合任何一条不满足就暂停均衡并记录原因。对于备电这种长期挂在浮充上的系统我还加了周期均衡窗口的概念每隔24小时主动触发一次均衡流程持续最长4小时防止电池组长期无人过问时悄悄失衡。3.4 均衡状态下的保护联动设计均衡不是无条件的动作它本身就是一种对电池的干预所以必须有保护兜底。我在设计里做了三层联动第一层是前端芯片的硬件告警。过压、欠压、过温这些事件通过ALERT引脚直接拉断MCU中断MCU在中断服务里立即关闭均衡控制位。这里强调立即是因为备电系统一旦出现异常多几毫秒响应都可能是安全事故。第二层是MCU自己的判定。即使前端芯片没报错MCU每轮采样都会检查温度是否超过45℃、被均衡的电芯电压是否异常异常跌落。温度高就进入SUSPEND状态暂停均衡等温度回落再继续电压异常跌落则直接置FAULT锁死只能人工复位。第三层是硬件看门狗和均衡超时保护。MCU的独立看门狗保证主程序卡死时系统能自动复位前端芯片的均衡定时器则保证即使MCU复位过程中控制位残留均衡也会在规定时间后自动关断。另外我还设了一个每24小时内累计均衡不超过6小时的软件上限防止长时间忘关把电芯过度泄放。三层保护层层叠加这套均衡系统才敢说在无人值守的备电场景里可以长期稳定运行。4. 软件实现从电压采样到均衡退出的完整链路4.1 主循环加定时器的裸机框架为什么够用均衡控制的时间尺度是秒级乃至分钟级——电芯的极化弛豫要几十毫秒热量累积要几百秒SOC变化则是更慢的过程。所以不需要上实时操作系统也不需要复杂的中断优先级设计。我的实现是裸机加定时器tick一个10ms的心跳驱动前端芯片的数据采集动作一个1s的节拍驱动BMS状态机一个可配置的慢速任务默认60s驱动均衡策略评估。这看起来很简单但有一个细节必须处理好I2C读数据用DMA。前端芯片每次要读两路电芯电压、一路温度、一个状态字数据量不大但如果用阻塞式I2C在主循环里读一旦总线上有从机拉长时钟主程序就会卡在那里。我的做法是把I2C接收配置成DMA传输10ms任务里发起读请求1s任务里检查DMA完成标志再取数。这样即使I2C偶尔被拉长也只是数据晚到不会堵死整个状态机。4.2 均衡状态机的状态定义与迁移条件状态机的设计直接决定系统的稳定性和可维护性。我用的是六个状态状态含义主要动作IDLE空闲低功耗等待周期唤醒采样SAMPLE采样读取电压、温度、充电状态并滤波EVALUATE判定计算电压差决定是否均衡BALANCE均衡执行写均衡控制寄存器周期复判SUSPEND暂停温度过高或通信异常等待恢复FAULT故障锁定过压、欠压、看门狗复位需人工干预迁移条件用伪代码表达比较直观static bms_state_t state BMS_IDLE; void bms_task_1s(void) { switch (state) { case BMS_IDLE: /* 每个周期都触发一次采样数据就绪后进入判定 */ bms_start_afe_conversion(); state BMS_SAMPLE; break; case BMS_SAMPLE: /* DMA完成且连续N次采样有效才能进入判定 */ if (bms_data_ready()) { bms_update_moving_average(); state BMS_EVALUATE; } break; case BMS_EVALUATE: bms_decide_balancing(); if (bms_balance_required()) { state BMS_BALANCE; } else { state BMS_IDLE; } break; case BMS_BALANCE: /* 执行均衡周期周期结束重新判定 */ bms_run_one_balance_cycle(); if (bms_balance_finished() || bms_balance_abort()) { state BMS_EVALUATE; } break; default: break; } }这里有个隐形规则值得强调从BALANCE回到EVALUATE而不是直接回IDLE。因为均衡过程中电压差是在缓慢变化的每完成一轮均衡比如60s定时周期都要重新采样、重新判定才能决定是继续均衡、切换均衡通道还是结束。如果均衡一轮就回IDLE还要再等下一个慢速周期才能重新评估白白浪费系统的响应能力。4.3 AFE寄存器读写与均衡控制的时序MP2642GR-Z 的寄存器访问走标准I2C协议。以我常用的寄存器映射习惯为例实际地址以官方手册为准这里只讲逻辑#define AFE_REG_STATUS 0x00 /* 状态字故障标志 */ #define AFE_REG_CELL1_VOLT 0x04 /* 电芯1电压16位 */ #define AFE_REG_CELL2_VOLT 0x06 /* 电芯2电压16位 */ #define AFE_REG_BAL_CTRL 0x10 /* 均衡控制bit0电芯1bit1电芯2 */ #define AFE_REG_BAL_TIMER 0x11 /* 均衡定时器配置 */读电压的流程是发起寄存器读请求 → DMA接收4字节 → 拼成两个16位原始值 → 乘以电压LSB系数得到实际电压值。电压LSB系数取决于前端芯片的基准和分压配置一般12位到16位不等标定时会确认。写均衡控制位的流程更讲究。不仅要写还要回读校验uint8_t bal_ctrl 0; if (cell1_v cell2_v BALANCE_START_MV) { bal_ctrl 0x01; /* 均衡电压高的电芯1 */ } else if (cell2_v cell1_v BALANCE_START_MV) { bal_ctrl 0x02; /* 均衡电压高的电芯2 */ } afe_write_reg(AFE_REG_BAL_CTRL, bal_ctrl); /* 回读校验确保AFE实际接受的配置与意图一致 */ uint8_t readback afe_read_reg(AFE_REG_BAL_CTRL); if (readback ! bal_ctrl) { bms_enter_fault(BMS_FAULT_COMM); }回读校验这个习惯是我在一次现场排查中养成的——当时发现前端芯片在某些电气噪声环境下会丢寄存器写入如果不回读你以为均衡开着实际上根本没动作整组电池就在那干烧时间。加上回读之后任何一次配置丢失都能立刻被发现并进入故障处理流程。4.4 生产标定与精度验证流程均衡判定依赖绝对电压精度所以前端芯片的ADC标定不能省。我的生产流程是两步第一步是两点校准。用精密电压源分别在2.5V和4.2V两个点给到两路电芯电压输入读取ADC原始码算出每路的增益和失调系数存入MCU内部Flash和校准数据一起存一个CRC校验值。状态机每次做电压换算时都用校准系数修正。第二步是交叉验证。备电系统上电自检时MCU用自己的ADC通过分压电阻读一次整组电压再和前端芯片报上来的两颗电芯电压之和做对比。两者差超过20mV就报电压链路异常因为这通常意味着前端芯片的采样链路出现了漂移或者某个输入通道接触不良。这个实测对比非常便宜但能拦住大部分因为连接器松动引发的假失衡问题。5. 实测数据与四个典型踩坑记录5.1 一组有代表性的均衡过程数据为了验证这套逻辑我用两节容量差异约5%的磷酸铁锂电芯做了一组测试。先把两节电芯分别充到不同SOC——一颗约80%一颗约60%然后串联起来静置半小时。初始静置电压差大约180mV这可不算小。接着接上7.2V浮充电源启动系统让均衡逻辑自己跑。整个过程的电压差变化如下时间点电压差均衡通道电芯温度上电静置180mV未开启28℃进入CV浮充45mV开启电芯131℃均衡30分钟32mV开启电芯132℃均衡60分钟21mV开启电芯131℃均衡90分钟12mV停止30℃有几个现象需要解释。第一充电器2A恒流阶段电压差基本没变化等电流掉到恒压尾段才明显收敛——这印证了前面说的均衡窗口理论。第二电压差的下降不是单调的中间有时会反弹两三毫伏这是电芯极化的弛豫效应造成的不是逻辑出问题。第三最后10mV的收敛速度明显放缓从20mV到12mV足足花了将近半小时。这是因为磷酸铁锂的电芯在SOC接近满充时电压曲线非常陡SOC差一点点就会反映成较大的电压差而均衡电流是恒定的所以越到后面电压差下降需要的时间越长。知道了这个特征就不会在调试时误以为均衡失效。5.2 坑1均衡开启瞬间读到的电压是假数据最初的固件里我让状态机在BALANCE状态下每10秒读一次电芯电压做监控。结果发现一个诡异的现象均衡开启时被均衡那颗电芯的读数反而升高了30到50mV。原因说穿了不值钱均衡MOSFET导通时100mA的泄放电流流过电芯内阻和采样路径电阻在测量端子上产生了一个压降。这个压降叠加进ADC的读数里导致开着均衡时电压越读越高。如果拿这个假数据去做判定系统会认为被均衡的电芯一直偏高永远关不掉均衡甚至切换均衡通道整个控制就乱套了。解决办法是给测量动作留出安静窗口需要做判定时先关闭均衡等10ms让信号链路稳定再读电压算完差值后再决定是否重新开启均衡。在实际代码里这体现为BALANCE状态下的一个子状态暂停泄放 → 延时 → 采样 → 恢复泄放。这个10ms的等待对均衡这种慢过程来说毫无成本但换来了干净的测量数据。5.3 坑2阈值不设迟滞均衡来回震荡这个坑前面在算法章节已经铺垫过这里说实测现象。最早版本用单一阈值15mV判定实测时用逻辑分析仪抓I2C看到均衡控制寄存器在0x00和0x01之间每秒跳变一次持续了将近两分钟。原因就是电压噪声叠加在临界阈值附近系统陷入乒乓振荡。修复方式就是迟滞加去抖启动阈值20mV停止阈值8mV并且停止条件要求连续三个判定周期都满足才真正停止。这里顺带说一个经验迟滞区间不要选得太窄8-20mV这个区间对两串系统来说是够用的。改完之后用逻辑分析仪复查均衡控制寄存器稳定多了一个均衡周期内只会切换一次。5.4 坑3均衡回路和采样回路共用走线导致测量偏移这个坑在硬件改版前一直困扰我。现象是均衡电流一大两节电芯的测量值都会产生固定偏差尤其是电芯2靠近系统地的那颗偏差最大时有10-15mV。排查到最后才发现是PCB布局问题——电芯2的采样线走在均衡泄放电流的回流路径旁边100mA的脉冲电流在采样地线上产生了压降直接污染了参考电位。这类问题的解法在业内叫开尔文接法采样线必须从电芯端子上独立引出单独走线回到前端芯片的采样地不能和功率回流路径共用铜箔。同时采样引脚靠近芯片的位置要加RC滤波100Ω串阻加上100nF对地电容截止频率约16kHz足够滤掉均衡开关产生的毛刺又不影响毫秒级响应。改版之后我在均衡开关瞬间用示波器测量前端芯片采样引脚的波形噪声从15mV降到了2mV以内。这个教训说明即使两串系统的功率路径看起来很简单采样和功率的隔离依然是不可妥协的底线。5.5 坑4系统掉电后I2C总线挂死储能备电系统最常遇到的异常就是掉电和上电而这恰恰最容易暴露通信链路的问题。一次掉电测试中前端芯片的供电先于MCU跌落等到系统恢复时I2C总线卡死在SCL低电平整个BMS状态机无法启动。这是典型的从机失去电源主机还在总线被拉死问题。两层解法第一前端芯片的电源由一个MCU控制的负载开关供电检测到I2C通信连续超时后MCU直接断开负载开关再重新上电让前端芯片完全复位第二MCU的I2C引脚保持开漏输出并且总线恢复时先做一次9个时钟脉冲的释放操作这在很多芯片的参考手册里都有描述目的是把卡在中间态的从机状态机踢出来。还有一个小细节I2C上拉电阻应当接到前端芯片的供电轨而不是MCU的3.3V轨。如果两个电源域不一致掉电时序不同步时可能造成电流倒灌。我在改版时统一了电源域并在前端芯片的电源引脚前加了RC滤波避开上电瞬间的毛刺。5.6 调试工具和日志规划最后聊点调试手段。这套系统里我强烈建议在I2C总线上挂一个带解码功能的逻辑分析仪开机阶段重点看三件事前端芯片是否正常应答、寄存器读写地址是否正确、均衡控制位写入后回读值是否一致。这三个点验证通过硬件链路基本就稳了。软件层面MCU的日志要记录这些字段时间戳、电芯1电压、电芯2电压、电压差、均衡状态、均衡累计时间、温度、故障字。备电系统往往部署在无人值守的现场出问题时能追溯的就只有日志。我把这些记录写进F439的Flash带CRC校验。实测中最有用的反而不是那些正常运行的记录而是故障发生前十分钟的预故障段——从这里能看出均衡策略是不是在故障前做了错误动作。最后补充一个现场经验如果电阻、阈值、时机都设置正确但电压差始终压不下去优先怀疑的不是算法而是电芯本身。用万用表实测一下每颗电芯的电压和前端芯片读回来的数据比对如果一致那就是电芯内部微短路或者容量衰减到了无法靠均衡修复的程度。这时候该做的是更换电芯而不是继续调均衡参数。我自己做完这套方案后最大的体会是两串均衡看起来是个小功能但真正决定它好不好的不是芯片多高级而是阈值、迟滞、采样时机、保护联动这些看起来不起眼的细节。把这些细节做扎实了比盲目堆硬件更能解决现场问题。