zoukankan      html  css  js  c++  java
  • MTK平台下Battery驱动分析

    主要涉及代码:

    Kernel:

    kernel-3.10driverspowermediatek

    kernel-3.10driversmiscmediatekmachmt6580<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
    ");
    		wait_event(bat_thread_wq, (is_usb_rdy() == KAL_TRUE));
    		battery_log(BAT_LOG_CRTI, "CDP, free
    ");
    	} else{
    		battery_log(BAT_LOG_CRTI, "CDP, PASS
    ");
    	}
    #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 
    ");
    		}
    #endif //#ifdef FG_BAT_INT
    
    		battery_log(BAT_LOG_FULL, "wait event
    ");
    
    		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)
    ",
    					    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.
    ");
    		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.
    ");	
    	}
    	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)
    ",
    			    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



  • 相关阅读:
    ubuntu 16.04 连接无线网络
    linux的内存管理-抛砖引玉
    linux的进程调度-抛砖引玉
    AliOS Things 异步事件框架Yloop
    AliOS Things 云端物联网操作系统
    linux performance tools
    linux进程调度
    linux 内核Lockup机制浅析
    cache和内存屏障
    linux设备模型及实例
  • 原文地址:https://www.cnblogs.com/jzssuanfa/p/7252435.html
Copyright © 2011-2022 走看看