MTK平台下Battery驱动分析

主要涉及代码:

Kernel:

kernel-3.10\drivers\power\mediatek\

kernel-3.10\drivers\misc\mediatek\mach\mt6580\<project_name>\power\

MTK Battery框架结构图

通过上层通过读取创建一系列的设备节点获取电池相关的状态信息

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 //电池技术

代码框架:

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:

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的整个的一个流程如下图所示:

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

时间: 2024-10-27 17:00:44

MTK平台下Battery驱动分析的相关文章

Android Ubuntu平台下ADB驱动的安装

在Android平台下做开发,adb总是需要使用到的,同时,因为linux没有windows这样操作傻瓜化,有些东西还是需要自行设置的,否则将会连接不上. 关于这些内容,google也有一定的描述,可以参考http://developer.android.com/tools/device.html 1. 确保设备已经连接正常 首先需要取得root权限,这个没啥说的.然后用lsusb命令列一下所有USB设备,如下图所示: 这里可以比较清楚的看到有一个设备,ID号是1782:5d04,如果你的系统上

windows平台下的oracle ORA-01031的解决方法

今天下午遇到一个很怪异的问题,在windows平台下sqlplus  / as sysdba登陆数据库,提示权限不足, 当时就纳闷了,sys用户登陆数据库还能权限不足,问题出现了,就开始寻找解决方法呗 首先查看$ORACLE_HOME/network/admin/sqlnet.ora中SQLNET.AUTHENTICATION_SERVICES项的配置信息, 网上的大多数帖子一致结论为: 在windows平台上,SQLNET.AUTHENTICATION_SERVICES必须设置为NTS或者AL

【MongoDB】在windows平台下mongodb的分片集群(五)

本篇接着上面的四篇继续讲述在window平台下mongodb的分片集群搭建.在分片集群中也照样可以创建索引,创建索引的方式与在单独数据库中创建索引的方式一样.因此这不再多说.本篇主要聚焦在分片键的选取问题上. 分片键通俗来说就是分割海量数据的标记符. 如果更高效的划分海量数据往往依赖于分片键的选择. 分片键选得不好,应用程序就无法利用分片集群所提供的诸多优势.在这种情况下,查询和插入得系能都回显著下降. 一.低效的分片键 1.1 分布差 BSON对象ID是每个mongodb文档的默认主键.所有的

不同平台下int类型、指针类型的数据大小

不同平台下int类型.指针类型的数据大小 对于int类型数据和指针类型数据的大小,是非常基础的问题. 在一个具体的平台上,确定他们最好的办法就是使用sizeof(type)对其进行判断,返回当前数据类型的大小. 在不同的平台下,int类型和指针类型的数据类型大小时怎样的呢?如果要给出一个统一的答案,自然不可能集齐每个平台,一个个地去试,我们必须从底层进行分析. 数据总线和地址总线 计算机内的数据总线是CPU与外设进行数据交换的通路,而地址总线则是CPU用于寻址的通路. 数据总线的位数决定了CPU

MAC平台下Xcode配置使用OpenCV的具体方法 (2016最新)

1.序言: 1.1 背景 本人小白一枚,不过因为最近在从事机器视觉方面的工作,所以接触到OpenCV. 因为工作需求,本人要在MAC端使用OpenCV实现一些视觉功能,配置环境成了最大的阻碍,网上查了很多相关资料和博客,都因为版本环境问题屡试屡败,不过经历重重尝试,笔者最终还是配置成功并运行了自己的源码.当然成功的关键还是因为笔者站在了巨人的肩膀上,借鉴了很多网上的教程,为了不误导大家配置的过程,参考文章的地址统一放在文章里,望各位大大看见之后能够理解,废话不说进入正题. 1.2 环境说明 如果

win7平台下React-Native Android:Unable to upload some APKs

一.问题描述 根据网络上的Win7平台下React-native配置教程配置好开发环境的过程中,在艰难进行到react-native run-android这一步时,发现一直出现错误,截图如下: 错误的说明信息表明是 Ubable to upload some APKs.经过网上搜索最后在这里(http://www.hacksparrow.com/react-native-android-unable-to-upload-some-apks.html)得到了一个比较全面的答案,我简单地翻译一下原

ios及android两个平台下x264编译脚本

今年做了一些音视频编解码工作,此前做图形.图像类工作多一些,做了音视频编解码才知道,这项工作还是很有趣.很神奇的.比如采集几十M的视频数据,经过编码存储后,文件体积仅仅有几十K大小,这当中就需要x264来发挥神奇了.下面将手机端ios及android两个平台下x264编译脚本分享出来,希望对正在做相关工作的同学有帮助.x264源码地址:http://www.videolan.org/developers/x264.html 1. ios平台下x264编译.这个编译脚本修改自:https://gi

20150311 NandFlash驱动分析

20150311 NandFlash驱动分析 2015-03-11 李海沿 一.结构体详解 MTD体系结构: 在linux中提供了MTD(Memory Technology Device,内存技术设备)系统来建立Flash针对linux的统一.抽象的接口 引入MTD后,linux系统中的Flash设备驱动及接口可分为4层: 设备节点 MTD设备层 MTD原始设备层 硬件驱动层 硬件驱动层:Flash硬件驱动层负责底层硬件设备实际的读.写.擦除,Linux MTD设备的NAND型Flash驱动位于

linux串口驱动分析

linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作.UART 使用系统时钟能够支持最高 115.2Kbps 的波特率.每一个 UART 通道对于接收器和发送器包含了 2 个 64 位的 FIFO. 寄存器 名称 地址 在linux中的描写叙述 (2410 和 2440 处理器对内存地址映射关系同样) UART 线性控制寄存器(ULCONn) ULC