电池续航这事儿,在MTK平台上做过量产的兄弟都懂,参数表上写的5000mAh,用户拿到手用两天就开始骂"虚标"。问题往往不在电芯本身,而在于放电曲线和库仑计这两块没调透。我前后跟过几个MTK功能机和智能机项目,从Android 8.1一路做到Android 13,踩过的坑足够写一本小册子。这篇就把放电曲线拟合、库仑值校准、以及那些文档里不会写的实操细节,按我自己的调试顺序捋一遍,给正在被续航数据折磨的同行一个可复现的参考。
1. 先搞清楚MTK电池管理这套东西到底怎么运转
1.1 从电芯到百分比:一条数据链上的四个角色
很多人一上来就改battery_profile.txt,改完发现百分比还是跳,原因是他没搞清楚MTK这套电池管理的数据流。整条链路大致是这样:电芯电压经过Fuel Gauge IC(MTK平台常见的是内置在PMIC里的FG模块,或者外挂的BQ系列、CW系列)采样,得到OCV(开路电压)和库仑积分值;这些原始数据交给charger IC驱动(mtk_charger.c、mtk_switch_charging.c这类);驱动再往上抛给Android Health HAL(Android 8以后是health@2.0或health@2.1,Android 11之后是android.hardware.healthAIDL);最后HAL把capacity、voltage_now、current_now这些字段喂给BatteryService,SystemUI才画出那个百分比。
关键点在于:百分比不是电压直接换算的。MTK的FG算法内部维护一张放电曲线表(OCV-SOC对照表),用开路电压查表得到SOC,再用库仑积分做动态修正。所以你会看到两个概念——电压法和库仑法,实际量产是两者融合。只调电压表,低电量段会跳;只调库仑,长期累积误差会让100%撑不住。
1.2 为什么MTK的放电曲线比高通更难伺候
MTK平台和高通平台在电池管理上的差异,做过双平台的人最有发言权。高通的FG通常在PMIC外部独立,算法封装得比较死,你能动的就是几个profile参数;MTK这边,尤其是中低端平台,FG模块和charger耦合更紧,battery_profile、gm_3d、discharge_temperature这些表都要自己填,灵活度高但坑也多。
更麻烦的是MTK的温度补偿。锂电池在不同温度下放电曲线差异巨大,0度和25度下同一颗电芯的截止电压能差200mV以上。MTK的FG算法里有一张discharge_temperature表,按温度区间分段补偿。如果你只测了常温曲线就量产,北方用户冬天就会遇到"20%直接关机"的投诉。这一点我在一个东北客户的项目上吃过亏,返修率一度到3%,后来补测了-10度、0度、45度三档曲线才压下去。
1.3 库仑计校准到底在校准什么
库仑计(Coulomb Counter)本质是一个高精度ADC,测量流过检流电阻的电流并对时间积分,得到电荷量。它的误差来源主要有三个:检流电阻的温漂、ADC的零点偏移、积分累积误差。校准就是针对这三项做补偿。
MTK平台的库仑校准分两个层面:一是硬件校准,在产线用标准电流源给一个已知电流,读回FG的原始值,算出增益和偏移,写进NV;二是软件校准,在运行时用OCV做锚点,当电池静置足够久(通常要求电流小于某个阈值持续几分钟),FG会认为此时电压接近OCV,用它来重置SOC,消除累积误差。这两个层面缺一不可,只做硬件校准,长期使用还是会漂;只做软件校准,产线一致性没法保证。
2. 放电曲线采集:从实验设计到数据拟合的完整链路
2.1 采集前的硬件准备,别急着上设备
在开始采集之前,有几件事必须先确认,否则采出来的数据全是废的。第一,电池必须是你量产要用的那一款,包括电芯型号、保护板、甚至连接器的接触电阻都要一致。我见过有人拿样品电池调好曲线,量产换了个供应商,曲线全废。第二,检流电阻的精度要确认,通常MTK参考设计用的是10mΩ,精度1%,如果你板上用的是5%的,库仑精度先天就不够。第三,温度采集点要贴在电芯表面而不是PCB上,PCB温度受充电IC发热影响,和电芯实际温度能差5度以上。
设备方面,你需要一台可编程电子负载(能恒流放电并记录数据)、一台高精度数据采集仪(至少6位半,采样率1Hz以上)、一个恒温箱(做温度曲线必备)。如果预算有限,电子负载可以用大功率电阻加继电器凑合,但数据精度会打折扣,量产项目不建议省这个钱。
2.2 标准放电曲线的采集步骤
我一般按这个流程走,每一步都有它的道理:
- 满充静置:用标准充电流程充到截止,静置2小时。静置是为了让电芯内部电化学反应稳定,此时电压才是真正的OCV。急着测的话,充电刚结束的电压偏高,曲线头部会失真。
- 恒流放电:按0.2C放电(比如5000mAh电池用1000mA),这是行业惯例,0.2C下极化最小,最接近真实使用。如果你想模拟重载场景,可以再补一组0.5C和1C的曲线。
- 数据记录:每10秒记录一次电压、电流、温度、累计容量。累计容量用电子负载的积分功能,或者自己用电流对时间积分。
- 截止条件:放电到截止电压(通常3.0V或2.75V,看电芯规格),记录此时的放出容量,这就是实际容量。
- 静置恢复:截止后再静置1小时,观察电压回弹,回弹幅度能反映电芯的健康度。
采集完你会得到一张表:容量百分比 vs 电压。但注意,这是放电态的曲线,充电曲线要单独采,两者之间有滞回,MTK的算法里充放电用的是不同的表。
2.3 把原始数据拟合成MTK能用的格式
MTK的battery_profile.txt格式长这样(不同平台略有差异):
[battery_profile] 0 3400 5 3550 10 3620 ... 100 4200左边是SOC百分比,右边是电压(mV)。但你不能直接把采集数据填进去,因为采集的是放电态电压,而MTK算法期望的是OCV。放电态电压 = OCV - I×R_internal,所以要先做内阻补偿。内阻怎么测?用两个不同电流放电,同一SOC下电压差除以电流差就是内阻。补偿完再填表,低电量段的曲线才会平滑。
拟合的时候有个技巧:低电量段(0-20%)多取几个点。因为这段电压变化最剧烈,点少了百分比会跳。我一般0-20%每2%一个点,20-80%每5%一个点,80-100%每2%一个点。另外,曲线必须单调递减,如果拟合出来有回升,说明数据有问题,要回去查采集过程。
2.4 温度分段的处理
前面说了温度补偿的重要性,具体怎么落地?MTK的discharge_temperature表按温度区间存多组曲线,常见分档是:-10度、0度、10度、25度、45度。每档都要单独采集,工作量翻5倍,但这是必须的。
如果项目周期紧,至少要把0度和45度这两档补上,因为这两个温度是用户投诉的高发区。低温下电芯内阻增大,截止电压要适当降低(比如从3.0V降到2.8V),否则会提前关机;高温下容量会略增,但循环寿命下降,曲线要相应调整。我一般会在恒温箱里每个温度点静置4小时以上再开始放电,确保电芯整体温度均匀。
3. 库仑值校准:产线校准与运行时修正的双轨制
3.1 产线硬件校准的具体操作
产线校准的目的是消除每台机器之间的个体差异。流程大致是:机器进入校准模式,用标准电流源给一个已知电流(比如1000mA),FG读回原始ADC值,和标准值比对,算出增益系数;再给一个反向电流,算偏移。这两个系数写进NV分区,开机时驱动读出来用。
这里有个坑:校准时的温度要和量产环境一致。检流电阻有温漂,你在25度校准,产线如果空调不给力到了35度,校准系数就偏了。我一般要求产线温度控制在23±3度,并且校准前机器要预热至少10分钟,让板子温度稳定。
另一个坑是校准电流的选择。电流太小,ADC分辨率不够,算出来的增益不准;电流太大,检流电阻发热又引入误差。经验值是选0.5C到1C之间的电流,比如5000mAh电池用2500mA到5000mA。MTK的参考代码里通常有个默认值,但最好根据你的电池容量调整。
3.2 运行时OCV修正的触发条件
软件层面的修正靠OCV锚点。FG算法会持续监测电流,当满足以下条件时触发修正:
- 电流绝对值小于某个阈值(通常50mA到100mA)持续5到10分钟
- 电池温度在合理范围(0到45度)
- 电压在OCV表的有效范围内
触发后,FG用当前电压查表得到SOC,和库仑积分得到的SOC做加权融合,重置积分基准。这个机制能有效消除累积误差,但触发太频繁会导致百分比抖动,触发太少又修正不及时。MTK的默认参数在mtk_charger.c里,我一般会把静置时间从默认的5分钟调到8分钟,减少误触发。
注意:OCV修正的前提是电池真的静置了。如果用户边充边玩,电流一直很大,修正永远不触发,这时候百分比就靠库仑积分硬撑,误差会累积。所以有些项目会在充电结束时强制做一次修正,这个逻辑要自己加。
3.3 校准效果的验证方法
校准完怎么知道有没有效?我一般做三个测试:
第一个是满充满放循环测试。从100%放电到自动关机,记录放出的实际容量和FG报告的容量,两者误差应该在3%以内。如果误差大,说明库仑增益没校准好。
第二个是静置漂移测试。充满后静置24小时,观察百分比变化。正常情况下应该稳定在100%或99%,如果掉到95%以下,说明自放电补偿没做好,或者OCV表在满电段不准。
第三个是温度循环测试。在恒温箱里从-10度到45度循环,观察百分比是否跳变。低温下如果百分比突然从30%掉到10%,说明低温曲线没拟合好。
这三个测试跑下来,基本能覆盖90%的续航投诉场景。
4. 那些文档里不会写的踩坑实录
4.1 百分比跳变的排查链路
百分比跳变是最常见的投诉,排查起来要有顺序,不能瞎改。我的排查链路是这样的:
第一步,确认是电压跳还是百分比跳。用adb shell dumpsys battery看voltage字段,如果电压平稳但百分比跳,问题在FG算法或曲线表;如果电压本身跳,问题在ADC采样或硬件。
第二步,看跳变发生的SOC区间。如果集中在低电量段,多半是曲线表低段点太少;如果集中在某个特定值,可能是OCV修正触发导致的。
第三步,抓FG的debug log。MTK的FG驱动有debug节点,可以打印每次修正的详细信息,包括修正前的SOC、修正后的SOC、触发原因。看log能快速定位是硬件校准问题还是软件修正问题。
第四步,复现。用同样的放电条件复现,如果复现不了,可能是偶发的温度或电流条件触发,要扩大测试范围。
我遇到过一个案例:百分比在15%附近反复跳,查了半天发现是OCV表在15%对应的电压点正好落在两个温度档的边界上,温度稍微波动就切换曲线,导致SOC跳。解决办法是在边界温度附近做曲线平滑过渡,而不是硬切换。
4.2 充电截止与放电截止的对称性陷阱
很多人调放电曲线调得很好,但充电截止没对应调,结果出现"充到100%但一拔线就掉到95%"的现象。原因是充电截止电压和放电曲线的100%点不匹配。
MTK的充电流程是CC-CV,CV阶段电流降到截止电流(通常0.05C到0.1C)就停。此时电芯电压是充电态电压,比OCV高。如果你放电曲线的100%点用的是OCV,两者就对不上。解决办法是充电截止后做一次静置修正,或者把放电曲线100%点的电压适当调高,让它和充电态电压匹配。
我一般会在充电结束后延迟30分钟再显示100%,给电芯一个弛豫时间。这个延迟逻辑在BatteryService里可以加,但要注意别让用户觉得"充不满"。
4.3 不同批次电芯的一致性处理
量产最头疼的是电芯批次差异。同一型号的电芯,不同批次容量可能差3%到5%,内阻差10%以上。如果你的曲线表是按某一批次调的,换批次就会出问题。
处理办法有两个:一是产线分档,按实际容量把电芯分成几档,每档用不同的曲线表,NV里存档位标识;二是算法自适应,FG在首次完整充放电后,根据实际放出容量自动调整曲线表的缩放系数。前者成本高但精度好,后者成本低但需要几个循环才能收敛。中低端项目一般用后者,高端项目用前者。
我个人的经验是,如果电芯供应商的批次一致性控制在2%以内,可以不做分档,靠算法自适应就够;如果超过3%,最好还是分档,否则售后压力大。
4.4 快充场景下的库仑计误差
快充(尤其是MTK的Pump Express系列)对库仑计是个考验。快充时电流大,检流电阻发热,温漂导致增益变化;同时大电流下电芯极化严重,OCV修正没法触发,库仑积分误差累积快。
针对这个问题,我的做法是:在快充协议里加入周期性小电流窗口。比如每充5分钟,暂停快充10秒,让电流降下来,给FG一个修正机会。这个逻辑要在charger驱动里改,MTK的PE协议栈有预留接口,但文档里没写清楚,需要看代码。
另外,快充时的温度采集要更频繁,因为大电流下温度上升快,温度补偿要跟上。我一般把快充时的温度采样周期从默认的10秒调到2秒。
5. 一套可复用的调试参数与验证清单
5.1 关键参数速查表
下面这张表是我这些年攒下来的经验值,不同项目可以在此基础上微调:
| 参数 | 典型值 | 说明 |
|---|---|---|
| 放电电流 | 0.2C | 标准曲线采集用 |
| 截止电压 | 3.0V(常温)/2.8V(低温) | 低温适当降低 |
| 静置修正触发电流 | 50mA | 低于此值开始计时 |
| 静置修正触发时间 | 8分钟 | 比默认5分钟更稳 |
| 库仑校准电流 | 0.5C-1C | 兼顾精度和发热 |
| 温度分档 | -10/0/10/25/45度 | 至少覆盖0和45度 |
| 低电量段取点间隔 | 2% | 0-20%区间 |
| 百分比误差目标 | <3% | 满充满放循环 |
5.2 上线前的验证清单
项目量产前,这几项必须跑完,缺一项都可能埋雷:
- 常温满充满放循环3次,容量误差<3%
- 低温(0度)满放,不提前关机
- 高温(45度)满充,不鼓包、百分比正常
- 静置24小时漂移<2%
- 快充全程百分比单调递增,无跳变
- 边充边玩场景,百分比不跳
- 不同批次电芯各测一台,一致性可接受
5.3 常用调试命令
调试时这几个命令用得最多,记下来能省不少时间:
# 查看当前电池状态 adb shell dumpsys battery # 查看FG原始数据(节点名因平台而异) adb shell cat /sys/class/power_supply/battery/uevent # 查看充电器状态 adb shell cat /sys/class/power_supply/ac/uevent # 抓取内核FG log adb shell dmesg | grep -i "fg\|coulomb\|battery"如果是userdebug版本,还可以通过/proc/mtk_battery或类似节点读取更详细的FG内部状态,具体路径看平台。
6. 从项目实战里攒下的几点体会
调电池管理这事儿,技术只是一半,另一半是对用户场景的理解。我见过太多项目,实验室数据漂亮得不行,一到用户手里就翻车。原因往往是实验室只测了标准场景,没测真实使用场景。
比如游戏场景,CPU和GPU满载,整机功耗可能到5W以上,这时候电池放电电流大,内阻压降明显,电压法算出来的SOC会偏低。如果曲线表没考虑大电流补偿,用户就会看到"玩游戏时电量掉得特别快,退出游戏又涨回来"。解决办法是在FG算法里加入电流补偿系数,根据放电电流动态调整电压-SOC映射。MTK的算法框架支持这个,但默认参数偏保守,需要自己调。
再比如待机场景,电流很小,OCV修正容易触发,百分比会频繁微调。如果修正幅度太大,用户会看到电量"一会儿99%一会儿100%"。这时候要限制单次修正的幅度,比如每次最多修正2%,避免视觉上的抖动。
还有个容易被忽略的点是电池老化。新电池和用了两年的电池,容量和内阻都变了,如果曲线表不更新,老电池的百分比会越来越不准。MTK的FG算法有老化补偿机制,但需要完整的充放电循环才能学习。我一般建议在系统里加一个"电池健康度"显示,让用户知道电池状态,同时也给算法学习的机会。
最后说个心态问题。电池管理是个"差不多就行"和"差一点都不行"之间的活儿。用户对续航的感知很敏感,5%的误差就能引发投诉,但要把误差压到3%以内,投入的调试成本可能是前者的好几倍。所以项目初期就要和产品经理对齐目标,是做到"能用"还是"好用",这决定了你投入多少精力。我的建议是,中高端项目一定要做到3%以内,低端项目可以放宽到5%,但低电量段(20%以下)必须准,因为这段用户最焦虑。
这套流程我在几个项目上跑下来,续航投诉率从最初的5%降到了0.5%以下。当然每个平台、每颗电芯都有它的脾气,参数不能照搬,但排查思路和验证方法是通用的。希望这些踩坑经验能帮到正在被电池问题折磨的同行,少走点弯路。