3.MT65MT6572/MT6582平台SWFG算法分析
4.误差和消除误差
MTK平台Battery软件架构基本如上图所示。
具体过程:
硬件ADC读取Battery的各路信息:包括温度,电压等。
MTK开发的电量算法分析得到的数据。
Kernel层将电量信息通过写文件节点的方式更新,并通过UEVENT通知上层。
点,获取电量信息。
根据不同的电量读取和计算的策略,第一步的读取和第二步的算法部分会有比较大的差
异,而后面的数据更新和事件通知部分一致性较高。
本篇重点分析MT65MT6572/MT6582平台SWFG算法实现,对比SW_FG和HW_FG在硬件及软件上的部分差异,分析电量误差形成的一些原因和MTK已经采取的消除误差的措施。对于Battery数据更新和充电流程则粗略分析。
充电状态机,battery充电的逻辑,就依赖于这张图,如果是用的externalchargeric,则应当参考该IC的充电逻辑。
linearcharging下cc转cv,是通过ADC读取电压后,软件切换。而使用chargeric则很可能是硬件直接切换。
alps/mediatek/kernel/drivers/power/linear_charging.c
alps/mediatek/kernel/drivers/power/switching_charging.c
除了10s一次的定时器更新,插拔充电器会触发中断,中断处理时同样会更新battery数据。
为了得到较为精确的电量数据,需要改善测量方式和计算方法,并针对已知误差采取优化手段。一下介绍MTK平台下采用的一些电量算法。
AUXADC算法:
事实上,所有算法都要依赖ADC读取电量信息,这边的AUXADC算法指只依赖ADC读值然后查表读取电量的算法。
这种算法只重构了ZCVtable,误差会很大。
库仑积分法:
通过开路电压查表得到初始电量D0,后续电量通过电流积分累积,通用性强,依赖初始电量的精确度。
混合型算法:
SWFG算法HWFG算法。事实上MTK平台项目通常采用的是混合型算法。
SWFG的参考电路:
HWFG的参考电路
相同点:NTC电阻用于测量温度ADC测量各路信号
不同点:HWFG有单独的ADC和20毫欧的电阻作电流的侦测。
HWFG和SWFG最大差异就是电流的获取方式。
混合算法的流程,HWFG通过FGADC读取FG电阻两端电压获得电流,而SWFG则结合库伦算法通过SW方式算得。这部分会详细介绍。
主要分析上图黄色部分
大部分项目都采用混合算法,下面从算法初始化开始介绍下SWFG的算法实现。
battery_meter.c
这个C文件主要负责电池电量算法的实现向上主要承接battery_common.c向下调用battery_meter_hal.c中的接口,以读取电池的各路信号。
=>battery_meter_initial
首先看下调用这个func的timing。
显然在开机初始化阶段,就会进入该函数,且只会运行一次。
针对AUXADCSW_FGHW_FG三种不同的电池算法方案分别初始化,因为MT6582平台采用的SW_FG,所以接下去先主要分析SW_FG的流程。
SW_FG的准备工作分为两步:table_initoam_init
先看table_init
首先要获取当前的温度信息
=>force_get_tbat
ADC读值
这边就是MTK为了结合实际温度获取较为精确的电池信息而采取的线性平均值法。原理是利用预先测得的分布在-1002550摄氏度下的ZCV表,结合真实温度,动态重构一张当前温度下的ZCV表格。
TEMPERATURE对应预留的空ZCV表格,如下
构造新表的函数如下
采用线性平均法填补了有效温度内所有的ZCV对应值但与真实曲线必然存在一定的误差。
=>oam_init
常见的指针函数传参比较有趣vol_bat这个参数下传给底下pmic做count,然后被重新赋值成读取的v_bat值之所以能这样做是因为这两块代码同处在kernel层并地址传参
battery_meter_hal.c虽然顶着hal的名头,其实是驱动程序,工作在内核层,主要实现上表各结构体针对MTK不同种的充电方案读取各项参数,包括v_battemperaturev_i_sense等
这边走pmic
vbat是channel5,要等到adc数据ready才能去读寄存器,看一下pmic的手册
精度15bit的ADC其中14bit用来存储数据1个bit做ready信号,似乎ADC3和我们之前的dwchannelnumber有点对不上?
可以看到dwchannel5最终访问的仍是ADC3,另外可以直接比较下寄存器地址。
和datasheet左上角的寄存器地址一致
最后还要做次数值转换,公式如下:
分辨率计算:测量电压范围/(2^AD位数-1)
另外,对于同为电压值的v_bat和v_i_sense,可能会出现adc量程不够的问题这时候需要通过电阻分压。所以case6和7的r_val_temp为分压比
和前面一样ADC读值但是6320的spec上对于PCHR没有说明从函数定义的名称上看是开路电压但是如何在一个闭路的环境中通过ADC读取ocv,有些不解。查了下读这个值的timing,只有在initsuspend和resume的时候才去获取.猜想一是可能利用linearcharging这种充9停1的方式,在第10s读取电压作为OCV也可能是因为刚开机时电流还不大读到的电压值可用作OCV总之这个值应该是真实的开路电压的一个近似值。
根据电压读表获取电量
可以看到用的是table_init时重构的新表
Ok这边再一次利用线性平均法这一次是针对ADC读到测到的电压线性平均后得到一个较精准的电量
首先判断是否插着充电器,读PMU寄存器实现
为什么需要判断有没有插着充电器呢?
我是这么理解的,之前通过ADC读取了两个电压值其中一个是V_BAT另一个是hw_ocv是在闭路环境下读取的开路电压近似值,如果此时插着充电器,会有充电电流通过这个hw_ocv的值和开路电压的误差会增大,因此需要做进一步的处理。
插入充电器时,电量误差不大于30满电误差不大于10否则hw_ocv无效
Dod是指用电深度,100-dod=电池剩余容量
看一下dod_init的实现
看下这个值是何时被写入的:
电池信息10supdate1次同样UI电量10s存入RTC寄存器一次。
用7个bit存储电量信息。
开机时直接显示关机时保存的电量,会增强用户体验性,但是如果是更换电池或其他情况造成关机电量和开机电量相差过大,显然应该采用开机电量,否则后续电池电量跳变反而会影响用户体验。
Normalboot下忽略||后面的条件主要就是要求没有插入充电器不处于低电量误差不超过40%
经过前面所有的判断最终得到了gFG_capacity这个电量,也就是开机电量因为开机电量在整个电量计算中相当重要并且又要结合用户体验所以之前会有很多的条件分支。
即25度用标准容量其他温度下需要乘上一个比例值。
oam_v_ocv_1和oam_v_ocv_2现在是根据dod_init的结果取得的大部分情况下就是关机电量查表得到的ocv电压值而注释掉的原方案直接采用hw_ocv的值
这边是算ocv1和ocv2对应的电池内阻r,通过查表的方式获取因为r和v的对应表也是开路条件下测得所以用hw_ocv查表获取的值比原先通过vbat取平均要精准些。
其他一些oam电量算法需要的参数初始化
oam_init后,oam_run这个func负责电量的计算,看一下调用的时机。
=>mt_battery_GetBatteryData
显然也是10s轮询一次,get_percentage这个func多个分支对应不同的电量算法
=>oam_run
先看下MTKSWFG算法的原理图
SWFG的核心在于通过两种方式更新电压,去逼近真实开路电压最终查表获取近似真实的电量值。
ocv1被假定为开路电压ocv2则是闭路电压,以下结合实际代码和上述流程图分析下SW_FG算法流程
D0D1D2D3D4D5代表不同的放电深度.
这个算法的思路是这样的:最终通过开路电压oam_v_ocv_1查ZCV表得到当前的电量值->开路电压需要通过闭路电压v_bat和闭路电流oam_i_2去回溯电池内阻逐次逼近–>oam_i_2通过另一种方式电量积分更新的电压oam_v_ocv_2
总的来说:电压通过两种方式更新电流积分求电量后查表/电池内阻回溯IRdrop求得电池内阻更新方式只有一种根据电压查表.
具体分析部分代码:
闭路电压的更新不需要算法支持直接通过读寄存器实现,注意vol_bat这个参数被复用,下传表平均次数返回时为最终的v_bat电压值
ocv_1和ocv_2分别是两种方式更新的电压这边通过内阻的IRdrop求电流.
上图R可以是电池内阻
关键是oam_i_2这边的I2有几个作用:
<1>因为电流是通过上图的内阻IRdrop得到的,而方式一内阻回溯逼近开路电压本质也是IRdrop,如果使用oam_i_1则没有意义,只能使用不同体系的I2.
<2>方式二电流积分求电量查表同样依赖oam_i_2这个体系是累积积分不需要引用其他体系的参数
<3>I2的方向作为充电还是放电的依据
而oam_i_1只有作用3
oam_car_1/oam_car_2是累积电量显然oam_car_2是算法的有效参数
gFG_BATT_CAPACITY_aging是电池总容量之前分析过了根据开机时读取的电池温度会有所不同
d2为积分法算出的电池当前容量d0为开机电量不会更新d1不重要
内阻回溯IRdrop逼近开路电压,具体分析下:
主要是这个for循环,首先通过v_bat去查表得到电池内阻r然后用另一种算法求得的电流I和内阻r算出内阻分去的电压v,推算ocv_1.
几次循环反复这个过程逼近真实的开路电压。
有几个注意点:
1.
这边的电压单位到底是v还是mv?实际上是0.1mv
2.gFG_resistance_bat和R_FG_VALUE分别是指代电池内阻和硬件FG使用的FG电阻(一般是20毫欧)这边是SW_FG所以后一项为0
3.
因为ret_compensate是int型变量做除法时取整处理会引入较大误差,加上这个值使结果四舍五入
实际做6次内阻回溯,这时的电压值已经和开路电压比较接近,查表得到D3,D3基本就能反映当前电量了。
MTK在D3的问题上针对电量跳变的情况又做了步优化得到D5,看下代码
这部分代码比较简单,1分钟内电量值不会改变,且每分钟电量的变化不会大于1%,这样用户体验会比较好。防止因为低电压时陡峭的电量曲线,以及比较耗电的应用,或突然的大电流引起的电量跳变。当然特殊应用下应该取消这个功能,否则会带来电量变化延时等问题。
这边的返回值将填充BMT_status.SOC,这个参数再经过优化得到BMT_status.UI_SOC就是菜单栏看到的电池电量了。
因为电量值本身不容易通过仪器直接测量,只能依赖开路电压与电量的关系即电量曲线或者电流积分公式演算,这样一个过程会有很多产生误差的点,MTK针对其中一些给出了优化方式。
另外由于电池特性有些时候真实的电量数据反而使得用户体验下降,这时候还要针对电量数据做一些特殊处理,以满足用户的预期。
以下是MT6582平台SWFG中部分电量误差产生的原因以及MTK用于消除/减小误差的方法。
库伦积分时的电流:
即使是cc状态,电流也是有波动的,而进入cv状态后,电流的变化会更大。因为这样一个电流变化并无规律,所以不可能导出电流公式用于电量积分。目前代码会把每10s算一次电流作为这10s的平均电流。
这样会形成一个电量累积误差。
电池内阻不稳定造成的误差:
电池的内阻会随温度变化,且幅度很大,而在SWFG的算法中依赖电池内阻计算电流大小,如果使用固定值的电池内阻会严重影响电流的计算。
MTK应对方案:
1.建立zcvtable
MTK的zcvtable,建立了几个特定温度(-10,0,25,50)下的内阻r和开路电压ocv的关系,这样可以根据可量测的电压信号查表推算内阻.
2.线性平均值
MTK在算法初始化时,读取温度信息,通过线性平均重构zcvtable,这样可以覆盖从-10到50的所有温度点。
MT6582平台MTK电量算法只会在初始化的时候去通过读取的温度重构zcvtable,但假设使用者周围的温度在手机使用过程中变化比较大,或者电池本身发热很厉害,电池内阻值有了较大改变,则测出的电量偏差也会比较大。
假设从高温环境到低温环境,根据电池特性,电池内阻会大幅增大,而电池可使用容量将会下降。实际耗流假设变化不大,由于还是用之前的zcv表格,用于计算的内阻比实际内阻小很多,则算出来的电流会偏大。这样显示电量会快速下降。
开机电压的误差:
由于开机过程中,外设逐渐进入工作状态,电流逐渐增大,这时候ADC读到的电压偏差比较大。
MTK方案:89的HWFG会在PMU未开放restb时获取电压取代MT6582的sw读开机ocv的方案
ADC精度问题
这是硬件设施的问题,有一点精度和分辨率是两码事。精度指转换后所得结果相对与实际值的准确度分辨率是指转换器所能分辨的模拟信号的最小变化值。
插拔和重新开机的显示误差
开机过程中有累积误差,开机读取ocv查表同样有误差,这样的结果是用户可能看到开关机的电量出现很大误差。
电池老化的误差
MTK方案:
HWFG可以通过充电到100%的过程重新算得电池最大容量,SWFG则并没有采取这种方案,可能是SWFG的库伦积分误差较大。
100%tracking&0%tracking
充满电和关机实际上有两个判断标准:
软件关机电压:system_off_voltage电量显示0%
截止电流:top_off_current电量显示100%
由于误差这两套标准在实际使用中肯定不一致,MTK通过UI_SOC这个变量对算法得到SOC进行处理。让电量显示follow电压和电流的判断。
UI_SOC如先到0%需要等待电压和电流的判断;
UI_SOC如后到,则需加快步伐每次-1
代码如下:
100%tracking原理和上面的差不多,不一一列举。
充满电后,还回去reset之前FG算法的一些参数比如DODCAR等,启到修正误差的作用。