MTK Battery系统
来源:互联网 发布:淘宝卖家发货怎么打印 编辑:程序博客网 时间:2024/05/18 03:48
MTK方案的电池充电过程分为预充、恒流充电(CC模式)、恒压充电(CV模式)三种模式,整个充电过程如下充电状态图所示:
从充电状态图看出来,刚开始充电的时候,代码先判断是插USB充电还是插ac充电,电池在进入充电阶段分为快速充电、CC(恒流充电)、CV(恒压充电)。而从CC模式切换到CV模式在代码中的alps/mediatek/kernel/drivers/power/linear_charging.c和alps/mediatek/kernel/drivers/power/ switch_charging.c。
MTK Battery系统驱动的大致流程主要是通过系统platform总线注册device和driver,然后在probe函数里面创建了一个线程,然后创建一个hrtimer定时器,定时器没10s运行一次,同时在probe函数里面会创建一些设备节点,通过这些设备节点,系统将每10s更新的数据上传给上层供上层调用显示。
1、battery硬件原理图
首先先介绍几个电压值:
VCHG:USB正极
VCDT:充电电压检测脚
ISENSE:充电电流检测电阻的正极
BATSNS:充电电流检测电阻的负极
BAT:电池正极引脚
BAT_ON:电池NTC(热敏电阻)引脚
通过三极管可以开启,关闭充电功能,开启充电的时候调节三极管的基级电流可以控制流过三极管CE端的电流从而实现充电电流大小的设置。
Rsense采样电阻:对于软件来说可以测量充电电流的大小:电流计算方法:(ISENSE – VBAT)/Rsense。对于PMU来说通过Rsense可以实现电流控制。比如要实现1A的充电电流,Rsense为0.2欧。PMU实现该电流的方法就是设法一直保证Rsense两端的电压是0.2V。
BAT_ON在充电中的作用:
1,检测电池是否存在。电压必须小于1.062V,否则认为电池不存在不能充电,硬件行为,软件无法关闭此功能
2,高温检测,电池电压必须大于0.2V,否则认为电池温度过高不能充电,硬件行为,软件可以关闭此功能
3,软件检测电池温度。
上图中NTC电阻会串联一个24k的电阻,这个会在后面源代码分析时起作用。2、Battery架构简析
MTK电池显示的具体过程为:硬件ADC读取Battery的各路信息:包括温度,电压等。然后利用MTK开发的电量算法分析得到的数据。Kernel层将电量信息通过写文件节点的方式更新,并通过UEVENT通知上层。上层Service开启UEVENT LISTENER,监听到UEVENT后,读取battery相关文件节点,获取电量信息。Service更新数据后,通过Broadcast通知所有开启了相关listener的activities。
根据不同的电量读取和计算的策略,第一步的读取和第二步的算法部分会有比较大的差异,而后面的数据更新和事件通知部分一致性较高。3、MTK电池驱动分析
在分析源代码前,先来看看PMU_ChargerStruct这个结构体。
- 146 typedef struct
- 147 {
- 148 kal_bool bat_exist; // 判断电池是否存在
- 149 kal_bool bat_full; //判断电池是否充满
- 150 INT32 bat_charging_state; //判断充电状态
- 151 UINT32 bat_vol; //电池平均电压
- 152 kal_bool bat_in_recharging_state; //电池是否在回充
- 153 kal_uint32 Vsense; // 电池瞬间电压
- 154 kal_bool charger_exist; // Charger是否存在Charger电压
- 155 UINT32 charger_vol; // Charger电压
- 156 INT32 charger_protect_status; //充电保护状态,过流或者过压保护状态
- 157 INT32 ICharging; // 充电电流
- 158 INT32 IBattery;
- 159 INT32 temperature; // 电池温度
- 160 INT32 temperatureR;
- 161 INT32 temperatureV;
- 162 UINT32 total_charging_time; //总的充电时间
- 163 UINT32 PRE_charging_time; // Pre cc充电时间
- 164 UINT32 CC_charging_time; //cc充电时间
- 165 UINT32 TOPOFF_charging_time; //TOPOFF充电时间
- 166 UINT32 POSTFULL_charging_time; //Postfull充电时间
- 167 UINT32 charger_type; //充电器类型
- 168 INT32 SOC; //底层的电量
- 169 INT32 UI_SOC; // 上层的电量
- 170 UINT32 nPercent_ZCV;
- 171 UINT32 nPrecent_UI_SOC_check_point; //N%同步点对应的开路电压以及UI电量
- 172 UINT32 ZCV; //电池当前开路电压
- 173 } PMU_ChargerStruct;
下面来看看代码的具体实现。
MTK的battery的注册是通过platform总线实现的,在mediatek/kernel/drivers/power/battery_common.c中,我们会看到对battery的device和driver进行注册的函数,device和driver通过name进行匹配,device和driver注册后,就是执行battery_driver结构体里面的函数,执行battery_driver里面的函数首先运行的是battery_probe函数,在battery_probe函数里面,先对battery进行字符设备的注册,注册的字符设备名为MT_pmic_adc_cali。在注册完字符设备后,probe会执行get_charging_control();这个函数,这个函数在整个充电过程中起着很重要的作用,该函数就一行代码:
- static void get_charging_control(void)
- {
- battery_charging_control = chr_control_interface;
- }
- kal_int32 chr_control_interface(CHARGING_CTRL_CMD cmd, void *data)
- {
- kal_int32 status;
- if(cmd < CHARGING_CMD_NUMBER)
- status = charging_func[cmd](data);
- else
- return STATUS_UNSUPPORTED;
- return status;
- }
- typedef enum
- {
- CHARGING_CMD_INIT,
- CHARGING_CMD_DUMP_REGISTER,
- CHARGING_CMD_ENABLE,
- CHARGING_CMD_SET_CV_VOLTAGE,
- CHARGING_CMD_GET_CURRENT,
- CHARGING_CMD_SET_CURRENT,
- CHARGING_CMD_SET_INPUT_CURRENT,
- CHARGING_CMD_GET_CHARGING_STATUS,
- CHARGING_CMD_RESET_WATCH_DOG_TIMER,
- CHARGING_CMD_SET_HV_THRESHOLD,
- CHARGING_CMD_GET_HV_STATUS,
- CHARGING_CMD_GET_BATTERY_STATUS,
- CHARGING_CMD_GET_CHARGER_DET_STATUS,
- CHARGING_CMD_GET_CHARGER_TYPE,
- CHARGING_CMD_GET_IS_PCM_TIMER_TRIGGER,
- CHARGING_CMD_SET_PLATFORM_RESET,
- CHARGING_CMD_GET_PLATFORM_BOOT_MODE,
- CHARGING_CMD_SET_POWER_OFF,
- CHARGING_CMD_NUMBER
- } CHARGING_CTRL_CMD;
- static kal_uint32 (*charging_func[CHARGING_CMD_NUMBER])(void *data)=
- {
- charging_hw_init
- ,charging_dump_register
- ,charging_enable
- ,charging_set_cv_voltage
- ,charging_get_current
- ,charging_set_current
- ,charging_set_input_current // not support, empty function
- ,charging_get_charging_status // not support, empty function
- ,charging_reset_watch_dog_timer
- ,charging_set_hv_threshold
- ,charging_get_hv_status
- ,charging_get_battery_status
- ,charging_get_charger_det_status
- ,charging_get_charger_type
- ,charging_get_is_pcm_timer_trigger
- ,charging_set_platform_reset
- ,charging_get_platfrom_boot_mode
- ,charging_set_power_off
- };
当probe函数注册完了字符设备后,函数进行的随后的进行的操作是在sys下面建立设备节点,总共建立了四个设备节点,分别为ac_main、usb_main、wireless_main和battery_main,这四个节点分别为使用适配器、USB、无线充电以及使用电池供电。电池电量发生变化的时候,会通过这些节点将数据上报给上层,也就是说上层是通过这些节点来读取底层电池电量变化的数据的。
当初始化完成后,probe函数会创建一个hrtimer定时器,定时器启动bat_thread_kthread函数,bat_thread_kthread函数中的while(1)里面包含了BAT_thread()函数,BAT_thread()就是充电的核心函数。
- int bat_thread_kthread(void *x)
- {
- ktime_t ktime = ktime_set(3, 0); // 10s, 10* 1000 ms
- /* Run on a process content */
- while (1) {
- mutex_lock(&bat_mutex);
- if((chargin_hw_init_done == KAL_TRUE) && (battery_suspended == KAL_FALSE))
- BAT_thread();
- mutex_unlock(&bat_mutex);
- battery_xlog_printk(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
- if( chr_wake_up_bat == KAL_TRUE && g_smartbook_update != 1) // for charger plug in/ out
- {
- g_smartbook_update = 0;
- battery_meter_reset();
- chr_wake_up_bat = KAL_FALSE;
- battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] Charger plug in/out, Call battery_meter_reset. (%d)\n", BMT_status.UI_SOC);
- }
- }
- return 0;
- }
- void BAT_thread(void)
- {
- static kal_bool battery_meter_initilized = KAL_FALSE;
- _g_bat_sleep_total_time = 0;
- 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;
- }
- mt_battery_charger_detect_check();
- mt_battery_GetBatteryData();
- 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();
- }
battery_meter_initial函数为系统启动时运行的,也电池充电做一些初始化操作。MTK有AUXADC、SW_FG、HW_FG三种不同的电池算法方案,这三种方案分别初始化,因为82平台采用的SW_FG,所以接下去先主要分析SW_FG的流程。而SW_FG中主要是利用线性插值算法来重构zcv表格,利用积分算法求电池的当前电量。
先来看看系统是如何利用线性插值的方法来求电池的温度,重构zcv表格,在table_init函数中:
- void table_init(void)
- {
- ..........
- int temperature = force_get_tbat(); //求电池的温度
- // Re-constructure r-table profile according to current temperature
- profile_p_r_table = fgauge_get_profile_r_table(TEMPERATURE_T);
- if (profile_p_r_table == NULL)
- {
- bm_print(BM_LOG_CRTI, "[FGADC] fgauge_get_profile_r_table : create table fail !\r\n");
- }
- fgauge_construct_r_table_profile(temperature, profile_p_r_table);
- // Re-constructure battery profile according to current temperature
- profile_p = fgauge_get_profile(TEMPERATURE_T);
- if (profile_p == NULL)
- {
- bm_print(BM_LOG_CRTI, "[FGADC] fgauge_get_profile : create table fail !\r\n");
- }
- fgauge_construct_battery_profile(temperature, profile_p);
- }
- int force_get_tbat(void)
- {
- ……………
- bat_temperature_volt = 2;
- //求的NTC电阻的电压,也就是原理图中的BAT_ON
- ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_TEMP, &bat_temperature_volt);
- if(bat_temperature_volt != 0)
- {
- ……………
- bat_temperature_val = BattVoltToTemp(bat_temperature_volt);
- }
- return bat_temperature_val;
- #endif
- }
NTC电阻就是通过与电阻的串联跟并联并且通过电压值来得到的。计算出系统当前NTC电阻的电阻值后,然后就调用BattThermistorConverTemp函数进行查表,对比出当前系统的温度。而BattThermistorConverTemp函数是通过alps/mediatek/custom/mt6582/kernel/battery/battery/cust_battery_meter_table.h中的Batt_Temperature_Table结构体,然后根据电阻值落在哪个区间,根据线性插值的方法求出当前电池的温度。 然后在回到table_init函数,MTK的zcv电池参数表格会预先测得的在-100 25 50 摄氏度开路电量跟放电深度之间的关系。结合真实的温度值,系统会自己构建一张当前温度值的ZCV电池曲线表格。
- // Re-constructure r-table profile according to current temperature
- profile_p_r_table = fgauge_get_profile_r_table(TEMPERATURE_T); //返回NTC电阻跟电压表格
- if (profile_p_r_table == NULL)
- {
- bm_print(BM_LOG_CRTI, "[FGADC] fgauge_get_profile_r_table : create table fail !\r\n");
- }
- //动态构建一个NTC电阻跟电压关系的表格
- fgauge_construct_r_table_profile(temperature, profile_p_r_table);
- R_PROFILE_STRUC_P fgauge_get_profile_r_table(kal_uint32 temperature)
- {
- switch (temperature)
- {
- case TEMPERATURE_T0:
- return &r_profile_t0[g_fg_battery_id][0];
- break;
- case TEMPERATURE_T1:
- return &r_profile_t1[g_fg_battery_id][0];
- break;
- case TEMPERATURE_T2:
- return &r_profile_t2[g_fg_battery_id][0];
- break;
- case TEMPERATURE_T3:
- return &r_profile_t3[g_fg_battery_id][0];
- break;
- case TEMPERATURE_T:
- return &r_profile_temperature[0];
- break;
- default:
- return NULL;
- break;
- }
- }
- for (i = 0; i < saddles; i++) // 构建表格中的电压值
- {
- if( ((high_profile_p + i)->voltage) > ((low_profile_p + i)->voltage) )
- {
- temp_v_1 = (high_profile_p + i)->voltage;
- temp_v_2 = (low_profile_p + i)->voltage;
- (temp_profile_p + i)->voltage = temp_v_2 + //采用线性平均法求电压
- (
- (
- (temperature - low_temperature) *
- (temp_v_1 - temp_v_2)
- ) /
- (high_temperature - low_temperature)
- );
- }
- else
- {
- temp_v_1 = (low_profile_p + i)->voltage;
- temp_v_2 = (high_profile_p + i)->voltage;
- (temp_profile_p + i)->voltage = temp_v_2 +
- (
- (
- (high_temperature - temperature) *
- (temp_v_1 - temp_v_2)
- ) /
- (high_temperature - low_temperature)
- );
- }
- }
- /* Interpolation for R_BAT */
- for (i = 0; i < saddles; i++) // 构建表格中的NTC电阻值
- {
- if( ((high_profile_p + i)->resistance) > ((low_profile_p + i)->resistance) )
- {
- temp_r_1 = (high_profile_p + i)->resistance;
- temp_r_2 = (low_profile_p + i)->resistance;
- (temp_profile_p + i)->resistance = temp_r_2 + //采用线性平均法求电阻
- (
- (
- (temperature - low_temperature) *
- (temp_r_1 - temp_r_2)
- ) /
- (high_temperature - low_temperature)
- );
- }
- else
- {
- temp_r_1 = (low_profile_p + i)->resistance;
- temp_r_2 = (high_profile_p + i)->resistance;
- (temp_profile_p + i)->resistance = temp_r_2 +
- (
- (
- (high_temperature - temperature) *
- (temp_r_1 - temp_r_2)
- ) /
- (high_temperature - low_temperature)
- );
- }
- }
table_init后面利用同样的原理,动态生成了一个开路电压与放电深度关系的结构体,这里就不介绍了。
MTK系统是通过battery_meter_get_battery_percentage函数来读取当前电池的电量的,然后在通过设备结点,通过给上层调用。battery_meter_get_battery_percentage函数主要就是调用oam_run函数来实现电流的库伦算法。积分法是利用电流计算公式 I = Q/t来求的,它的优点时适用于各种电池,但缺点是初始电量无法获取。
而整个积分过程可参考下图:
- void oam_run(void)
- {
- ……
- //now_time = rtc_read_hw_time();
- getrawmonotonic(&now_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); //得到闭路电压
- oam_i_1 = (((oam_v_ocv_1-vol_bat)*1000)*10) / oam_r_1; //0.1mA
- 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
- 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; //gFG_BATT_CAPACITY_aging is Q_MAX
- 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_run函数中算法的大致思路为:系统当前的电量通过最终的开路电压oam_v_ocv_1查ZCV表得到当前的电量值,而最终开路电压需要通过闭路电压v_bat和闭路电流oam_i_2 去回溯电池内阻,逐次逼近,而oam_i_2 通过另一种方式即电量积分更新的电压oam_v_ocv_2来得到。
下面看看具体代码的实现。
- vol_bat = 15; //set avg times
- ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE, &vol_bat);
- oam_i_1 = (((oam_v_ocv_1-vol_bat)*1000)*10) / oam_r_1; //0.1mA
- 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
- oam_car_2 = (oam_i_2*delta_time/3600) + oam_car_2; //0.1mAh
这里的关键是oam_i_2,这边的I2 有几个作用:
1、因为电流是通过上图的内阻IR drop得到的,而方式一内阻回溯逼近开路电压本质也是IR drop,如果使用oam_i_1 则没有意义,只能使用不同体系的I2.
2、方式二 电流积分求电量查表 同样依赖 oam_i_2 这个体系是累积积分不需要引用其他体系的参数
3、I2的方向作为充电还是放电的依据
而oam_i_1只有作用3。这里还要注意的是oam_i_1和oam_i_2的单位是0.1mA。而oam_car_1和oam_car_2 是累积电量,同样对应的单位是0.1mAh,显然oam_car_2是算法的有效参数。
- oam_v_ocv_1 = vol_bat + mtk_imp_tracking(vol_bat, oam_i_2, 5);
- kal_int32 mtk_imp_tracking(kal_int32 ori_voltage, kal_int32 ori_current, kal_int32 recursion_time)
- {
- kal_int32 ret_compensate_value = 0;
- kal_int32 temp_voltage_1 = ori_voltage;
- kal_int32 temp_voltage_2 = temp_voltage_1;
- int i = 0;
- for(i=0 ; i < recursion_time ; i++)
- {
- gFG_resistance_bat = fgauge_read_r_bat_by_v(temp_voltage_2);
- ret_compensate_value = ( (ori_current) * (gFG_resistance_bat + R_FG_VALUE)) / 1000;
- ret_compensate_value = (ret_compensate_value+(10/2)) / 10;
- temp_voltage_2 = temp_voltage_1 + ret_compensate_value;
- bm_print(BM_LOG_FULL, "[mtk_imp_tracking] temp_voltage_2=%d,temp_voltage_1=%d,ret_compensate_value=%d,gFG_resistance_bat=%d\n",
- temp_voltage_2,temp_voltage_1,ret_compensate_value,gFG_resistance_bat);
- }
- gFG_resistance_bat = fgauge_read_r_bat_by_v(temp_voltage_2);
- ret_compensate_value = ( (ori_current) * (gFG_resistance_bat + R_FG_VALUE + FG_METER_RESISTANCE)) / 1000;
- ret_compensate_value = (ret_compensate_value+(10/2)) / 10;
- gFG_compensate_value = ret_compensate_value;
- bm_print(BM_LOG_FULL, "[mtk_imp_tracking] temp_voltage_2=%d,temp_voltage_1=%d,ret_compensate_value=%d,gFG_resistance_bat=%d\n",
- temp_voltage_2,temp_voltage_1,ret_compensate_value,gFG_resistance_bat);
- return ret_compensate_value;
- }
- 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);
- if(d5_count >= d5_count_time)
- {
- if(gFG_Is_Charging == KAL_FALSE)
- {
- if( oam_d_3 > oam_d_5 )
- {
- 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 )
- {
- 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;
- }
- else
- {
- d5_count = d5_count + 10;
- }
- MTK Battery系统
- MTK Battery系统
- MTK Battery系统
- MTK Battery系统
- MTK Battery系统
- MTK Android Driver :battery
- MTK Android Driver :battery
- MTK Android Driver :battery
- MTK Android Driver :battery
- MTK battery 原理
- MTK Android Driver :battery
- MTK Android Driver :battery
- MTK Android Driver :battery
- MTK平台下Battery驱动分析
- MTK平台下Battery驱动分析
- MTK平台下Battery驱动分析
- MTK平台下Battery驱动分析及充电流程
- MTK平台下Battery驱动分析及充电流程
- shell
- 关于博客中公式乱码的公告
- e
- Mysql 时间字段相关
- [黑苹果]G480 黑苹果之路
- MTK Battery系统
- websocket
- bad file descriptor
- Oracle数据库一体机助力零售走向互联网零售新模式
- MySQL索引类型总结和使用技巧以及注意事项
- caffe用自己的数据训练模型,并测试输出图片类别
- 端模式
- CentOS 7 设置默认进入图形界面或文本界面
- 多线程图片下载