MTK Fuel Gauge算法分析Linux内核电量计相关.docx
《MTK Fuel Gauge算法分析Linux内核电量计相关.docx》由会员分享,可在线阅读,更多相关《MTK Fuel Gauge算法分析Linux内核电量计相关.docx(30页珍藏版)》请在冰豆网上搜索。
MTKFuelGauge算法分析Linux内核电量计相关
SWFG算法分析
目录
1,Battery架构简析
2,MTK电量算法简析
3,72/82平台SWFG算法分析
4,误差和消除误差
Battery架构简析
MTK平台Battery软件架构基本如上图所示。
具体过程:
硬件ADC读取Battery的各路信息:
包括温度,电压等。
MTK开发的电量算法分析得到的数据。
Kernel层将电量信息通过写文件节点的方式更新,并通过UEVENT通知上层。
上层Service开启UEVENTLISTENER,监听到UEVENT后,读取battery相关文件节点,获取电量信息。
Service更新数据后,通过Broadcast通知所有开启了相关listener的activities。
根据不同的电量读取和计算的策略,第一步的读取和第二步的算法部分会有比较大的差异,而后面的数据更新和事件通知部分一致性较高。
本篇重点分析72/82平台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
kernel层battery驱动工作的流程,Bat_thread是工作的重点,通过单独的线程依赖10s定时器,更新battery相关信息。
电量算法分析后得到的数据也不会直接update,InformationProcessing还会针对一些特殊情况对显示电量做调整,比如0%tracking&100%tracking。
除了10s一次的定时器更新,插拔充电器会触发中断,中断处理时同样会更新battery数据。
所有和电池充电相关的数据都存储在power_supply类型的结构体中,这是linux标准的电源子系统体系。
MTK电量算法简析
为了得到较为精确的电量数据,需要改善测量方式和计算方法,并针对已知误差采取优化手段。
一下介绍MTK平台下采用的一些电量算法。
AUXADC算法:
事实上,所有算法都要依赖ADC读取电量信息,这边的AUXADC算法指只依赖ADC读值然后查表读取电量的算法。
这种算法只重构了ZCVtable,误差会很大。
库仑积分法:
通过开路电压查表得到初始电量D0,后续电量通过电流积分累积,通用性强,依赖初始电量的精确度。
混合型算法:
SWFG算法HWFG算法。
事实上MTK平台项目通常采用的是混合型算法。
SWFG的参考电路:
HWFG的参考电路
相同点:
NTC电阻用于测量温度ADC测量各路信号
不同点:
HWFG有单独的ADC和20毫欧的电阻作电流的侦测。
HWFG和SWFG最大差异就是电流的获取方式。
混合算法的流程,HWFG通过FGADC读取FG电阻两端电压获得电流,而SWFG则结合库伦算法通过SW方式算得。
这部分会详细介绍。
72/82平台SWFG算法分析
主要分析上图黄色部分
大部分项目都采用混合算法,下面从算法初始化开始介绍下SWFG的算法实现。
battery_meter.c
这个C文件主要负责电池电量算法的实现向上主要承接battery_common.c向下调用battery_meter_hal.c中的接口,以读取电池的各路信号。
=>battery_meter_initial
首先看下调用这个func的timing。
显然在开机初始化阶段,就会进入该函数,且只会运行一次。
针对AUXADCSW_FGHW_FG三种不同的电池算法方案分别初始化,因为82平台采用的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
这个函数也是起分流作用的通过dwchannel,分到不同的处理函数去。
硬件上,ADC通过一个mux数据选择器对各路模拟信号进行切换有点类似cpu的时间片和移动通信的时隙切换。
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总之这个值应该是真实的开路电压的一个近似值。
=>oam_init
根据电压读表获取电量
可以看到用的是table_init时重构的新表
Ok这边再一次利用线性平均法这一次是针对ADC读到测到的电压线性平均后得到一个较精准的电量
=>oam_init
首先判断是否插着充电器,读PMU寄存器实现
为什么需要判断有没有插着充电器呢?
我是这么理解的,之前通过ADC读取了两个电压值其中一个是V_BAT另一个是hw_ocv是在闭路环境下读取的开路电压近似值,如果此时插着充电器,会有充电电流通过这个hw_ocv的值和开路电压的误差会增大,因此需要做进一步的处理。
插入充电器时,电量误差不大于30满电误差不大于10否则hw_ocv无效
=>oam_init
Dod是指用电深度,100-dod=电池剩余容量
看一下dod_init的实现
⇨dodinit
首先看g_rtc_fg_soc这个变量MTK的策略是每隔一段时间将当前电量存储到RTC寄存器中,在开机时读取该电量。
主要目的是改善用户体验。
看下这个值是何时被写入的:
电池信息10supdate1次同样UI电量10s存入RTC寄存器一次。
用7个bit存储电量信息。
开机时直接显示关机时保存的电量,会增强用户体验性,但是如果是更换电池或其他情况造成关机电量和开机电量相差过大,显然应该采用开机电量,否则后续电池电量跳变反而会影响用户体验。
Normalboot下忽略||后面的条件主要就是要求没有插入充电器不处于低电量误差不超过40%
经过前面所有的判断最终得到了gFG_capacity这个电量,也就是开机电量因为开机电量在整个电量计算中相当重要并且又要结合用户体验所以之前会有很多的条件分支。
Q_MAX_POS_25是常温下电池容量因为FG算法计算电池容量会用到库伦积分所以需要关注电池容量的问题。
这个值需要根据实际电池容量客制化。
即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这个体系是累积积分不