mtk平台充电器检测

平台:mt6735 5.1

首先来看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 */
		BMT_status.nPercent_ZCV = battery_meter_get_battery_nPercent_zcv();
		battery_meter_initilized = KAL_TRUE;
	}

	dodprint();
	mt_battery_charger_detect_check();
	mt_battery_GetBatteryData();
	if (BMT_status.charger_exist == KAL_TRUE) {
		check_battery_exist();
	}
	mt_battery_thermal_check();
	mt_battery_notify_check();

	if (BMT_status.charger_exist == KAL_TRUE) {
		mt_battery_CheckBatteryStatus();
		mt_battery_charging_algorithm();
	}

	mt_battery_update_status();
	mt_kpoc_power_off_check();
}

BAT_thread()是battery系统核心的一个函数,每10秒钟被调用一次(通过hrtimer定时器来实现)。

跟充电相关的首先是mt_battery_charger_detect_check()函数,这个函数首先是检测充电器是否存在,如果充电器存在然后再检测充电器的类型。

static void mt_battery_charger_detect_check(void)
{
	if (upmu_is_chr_det() == KAL_TRUE) {
		wake_lock(&battery_suspend_lock);

		#if !defined(CONFIG_MTK_DUAL_INPUT_CHARGER_SUPPORT)
		BMT_status.charger_exist = KAL_TRUE;
		#endif

#if defined(CONFIG_MTK_WIRELESS_CHARGER_SUPPORT)
		mt_charger_type_detection();

		if ((BMT_status.charger_type == STANDARD_HOST)
		    || (BMT_status.charger_type == CHARGING_HOST)) {
			mt_usb_connect();
		}
#else
		#if !defined(CONFIG_MTK_DUAL_INPUT_CHARGER_SUPPORT)
		if (BMT_status.charger_type == CHARGER_UNKNOWN) {
		#else
		if ((BMT_status.charger_type == CHARGER_UNKNOWN) &&
		    (DISO_data.diso_state.cur_vusb_state == DISO_ONLINE)) {
		#endif
			mt_charger_type_detection();

			if ((BMT_status.charger_type == STANDARD_HOST)
			    || (BMT_status.charger_type == CHARGING_HOST)) {
				mt_usb_connect();
			}
		}
#endif

		battery_log(BAT_LOG_CRTI, "[BAT_thread]Cable in, CHR_Type_num=%d\r\n",
				    BMT_status.charger_type);

	} else {
		wake_unlock(&battery_suspend_lock);

		BMT_status.charger_exist = KAL_FALSE;
		BMT_status.charger_type = CHARGER_UNKNOWN;
		BMT_status.bat_full = KAL_FALSE;
		BMT_status.bat_in_recharging_state = KAL_FALSE;
		BMT_status.bat_charging_state = CHR_PRE;
		BMT_status.total_charging_time = 0;
		BMT_status.PRE_charging_time = 0;
		BMT_status.CC_charging_time = 0;
		BMT_status.TOPOFF_charging_time = 0;
		BMT_status.POSTFULL_charging_time = 0;

		battery_log(BAT_LOG_CRTI, "[BAT_thread]Cable out \r\n");

		mt_usb_disconnect();
	}
}
检测充电器是否存在调用的是upmu_is_chr_det()函数,这个函数调用的是get_charger_detect_status()函数去检测的,最终这个函数会去读取pmic mt6328芯片的CHR_CON0寄存器,而这个寄存器的第5位标明了是否有充电器的插入。

需要注意的是,充电器插入与拔出是通过硬件来检测的。默认充电器的检测电压范围是4.3v~6.5v,只要插入充电器的电压在这个范围内都是能够检测到充电器插入的。那么这个范围也是可以设置的,在mt6735平台上充电器低电压使用的是该寄存器的默认值4.3v,而高电压定义在cust_charging.h中,即V_CHARGER_MAX,默认值为6.5v,这个值最终是会被设置到mt6328的CHR_CON1寄存器的高4位中。

先回到mt_battery_charger_detect_check()函数这里,如果检测到了有充电器的插入,再调用mt_charger_type_detection()函数去识别充电器的类型,例如是USB充电,还是AC充电器(一般AC充电器的d+、d-是短接的)。

回到BAT_thread()函数,充电器检测完成之后,调用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 <= 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_charger_voltage()函数得到的。
kal_int32 battery_meter_get_charger_voltage(void)
{
	int ret = 0;
	int val = 0;

	val = 5;		/* set avg times */
	ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_CHARGER, &val);

	/* val = (((R_CHARGER_1+R_CHARGER_2)*100*val)/R_CHARGER_2)/100; */
	return val;
}
这个电压值是pmic mt6328通过adc采样得到的,最终调用的是PMIC_IMM_GetOneChannelValue()函数,adc通道是通道2。

在PMIC_IMM_GetOneChannelValue()函数里,首先是读取adc采样得到的值,然后再转换成电压值,这个电压值的范围是0~1.8v,注意最后返回的单位是mv,比如说510,那么采样得到的值就是510mv。

注意adc采样电压范围是0~1.8v,所以vbus电压不能直接接到pmic上,mtk参考电路是接两个电阻分压之后接入pmic的,这个两个电阻值分别是330k、39k,电阻值最好不要做更换。

采样得到的那个值,还要经过一个公式计算才能得到实际的充电器电压值,公式为(R1 = 330k,R2 = 39k):
(adc_val / R2) * (R1 + R2)
例如前面采样得到510mv,经过计算得到实际的充电器电压值为4825mv。

如果插入充电器的电压值超过默认的6.5v怎么办,会有两个问题:
1. 识别不了充电器的插入。
2. 这里也会得到充电器的电压值,但是这个电压值超过了6.5v这个门限,那么这里会给出充电器过压的警示。


Ok,BAT_thread()函数只是读取充电器电压、充电电流等信息,真正检测充电器的插入与拔出在pmic.c的chrdet_int_handler()函数中。
void chrdet_int_handler(void)
{
	PMICLOG("[chrdet_int_handler]CHRDET status = %d....\n",pmic_get_register_value(PMIC_RGS_CHRDET));

#ifdef CONFIG_MTK_KERNEL_POWER_OFF_CHARGING
	if (!upmu_get_rgs_chrdet())
	{
	    int boot_mode = 0;
	    boot_mode = get_boot_mode();
	    
	    if(boot_mode == KERNEL_POWER_OFF_CHARGING_BOOT || boot_mode == LOW_POWER_OFF_CHARGING_BOOT)
	    {
	        PMICLOG("[chrdet_int_handler] Unplug Charger/USB In Kernel Power Off Charging Mode!  Shutdown OS!\r\n");
	        mt_power_off();
	    }
	}
#endif
	pmic_set_register_value(PMIC_RG_USBDL_RST,1);
	do_chrdet_int_task();
}
充电器的插入、拔出都会调用该函数,这个检测都是通过硬件来完成的,只要插入充电器的电压值在合适范围内都会被认为有充电器插入,反之则不会检测到充电器。

chrdet_int_handler()函数被调用之后,最终还是会去读取pmic的CHR_CON0寄存器,判断是插入还是拔出,最后识别出充电器的类型。


总结:充电器的插入与拔出是通过硬件来检测的,BAT_thread()函数只是将充电器电压、充电电流这些信息读取出来,作为警示作用或者用作其他。


// 2016-11-11 add
如何支持12充电器充电呢?


// 2017-03-31 add
充电电流的获取

前面只说了充电器电压的获取,在电池系统里面,充电电流也是一个很重要的参数,那么充电电流如何得到呢?

我们知道电流计算公式为:I = (V / R),那么在mtk平台里面呢,电流的检测也是通过这个公式得来的。

计算流程是这样的,首先在电池VBAT引脚和充电IC之间串联一个电阻,通过ADC读取这个电阻两端的电压值,这个差值再除以电阻值,就得到充电电流值,在mt6735平台上,这个电阻的阻值默认为68毫欧姆。

注意,这个电阻的两端分别接到MT6328的BATSNS、ISENSE引脚上的,分别对应ADC通道的0和1,同时电池的电压也是通过ADC通道0读取的。

来看代码,在mt_battery_GetBatteryData()函数里面调用battery_meter_get_charging_current()来获取充电电流值:

kal_int32 battery_meter_get_charging_current(void)
{
#ifdef DISABLE_CHARGING_CURRENT_MEASURE
	return 0;
#elif !defined (EXTERNAL_SWCHR_SUPPORT)
	kal_int32 ADC_BAT_SENSE_tmp[20] =
	    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
	kal_int32 ADC_BAT_SENSE_sum = 0;
	kal_int32 ADC_BAT_SENSE = 0;
	kal_int32 ADC_I_SENSE_tmp[20] =
	    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
	kal_int32 ADC_I_SENSE_sum = 0;
	kal_int32 ADC_I_SENSE = 0;
	int repeat = 20;
	int i = 0;
	int j = 0;
	kal_int32 temp = 0;
	int ICharging = 0;
	int ret = 0;
	int val = 1;

	for (i = 0; i < repeat; i++) {
		val = 1;	/* set avg times */
		ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE, &val);
		ADC_BAT_SENSE_tmp[i] = val;

		val = 1;	/* set avg times */
		ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_I_SENSE, &val);
		ADC_I_SENSE_tmp[i] = val;

		ADC_BAT_SENSE_sum += ADC_BAT_SENSE_tmp[i];
		ADC_I_SENSE_sum += ADC_I_SENSE_tmp[i];
	}

	/* sorting    BAT_SENSE */
	for (i = 0; i < repeat; i++) {
		for (j = i; j < repeat; j++) {
			if (ADC_BAT_SENSE_tmp[j] < ADC_BAT_SENSE_tmp[i]) {
				temp = ADC_BAT_SENSE_tmp[j];
				ADC_BAT_SENSE_tmp[j] = ADC_BAT_SENSE_tmp[i];
				ADC_BAT_SENSE_tmp[i] = temp;
			}
		}
	}

	bm_print(BM_LOG_FULL, "[g_Get_I_Charging:BAT_SENSE]\r\n");
	for (i = 0; i < repeat; i++) {
		bm_print(BM_LOG_FULL, "%d,", ADC_BAT_SENSE_tmp[i]);
	}
	bm_print(BM_LOG_FULL, "\r\n");

	/* sorting    I_SENSE */
	for (i = 0; i < repeat; i++) {
		for (j = i; j < repeat; j++) {
			if (ADC_I_SENSE_tmp[j] < ADC_I_SENSE_tmp[i]) {
				temp = ADC_I_SENSE_tmp[j];
				ADC_I_SENSE_tmp[j] = ADC_I_SENSE_tmp[i];
				ADC_I_SENSE_tmp[i] = temp;
			}
		}
	}

	bm_print(BM_LOG_FULL, "[g_Get_I_Charging:I_SENSE]\r\n");
	for (i = 0; i < repeat; i++) {
		bm_print(BM_LOG_FULL, "%d,", ADC_I_SENSE_tmp[i]);
	}
	bm_print(BM_LOG_FULL, "\r\n");

	ADC_BAT_SENSE_sum -= ADC_BAT_SENSE_tmp[0];
	ADC_BAT_SENSE_sum -= ADC_BAT_SENSE_tmp[1];
	ADC_BAT_SENSE_sum -= ADC_BAT_SENSE_tmp[18];
	ADC_BAT_SENSE_sum -= ADC_BAT_SENSE_tmp[19];
	ADC_BAT_SENSE = ADC_BAT_SENSE_sum / (repeat - 4);

	bm_print(BM_LOG_FULL, "[g_Get_I_Charging] ADC_BAT_SENSE=%d\r\n", ADC_BAT_SENSE);

	ADC_I_SENSE_sum -= ADC_I_SENSE_tmp[0];
	ADC_I_SENSE_sum -= ADC_I_SENSE_tmp[1];
	ADC_I_SENSE_sum -= ADC_I_SENSE_tmp[18];
	ADC_I_SENSE_sum -= ADC_I_SENSE_tmp[19];
	ADC_I_SENSE = ADC_I_SENSE_sum / (repeat - 4);

	bm_print(BM_LOG_FULL, "[g_Get_I_Charging] ADC_I_SENSE(Before)=%d\r\n", ADC_I_SENSE);


	bm_print(BM_LOG_FULL, "[g_Get_I_Charging] ADC_I_SENSE(After)=%d\r\n", ADC_I_SENSE);

	if (ADC_I_SENSE > ADC_BAT_SENSE) {
		ICharging = (ADC_I_SENSE - ADC_BAT_SENSE + g_I_SENSE_offset) * 1000 / CUST_R_SENSE;
	} else {
		ICharging = 0;
	}

	return ICharging;
#else
    return 0;
#endif
}
在battery_meter_get_charging_current()函数里面,首先是使用ADC采样电压值,都读取20次,同时计算出20次采样的总和。

然后对采样出来的值做一个排序,去掉最小的两个值和最大的两个值,然后计算出平均值。

最后ISENSE通道计算出来的值减去BATSNS除以电阻就得到充电电流值,这个电阻是通过宏CUST_R_SENSE来定义的,默认是68,即68毫欧,需要根据自己项目使用的值来适配。

你可能感兴趣的:(mtk开发)