1. 从一次备用电源故障说起:为什么2S电池要专门做均衡
两节3.2V磷酸铁锂电芯串联成6.4V电池包,很多工程师第一反应是"这有什么好做的"。但你要是做过储能和备用电源,就知道这种2S包恰恰最容易翻车:充电时上管被顶到3.62V,下管还在3.47V,可用容量悄悄缩水。我解决这个问题的组合是均衡芯片MP2642GR-Z加主控PIC32MX470F512H,前者负责把多出来的电从高电压电芯搬到低电压电芯,后者负责采样、保护和通信。
这个故障的机理很简单,但值得仔细说。两个电芯串联,同一个充电电流必然同时流过它们。如果两节电芯的容量、内阻、自放电率不完全一样——哪怕出厂时配对得很齐——经过几百次充放电和长期浮充,充电时电压上升快的永远是同一节,放电时电压跌落快的也永远是同一节。于是高电压那节总是先到充电截止电压,充电器以为满了,实际另一节还差一截;放电时弱的那节又先到放电截止,强的那节还有大量剩余。整个电池包的有效容量被最弱一节锁死,这就是所谓的"短板效应"。
在备用电源这个场景里,问题会被放大。备用电源大多数时间处于浮充和待机状态,充电电流很小但持续时间很长,长期小电流充电会让电压高的一节持续过充;放电又是短时大电流,热量和压差反复叠加,电芯老化差异会越拉越大。储能系统则是另一面:长期浅充浅放,SOC不对中的电池包容量越用越少,最后整包效率很难看。所以2S不是"不需要均衡",而是"必须在有限成本里把均衡做好"。
均衡的实现方式,我的选择是主动均衡而不是被动均衡。被动均衡就是把高电压那节多余的能量通过电阻放掉,电路简单,但能量变成热量,在储能和备用电源这种对效率敏感的场合很浪费;主动均衡是把高电芯的电荷搬移到低电芯,能量回收利用。MP2642GR-Z这颗芯片就是干这个活的,它在电池包的B+、中点、B-三个节点之间建立受控的电荷搬移通路,PIC32MX470F512H只需要告诉它"什么时候搬、搬多久",不需要自己去搭半桥驱动和电流检测。分工清晰,安全性也好得多。
之所以选PIC32MX470F512H而不是一颗8位单片机,是因为这颗芯片有512KB Flash和128KB RAM,跑完整的BMS状态机、SOC积分和CAN通信完全不吃力;同时它片上的ADC虽然精度不算惊艳,但配合校准和过采样,做2S均衡足够了。后面我会从硬件架构、固件状态机、实测排坑三个角度完整还原这套方案,所有参数都是我自己调过的参考值,抄作业时记得按你的电芯和负载重新算一遍。
2. MP2642GR-Z与PIC32MX470F512H的分工:这套硬件架构是怎么搭的
2.1 电池包节点与均衡芯片的驱动关系
整套系统的核心节点只有三个:B+、MID、B-。B+是电芯1正极,MID是电芯1负极与电芯2正极的公共点,B-是电芯2负极。MP2642GR-Z跨接在这三个节点上,PIC32给一个使能信号,它就在两节电芯之间做电荷搬移。下面这张表是我板子上的信号分配,管脚号以你用的封装为准,逻辑关系是通用的:
| 信号 | 来源/去向 | PIC32侧功能 | 说明 |
|---|---|---|---|
| CELL1_V | B+与MID分压 | ADC通道0 | 采样电芯1电压 |
| CELL2_V | MID与B-分压 | ADC通道1 | 采样电芯2电压 |
| PACK_I | 采样电阻+差分放大 | ADC通道2 | 充放电电流 |
| NTC1/NTC2 | 紧贴电芯的热敏电阻 | ADC通道3/4 | 两节电芯温度 |
| BAL_EN | PIC32 GPIO → MP2642使能 | 推挽输出 | 均衡启动/停止 |
| BAL_STAT | MP2642状态输出 | GPIO输入 | 均衡状态或故障标志 |
| CHG_FET/DSG_FET | PIC32 GPIO → 栅极驱动 | 推挽输出 | 充电/放电MOS管控制 |
这里最容易被忽略的是MID节点。均衡工作时,MID上的电流方向不是固定不变的,高频开关动作会把这里变成一个干扰源。我第一版布局就把MID走线拉得很远,结果均衡一开,ADC读数整体漂移。后来把MP2642放在离电池连接器很近的位置,MID功率走线尽量短而粗,模拟采样线单独走,问题才消停。
2.2 电芯电压采样为什么一定要用独立走线
均衡判断的本质是电压比较,而电压采样的误差直接决定均衡会不会误动作。最典型的错误是采样线和均衡电流共用一段走线。假设PCB走线加连接器触点总共50mΩ,均衡电流0.6A,那在这段走线上就产生30mV压降。30mV是什么概念?磷酸铁锂充满后静置半小时,电压回落都可能不止这个数。也就是说,一次正常的均衡动作会被误读成"电压差还在",均衡永远退不出来。
我的做法是四线制感测的思路:功率路径走B+、MID、B-,采样路径从电芯极耳附近单独引出两条细线,在PIC32的ADC输入端加100Ω加100nF的RC低通,带宽大约16kHz,正好滤掉均衡开关的纹波,又不会拖慢电压响应。分压电阻我用的是低温漂陶瓷贴片,分成两段串联,避免单颗电阻的电压系数在高电压下引入额外偏差。分压比要按MCU的ADC参考电压来算:参考电压3.3V,单节磷酸铁锂满电约3.65V,我取1.5:1左右的分压比,把满电电压压到2.4V附近,留出裕量;如果你用的是三元锂,上限要按4.3V重新算。
提示:分压电阻用0.5%甚至0.1%精度的,批量一致性依然不够,最终还是要靠软件校准。别指望电阻精度能救你。
2.3 电流采样、温度采样与保护通道
电流采样我用的是合金采样电阻加差分放大器。以10Ah电池包为例,最大持续放电电流按20A设计,采样电阻取5mΩ,20A时压降100mV,差分放大器增益30倍,输出3V,对应ADC满量程3.3V还有余量。放大器的输出同样加RC滤波,带宽压到几kHz,避免开关噪声叠加进电流环。
温度采样用10k NTC,和固定电阻组成分压网络,接到ADC。NTC的位置比精度更重要:要紧贴电芯表面,别贴在电池架上,否则测到的是空气温度。两节电芯各放一个,均衡长时间运行时,两节电芯的温差也会影响电压判据,这一点后面讲状态机时还会提到。
保护MOS管我放在低边,充电和放电各一组,用专门的栅极驱动IC驱动,PIC32的GPIO不直接推大负载。原因很现实:MOS管栅极电容不小,直接用GPIO驱动,开关瞬间很容易拉低MCU电源,严重的直接复位。栅极回路加10Ω串联电阻,栅源之间加一个100k下拉,防止上电瞬间误导通。这个细节在第一版板上吃过亏,后面排坑部分会展开说。
2.4 把均衡逻辑交给MP2642,MCU不要自己搭H桥
有人可能会问,既然PIC32有PWM、有ADC、有GPIO,为什么不自己用电感和MOS管搭一个双向升降压去搬电?我第一版确实这么想过,后来放弃了。自己搭cell-to-cell均衡,意味着要处理死区时间、同步整流、电流环、过流保护、EMI,这些问题任何一个没做好,均衡效率都会很难看,甚至烧管子。MP2642GR-Z把这些集成进去,MCU只需要控制使能和读取状态,这在安全上有一个额外的好处:即使MCU死机,MP2642也会根据内部判断停止搬移,不会一直把某一节电芯抽到过放。系统设计里,安全功能尽量下放到专用芯片,主控只做决策,这是我一直坚持的原则。
3. 跑起来的关键:ADC采样、校准与均衡状态机
3.1 ADC过采样与两点校准,把5mV级误差压下去
PIC32MX470F512H的ADC是10位分辨率。按我1.5:1的分压比折算,采样读数在电芯端的LSB大约等于5mV。单次ADC读数的意义其实很有限,满量程1024级,随机噪声稍大一点,一次读数就能偏好几个字。我最后用的是16次采样去掉最大最小再平均的方案,效果比纯滑动平均好很多,因为它对脉冲噪声天生免疫。
static uint16_t adc_sample_filtered(int ch) { uint16_t raw[16]; uint16_t min = 0xFFFF, max = 0; uint32_t sum = 0; for (int i = 0; i < 16; i++) { raw[i] = adc_read(ch); if (raw[i] < min) min = raw[i]; if (raw[i] > max) max = raw[i]; sum += raw[i]; } sum -= min + max; return (uint16_t)(sum / 14); }滤波解决的是随机噪声,系统误差要靠校准。分压电阻本身的容差、ADC参考电压的批次差异,叠加起来能到几十mV,比均衡阈值还大。所以我做了两点校准:用精密电压源分别在2.5V和3.4V两个点注入B+与MID之间,读出实际ADC值与理论值的偏移和增益,把校准系数写进外部EEPROM。换板子不换固件,每块板子出厂时烧一遍系数。
提示:校准系数一定要存EEPROM或Flash的独立区域,别和业务逻辑代码混在一起,否则每次固件升级都可能把系数冲掉。
3.2 均衡状态机的触发、滞回与充电阶段配合
均衡不能只看电压差一个条件就启动,我把整个控制器做成一个状态机:IDLE、CHARGING、DISCHARGING、BALANCING、FAULT。均衡只允许在浮充和待机状态启动,并且要满足电流条件,原因是大电流充放电时电芯端电压包含内阻压降,反映的不是真实开路电压,这时候看电压差就是自欺欺人。
具体判据我这里给一组参考值:电压差超过40mV且持续2秒以上才触发均衡;目标是把电压差压到15mV以下关闭;关闭之后再要触发,必须等电压差重新超过25mV。这里故意做了滞回,防止均衡在临界点反复启停。触发均衡的时间也需要去抖,我用一个软件计时器,去抖时间设成2秒,示波器上能看到均衡电流不是一抖一抖的方波,而是平滑的启动包络。
typedef enum { STATE_IDLE, STATE_CHARGING, STATE_DISCHARGING, STATE_BALANCING, STATE_FAULT } bms_state_t; bms_state_t bms_tick(void) { float v1 = read_cell_voltage(0); float v2 = read_cell_voltage(1); float i = read_pack_current(); float t = read_temperature(0); if (check_protections(v1, v2, i, t) == true) { balancer_enable(false); return STATE_FAULT; } static uint16_t debounce = 0; float dv = v1 - v2; bool need_balance = false; if (charger_in_cv_stage() || charger_in_float_stage()) { if (fabsf(i) < 0.05f * CELL_CAPACITY) { if (dv > 0.040f || dv < -0.040f) { need_balance = true; } } } if (need_balance) { if (debounce < 100) debounce++; if (debounce >= 100) balancer_enable(true); } else { if (fabsf(dv) < 0.015f) balancer_enable(false); debounce = 0; } return balancer_is_on() ? STATE_BALANCING : STATE_IDLE; }注意一个细节:均衡开启后,如果MP2642的搬移电流比较大,低电芯的电压会缓慢上升,高电芯的电压会缓慢下降,两者可能在某一点交叉。这时候如果只看瞬时电压差,状态机可能提前关闭均衡。所以我额外加了一个最小均衡时长,比如每次至少运行5分钟,除非触发保护,否则不中途退出。这个参数对磷酸铁锂尤其重要,因为它的OCV平台太平,电压交叉不代表真正平衡了。
3.3 保护逻辑与SOC估算怎么和均衡配合
保护逻辑和均衡状态机共用同一组采样数据。每节电芯的过压、欠压、过流、过温都单独设阈值,并且各有不同的恢复方式:过压和过流需要锁存,人工或上位机确认后才恢复;欠温和过温可以自动恢复,但要留足够大的滞回防止振荡。所有保护动作都优先于均衡,也就是说,一旦进入FAULT,均衡必须立刻关闭,不能一边保护一边搬电。
SOC估算我用了最简单的库仑积分加开路电压修正。每10ms累计一次电流和时间,得到安时数;系统静止超过30分钟后,用OCV查表修正一次。这里有个容易忽视的问题:均衡时电荷是从高电芯内部搬到低电芯内部,包外电流没有变化,所以单纯对整包做库仑积分不会错,但如果你想分别估算两节电芯各自的SOC,就必须把均衡电流乘以时间,从高电芯扣掉、加回低电芯。否则均衡结束那一刻,你会看到低电芯的SOC突然跳变,数据很假。我是在MP2642的使能回路里串联了一个小采样电阻,用PIC32的另一个ADC通道实时读均衡电流,精度不需要多高,但趋势不能错。
4. 实测数据与排坑实录:哪些坑我踩了,值多少代价
4.1 三组实测结果
实验室里我特意找了两节容量有5%偏差的磷酸铁锂电芯,其中一节是从退役电池里拆的,内阻比另一节大约15%。测试条件:10Ah标称容量,0.5C充电,0.5C放电,浮充电压按两节3.6V设置。结果如下:
| 测试场景 | 处理前状态 | 处理后状态 | 均衡用时 |
|---|---|---|---|
| 常态充放电循环 | 充满末端ΔV=45mV,可用容量9.2Ah | 充满后端ΔV=9mV,可用容量回到9.9Ah | 约28分钟 |
| 搁置两周后重新上电 | ΔV=58mV | 触发均衡后ΔV=12mV | 约35分钟 |
| 长期浮充72小时 | 上节持续偏高,最高到3.65V | 浮充期间电压差被限制在10mV以内 | 间歇启停 |
第一组数据最有说服力:同样的电池包,单纯因为不均衡,可用容量就掉了0.7Ah,这是实打实的客户投诉源头。均衡开启后,容量基本恢复了。但我也要泼一盆冷水:如果电芯容量偏差超过10%,均衡是救不回来的,属于治标不治本,正确做法是先换电芯再谈均衡。
4.2 ADC读数随均衡启停跳变的坑
第一次联调的时候,我遇到一个很头疼的问题:均衡一启动,PIC32读到的电芯电压瞬间跳了大约8mV,均衡还没干活,判断数据先错了。排查过程比较典型。先用示波器量B+、MID、B-三个节点,确实在均衡启动后有高频纹波;再用示波器量MCU的VDD和GND,发现GND上有大约50mV的尖刺,频率和均衡开关一致。原因很简单:均衡电流的返回路径和模拟采样的参考地共用了一段PCB走线,开关电流在地回路产生了压降,ADC的地被抬高了。
修复方式是把功率地和模拟地彻底分开,单点汇接,所有模拟信号在ADC输入端加RC滤波。同时我把ADC的采样时序错开到均衡PWM的某个固定相位,让每次采样都落在纹波的低点。这两步做完,均衡启停造成的读数漂移从8mV降到了2mV以内,这个量级对均衡判断已经可以忽略。
4.3 MOSFET驱动引起的MCU复位
另一个坑是放电MOS管一开通,MCU就复位。最初板子用GPIO直接推栅极,栅极电容大,开通瞬间需要很大的瞬态电流,把这个电流尖峰直接耦合进了3.3V电源轨,MCU掉电复位。这个问题的本质是驱动强度不够,而不是MCU太弱。
解决分三步:GPIO和栅极之间串10Ω电阻,限制瞬态电流;栅源之间加100k下拉,防止误开通;MCU电源引脚旁边放一个100nF加10uF的组合去耦,电源走线加宽。后来我把两个低边MOS管交给专门的栅极驱动IC,GPIO只输出开关逻辑,彻底和功率级隔离。这个经验我写进自己的设计Checklist:凡是MCU直接驱动感性或容性负载,都要重新审视电源和地。
4.4 "均衡一直不启动"的排查链路
第三版固件调试时,客户现场反馈:电压差已经45mV了,均衡就是不动作。我第一反应是使能信号没输出,用逻辑分析仪一抓,BAL_EN一直在跳,说明状态机根本没进BALANCING。再查判据,发现卡在电流条件上:充电器处于浮充阶段,但充电电流还有0.8A,对应0.08C,而我的条件是小于0.05C才允许均衡,所以永远触发不了。原因不是硬件坏,是边界条件定得太死。浮充末期电流本来就是缓慢下降的,等0.05C可能要等很久。
我后来把判据改成了双通道:充电电流小于0.05C持续30秒,或者CV阶段维持超过60分钟强制进入均衡。现场老电池充电器压差大,但60分钟后均衡一定能启动。这个改动不大,却解决了"看似没问题但就是不动"的尴尬。这类问题在文档里很难写清楚,只有跑现场才会碰到。
5. 往更大系统扩展前,我整理的三条经验
5.1 阈值要跟着电芯化学体系走
磷酸铁锂的OCV曲线在中段非常平,3.30V到3.40V之间可能对应了10%以上的SOC差,所以电压差小不代表真的平衡。我的经验是:磷酸铁锂的均衡触发阈值要放大,建议电压差超过50mV再动,而且更多依赖安时积分来修正;三元锂OCV陡一些,20mV以上就可以考虑触发,滞回可以设在10mV左右。这些数值不是金科玉律,但方向是对的:判断依据最好是SOC差,而不是单纯的电压差,电压只是触发条件。
5.2 布局和热,决定均衡能不能长期跑
均衡电流我最终调在0.6A左右。这个电流下,MP2642的发热不算大,但PCB上必须给出足够的铜皮散热。芯片下方的散热焊盘要打散热过孔到内层地平面,否则长时间工作温度一高,均衡效率会明显下降。另外,B+、MID、B-三个节点之间建议各放一颗100nF陶瓷电容,把均衡开关的纹波就地钳住,不要让高频分量在功率线上跑。MID是高频跳变点,采样线一定要避开它,这个在2.2节已经说过,这里再强调一次,因为我在第二版板上又踩了一遍。
5.3 通信、日志与固件升级
储能和备用电源系统通常有一个上位机或系统控制器,PIC32通过CAN把电芯电压、电流、温度、均衡状态和SOC定期上报。UART口保留给调试和日志,日志用环形缓冲存内存,再异步写进外部Flash,方便异常断电后回看。均衡阈值、温度阈值、电压差滞回这类参数做成EEPROM配置项,现场通过CAN指令在线修改,不需要重新烧固件。
我个人建议在量产前把校准系数、电池序列号、电芯配对信息都写进EEPROM,这样换板子、换电芯时,固件能自动识别参数,省掉大量现场调试时间。
这套板子我前后跑了两个多月的循环和浮充,最深的体会是:均衡不是救一块已经坏掉的电池,而是在止损。它让本来只有5%容量偏差的两节电芯,在系统里一直保持步调一致,直到寿命终点。最后分享一个小技巧:调试状态机时,先用一个可调电阻箱代替其中一节电芯模拟失衡,比真实电池更容易复现问题,也更容易验证你的状态机有没有写错。等状态机逻辑稳定了,再换上真实电芯做全流程验证,整个调试周期能缩短一半。