MTK平台下Battery驱动分析
主要涉及代码:
Kernel:
kernel-3.10\drivers\power\mediatek\
kernel-3.10\drivers\misc\mediatek\mach\mt6580\<project_name>\power\
MTK Battery框架结构图
通过上层通过读取创建一系列的设备节点获取电池相关的状态信息
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
android电源管理系统
/sys/class/power_supply/ac/online //AC 电源连接状态 交流电 即电源插座
/sys/class/power_supply/usb/online //USB电源连接状态
/sys/class/power_supply/battery/status //充电状态
/sys/class/power_supply/battery/health //电池状态
/sys/class/power_supply/battery/present //使用状态
/sys/class/power_supply/battery/capacity //电池 level
/sys/class/power_supply/battery/batt_vol //电池电压
/sys/class/power_supply/battery/batt_temp //电池温度
/sys/class/power_supply/battery/technology //电池技术
代码框架:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
battery_common.c
在Battery驱动模块中。battery_probe函数中会创建一些设备节点,而且执行一个线程bat_thread_kthread获取电池相关的数据信息
battery_kthread_hrtimer_init();//检測电池插入/拔出
kthread_run(bat_thread_kthread, NULL, "bat_thread_kthread");
在bat_thread_kthread线程中
int bat_thread_kthread(void *x)
{
ktime_t ktime = ktime_set(3, 0); /* 10s, 10* 1000 ms */ #ifdef BATTERY_CDP_WORKAROUND
if (is_usb_rdy() == KAL_FALSE) {
battery_log(BAT_LOG_CRTI, "CDP, block\n");
wait_event(bat_thread_wq, (is_usb_rdy() == KAL_TRUE));
battery_log(BAT_LOG_CRTI, "CDP, free\n");
} else{
battery_log(BAT_LOG_CRTI, "CDP, PASS\n");
}
#endif /* Run on a process content */
while (1) {
mutex_lock(&bat_mutex); if (((chargin_hw_init_done == KAL_TRUE) && (battery_suspended == KAL_FALSE)) || ((chargin_hw_init_done == KAL_TRUE) && (fg_wake_up_bat == KAL_TRUE)))
BAT_thread(); mutex_unlock(&bat_mutex); #ifdef FG_BAT_INT
if(fg_wake_up_bat==KAL_TRUE)
{
wake_unlock(&battery_fg_lock);
fg_wake_up_bat=KAL_FALSE;
battery_log(BAT_LOG_CRTI, "unlock battery_fg_lock \n");
}
#endif //#ifdef FG_BAT_INT battery_log(BAT_LOG_FULL, "wait event\n"); wait_event(bat_thread_wq, (bat_thread_timeout == KAL_TRUE)); bat_thread_timeout = KAL_FALSE;
hrtimer_start(&battery_kthread_timer, ktime, HRTIMER_MODE_REL);
ktime = ktime_set(BAT_TASK_PERIOD, 0); /* 10s, 10* 1000 ms 设置时间为10秒*/
if (chr_wake_up_bat == KAL_TRUE && g_smartbook_update != 1) /* for charger plug in/ out */
{
#if defined(CONFIG_MTK_DUAL_INPUT_CHARGER_SUPPORT)
if (DISO_data.chr_get_diso_state) {
DISO_data.chr_get_diso_state = KAL_FALSE;
battery_charging_control(CHARGING_CMD_GET_DISO_STATE, &DISO_data);
}
#endif g_smartbook_update = 0;
#if defined(CUST_CAPACITY_OCV2CV_TRANSFORM)
battery_meter_set_reset_soc(KAL_FALSE);
#endif
battery_meter_reset();
chr_wake_up_bat = KAL_FALSE; battery_log(BAT_LOG_CRTI,
"[BATTERY] Charger plug in/out, Call battery_meter_reset. (%d)\n",
BMT_status.UI_SOC);
} } return 0;
在这个线程中,每隔10s会去调用函数BAT_Thread去获取电池数据
BAT_Thread
void BAT_thread(void)
{
static kal_bool battery_meter_initilized = KAL_FALSE;
if (battery_meter_initilized == KAL_FALSE) {
battery_meter_initial(); /* move from battery_probe() to decrease booting time 第一次进该函数会进行一些初始化,如设置D0的值(初始电量,构建电池曲线等) table_init, oam_init*/
BMT_status.nPercent_ZCV = battery_meter_get_battery_nPercent_zcv();
battery_meter_initilized = KAL_TRUE;
} mt_battery_charger_detect_check();//充电器型号。是否插入等方面的检測,
mt_battery_GetBatteryData();//核心函数获取电池数据
if (BMT_status.charger_exist == KAL_TRUE) {
check_battery_exist();
}
mt_battery_thermal_check();//电池温度检測以及开机mode
mt_battery_notify_check();//检測电池电压,电流等 if (BMT_status.charger_exist == KAL_TRUE) {
mt_battery_CheckBatteryStatus();//充电异常检測
mt_battery_charging_algorithm();//切换充电模式 Pre_CC->CC->CV->Full
} mt_battery_update_status();//上报电池数据
mt_kpoc_power_off_check();
}
mt_battery_GetBatteryData:
void mt_battery_GetBatteryData(void)
{
kal_uint32 bat_vol, charger_vol, Vsense, ZCV;
kal_int32 ICharging, temperature, temperatureR, temperatureV, SOC;
static kal_int32 bat_sum, icharging_sum, temperature_sum;
static kal_int32 batteryVoltageBuffer[BATTERY_AVERAGE_SIZE];
static kal_int32 batteryCurrentBuffer[BATTERY_AVERAGE_SIZE];
static kal_int32 batteryTempBuffer[BATTERY_AVERAGE_SIZE];
static kal_uint8 batteryIndex = 0;
static kal_int32 previous_SOC = -1; bat_vol = battery_meter_get_battery_voltage(KAL_TRUE);
Vsense = battery_meter_get_VSense();
if( upmu_is_chr_det() == KAL_TRUE ) {
ICharging = battery_meter_get_charging_current();
} else {
ICharging = 0;
} charger_vol = battery_meter_get_charger_voltage();
temperature = battery_meter_get_battery_temperature();
temperatureV = battery_meter_get_tempV();
temperatureR = battery_meter_get_tempR(temperatureV); if (bat_meter_timeout == KAL_TRUE || bat_spm_timeout == TRUE || fg_wake_up_bat== KAL_TRUE)
{
SOC = battery_meter_get_battery_percentage();//获取电池电量
//if (bat_spm_timeout == true)
//BMT_status.UI_SOC = battery_meter_get_battery_percentage(); bat_meter_timeout = KAL_FALSE;
bat_spm_timeout = FALSE;
} else {
if (previous_SOC == -1)
SOC = battery_meter_get_battery_percentage();
else
SOC = previous_SOC;
} ZCV = battery_meter_get_battery_zcv(); BMT_status.ICharging =
mt_battery_average_method(BATTERY_AVG_CURRENT, &batteryCurrentBuffer[0], ICharging, &icharging_sum,
batteryIndex); if (previous_SOC == -1 && bat_vol <= batt_cust_data.v_0percent_tracking) {
battery_log(BAT_LOG_CRTI,
"battery voltage too low, use ZCV to init average data.\n");
BMT_status.bat_vol =
mt_battery_average_method(BATTERY_AVG_VOLT, &batteryVoltageBuffer[0], ZCV, &bat_sum,
batteryIndex);
} else {
BMT_status.bat_vol =
mt_battery_average_method(BATTERY_AVG_VOLT, &batteryVoltageBuffer[0], bat_vol, &bat_sum,
batteryIndex);
} if (battery_cmd_thermal_test_mode == 1)
{
battery_log(BAT_LOG_CRTI,
"test mode , battery temperature is fixed.\n");
}
else
{
BMT_status.temperature =
mt_battery_average_method(BATTERY_AVG_TEMP, &batteryTempBuffer[0], temperature, &temperature_sum,
batteryIndex);
} BMT_status.Vsense = Vsense;
BMT_status.charger_vol = charger_vol;
BMT_status.temperatureV = temperatureV;
BMT_status.temperatureR = temperatureR;
BMT_status.SOC = SOC;
BMT_status.ZCV = ZCV; #if !defined(CUST_CAPACITY_OCV2CV_TRANSFORM)
if (BMT_status.charger_exist == KAL_FALSE) {
if (BMT_status.SOC > previous_SOC && previous_SOC >= 0)
BMT_status.SOC = previous_SOC;
}
#endif previous_SOC = BMT_status.SOC; batteryIndex++;
if (batteryIndex >= BATTERY_AVERAGE_SIZE)
batteryIndex = 0; if (g_battery_soc_ready == KAL_FALSE)
g_battery_soc_ready = KAL_TRUE; battery_log(BAT_LOG_CRTI,
"AvgVbat=(%d),bat_vol=(%d),AvgI=(%d),I=(%d),VChr=(%d),AvgT=(%d),T=(%d),pre_SOC=(%d),SOC=(%d),ZCV=(%d)\n",
BMT_status.bat_vol, bat_vol, BMT_status.ICharging, ICharging,
BMT_status.charger_vol, BMT_status.temperature, temperature,
previous_SOC, BMT_status.SOC, BMT_status.ZCV); }
battery_meter_get_battery_percentage:
kal_int32 battery_meter_get_battery_percentage(void)
{
//去掉了一些无效代码
if (bat_is_charger_exist() == KAL_FALSE)
fg_qmax_update_for_aging_flag = 1; oam_run();//核心函数 return (100 - oam_d_5); }
oam_run:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
void oam_run(void)
{
int vol_bat = 0;
/* int vol_bat_hw_ocv=0; */
/* int d_hw_ocv=0; */
int charging_current = 0;
int ret = 0;
/* kal_uint32 now_time; */
struct timespec now_time;
kal_int32 delta_time = 0; /* now_time = rtc_read_hw_time(); */
get_monotonic_boottime(&now_time); /* delta_time = now_time - last_oam_run_time; */
delta_time = now_time.tv_sec - last_oam_run_time.tv_sec; last_oam_run_time = now_time; /* Reconstruct table if temp changed; */
fgauge_construct_table_by_temp(); vol_bat = 15; /* set avg times */
ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE, &vol_bat);//首先获取电池Vbat端的电压 oam_i_1 = (((oam_v_ocv_1 - vol_bat) * 1000) * 10) / oam_r_1; /* 0.1mA 计算电流oam_v_ocv_1为上一次測得的开路电压*/
oam_i_2 = (((oam_v_ocv_2 - vol_bat) * 1000) * 10) / oam_r_2; /* 0.1mA */ oam_car_1 = (oam_i_1 * delta_time / 3600) + oam_car_1; /* 0.1mAh 损失/获取 的电量 = i*t 即为放电深度,oam_car_1>0则为放电,反之则为充电*/
oam_car_2 = (oam_i_2 * delta_time / 3600) + oam_car_2; /* 0.1mAh */ oam_d_1 = oam_d0 + (oam_car_1 * 100 / 10) / gFG_BATT_CAPACITY_aging;//<span style="font-family: Arial, Helvetica, sans-serif;">gFG_BATT_CAPACITY_aging为电池总的容量。在oam_init的时候赋值</span> if (oam_d_1 < 0)
oam_d_1 = 0;
if (oam_d_1 > 100)
oam_d_1 = 100; oam_d_2 = oam_d0 + (oam_car_2 * 100 / 10) / gFG_BATT_CAPACITY_aging;
if (oam_d_2 < 0)
oam_d_2 = 0;
if (oam_d_2 > 100)
oam_d_2 = 100; oam_v_ocv_1 = vol_bat + mtk_imp_tracking(vol_bat, oam_i_2, 5); oam_d_3 = fgauge_read_d_by_v(oam_v_ocv_1);//算出的开路电压查表获得放电深度
if (oam_d_3 < 0)
oam_d_3 = 0;
if (oam_d_3 > 100)
oam_d_3 = 100; oam_r_1 = fgauge_read_r_bat_by_v(oam_v_ocv_1); oam_v_ocv_2 = fgauge_read_v_by_d(oam_d_2);
oam_r_2 = fgauge_read_r_bat_by_v(oam_v_ocv_2); oam_d_4 = oam_d_3; gFG_columb = oam_car_2 / 10; /* mAh */ if ((oam_i_1 < 0) || (oam_i_2 < 0))
gFG_Is_Charging = KAL_TRUE;
else
gFG_Is_Charging = KAL_FALSE; d5_count_time = 60; d5_count = d5_count + delta_time;
if (d5_count >= d5_count_time) {
if (gFG_Is_Charging == KAL_FALSE) {
if (oam_d_3 > oam_d_5) {//在放电状态下。oam_d_3大于上一次电量oam_d_5,则电池电量-1
oam_d_5 = oam_d_5 + 1;
} else {
if (oam_d_4 > oam_d_5) {
oam_d_5 = oam_d_5 + 1;
}
}
} else {
if (oam_d_5 > oam_d_3) {//<span style="font-family: Arial, Helvetica, sans-serif;">在充电状态下,oam_d_3小于上一次电量oam_d_5。则电池电量+1</span> oam_d_5 = oam_d_5 - 1;
} else {
if (oam_d_4 < oam_d_5) {
oam_d_5 = oam_d_5 - 1;
}
}
}
d5_count = 0;
oam_d_3_pre = oam_d_3;
oam_d_4_pre = oam_d_4;
} }
oam_run的整个的一个流程例如以下图所看到的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
MTK採用的Fuel方案获取剩余电池电量
首先通过adc读取电池 Vbat脚的电压值,然后通过这个闭路电压值查找 R—Table (cust_battery_meter_table.h )获得当前电压和温度下的电池内阻值。然后通过递归回溯的方法得到开路电压 OCV,然后通过这个OCV电压值查找 放电深度表获取当前的放电深度。从而算出剩余的电池电量。
调试驱动时应注意的一些关键点
1、电池曲线
2、充电电流的一些设置(AC_CHARGER_CURRENT,NON_STD_AC_CHARGER_CURRENT,USB_CHARGER_CURRENT等),是否是高压电池HIGH_BATTERY_VOLTAGE_SUPPORT
最高温度: MAX_CHARGE_TEMPERATURE
最高电压: V_CHARGER_MAX
截止满充电流大小:CHARGING_FULL_CURRENT
充关机最大电池差异:CUST_POWERON_DELTA_CAPACITY_TOLRANCE(若小于这个值。则下一次开机用RTC里面存储的剩余电池电量值)
关机电压:SYSTEM_OFF_VOLTAGE
UI电量同步时间:SYNC_TO_REAL_TRACKING_TIME
MTK平台下Battery驱动分析的更多相关文章
- BEA WebLogic平台下J2EE调优攻略--转载
BEA WebLogic平台下J2EE调优攻略 2008-06-25 作者:周海根 出处:网络 前 言 随着近来J2EE软件广泛地应用于各行各业,系统调优也越来越引起软件开发者和应用服务器提供 ...
- Linux平台下Lotus Domino服务器部署案例
Linux平台下Lotus Domino服务器部署案例 几年前我写了篇<RHAS2.1下安装中文LotusDominoR6.5图解>这篇文档被多个大型网站转载,曾帮助过很多公司系统管理员部 ...
- 【MongoDB】在windows平台下mongodb的分片集群(五)
本篇接着上面的四篇继续讲述在window平台下mongodb的分片集群搭建. 在分片集群中也照样能够创建索引,创建索引的方式与在单独数据库中创建索引的方式一样.因此这不再多说.本篇主要聚焦在分片键的选 ...
- 【转】android电池(四):电池 电量计(MAX17040)驱动分析篇
关键词:android 电池 电量计 MAX17040 任务初始化宏 power_supply 平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台: ...
- Dotnet全平台下APM-Trace探索
背景 随着支撑的内部业务系统越来越多,向着服务化架构进化,在整个迭代过程中,会逐渐暴露出以下问题. 传统依赖于应用服务器日志等手段的排除故障原因的复杂度越来越高,传统的监控服务已经无法满足需求. 终端 ...
- net平台下c#操作ElasticSearch详解
net平台下c#操作ElasticSearch详解 ElasticSearch系列学习 ElasticSearch第一步-环境配置 ElasticSearch第二步-CRUD之Sense Elasti ...
- windows平台下的oracle ORA-01031的解决方法
今天下午遇到一个很怪异的问题,在windows平台下sqlplus / as sysdba登陆数据库,提示权限不足, 当时就纳闷了,sys用户登陆数据库还能权限不足,问题出现了,就开始寻找解决方法呗 ...
- ElasticSearch-.net平台下c#操作ElasticSearch详解
ElasticSearch系列学习 ElasticSearch第一步-环境配置 ElasticSearch第二步-CRUD之Sense ElasticSearch第三步-中文分词 ElasticSea ...
- android电池(四):电池 电量计(MAX17040)驱动分析篇【转】
本文转载自:http://blog.csdn.net/xubin341719/article/details/8969369 电池电量计,库仑计,用max17040这颗电量IC去计量电池电量,这种方法 ...
随机推荐
- 2>&1使用
2>&1使用 一 相关知识 1)默认地,标准的输入为键盘,但是也可以来自文件或管道(pipe |).2)默认地,标准的输出为终端(terminal),但是也可以重定向到文件,管道或后引号 ...
- 自己写了一个超级简便且傻瓜式的且功能强大的CSV组件(并且代码优雅,绝对没有一行多余的代码)
github地址: https://github.com/yangfeixxx/chipsCSV.git 解决的问题:解决了传统的CSV工具类对于实体类无法自动化封装为带表头的CSV文件的痛点,在读取 ...
- 【Hihocoder1034】毁灭者问题(splay,树状数组)
题意: 假设你拥有 n 个魔法单位,他们从左到有站在一行,编号从 1 到 n. 每个单位拥有三项属性: si: 初始法力. mi: 最大法力上限. ri: 每秒中法力回复速度. 现在你操纵一个毁灭者, ...
- Codeforces963D. Frequency of String
$n \leq 100000$的一文本串,给$m \leq 100000$个询问,每次问一小串在文本串的哪一个最短的子串里出现指定次数.注意,询问串互不相同,且总长度$\leq 100000$. 比赛 ...
- 从dataset表中获取某一列的所有值方法
原文发布时间为:2008-07-31 -- 来源于本人的百度文章 [由搬家工具导入] 可以datarow遍历所有行即可,如下:pubauthor这个表中的au_lname的所有值加到listbox上面 ...
- oracle查询当前用户名下所有表
select * from all_tables where owner='TEST': TEST为用户名,用户名必须是大写. 查看当前登录的用户的表: select table_name from ...
- codevs——1065 01字符串
1065 01字符串 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 输出仅有0和1组成的长度为n的字符串, ...
- C标准提前定义宏,调试时加打印非常实用
#include<stdio.h> int main(int argc, char *argv[]) { printf("File:[%s]\r\n", __FILE_ ...
- [NPM] Set default values for package.json using npm set
Npm by default uses global values when initializing a new package.json file. Learn how to set your o ...
- 全志a13开发总结
这几天因为工作的原因,開始接触全志a13芯片,本人在网上搜集了好长时间,可是网上的资料对这方面的描写叙述是很少的, 所以,仅仅能靠数据手冊还有官网上面的英文文档进行开发了,下面仅仅是开发中的非常少的一 ...