MT6580 Android N Battery框架总结

1.概念

        ZCV:开路电压
        OCV: 开路电压
        VC:闭路电压
        CAR:库伦计
        DOD: 放电深度,100-DOD 即电容容量
        Cmax/Qmax: 电池容量

2.相关文件关系

        Battery_common.c (s:\i841\mediatek\kernel\drivers\power)                    // 充电逻辑文件 
        Charging_hw_pmic.c (s:\i841\mediatek\platform\mt6582\kernel\drivers\power)  // 具体的充电芯片,相关电池参数检测
        Linear_charging.c (s:\i841\mediatek\kernel\drivers\power)                   // 充电状态控制,内部 PMIC
        Switch_charging.c (s:\i841\mediatek\kernel\drivers\power)                   // 充电状态控制,外部 Charge IC 

3.硬件原理图

NTC 检测温度电路原理如下:

                Vu                                Ru:上拉电阻值           
                ---                               Rd: 下拉电阻值 
                 |                                Rntc: NTC 温度电阻 阻值
                |||   Ru                          Vu: 上拉电压值 
                 |                                Gnd: 地 
            ---------- -----Vntc                  Vntc: NTC 电压 
            |        |
    Rntc   |||      |||  Rd 
            |        |
            ----------                          Rntc = (Ru*Rd*Vntc) / (Vru * Rd - Vntc * Ru)
                 |
               -----
                ---
                 -
                Gnd 

4.充电框架总结

1.0 battery_common.c ----->充电控制主线程
	kernel-3.18/drivers/power/mediatek/battery_common.c
		late_initcall(battery_init);
		
			static int __init battery_init(void)	
				ret = platform_device_register(&battery_device);
				ret = platform_driver_register(&battery_driver);
				
					struct platform_device battery_device = {
						.name = "battery",
						.id = -1,
					};				
					static struct platform_driver battery_driver = {
						.probe = battery_probe,
						.remove = battery_remove,
						.shutdown = battery_shutdown,
						.driver = {
							.name = "battery",
							.pm = &battery_pm_ops,
							},
					};
						
						/*在 Battery 驱动模块中, battery_probe 函数中会创建一些设备节点,并且运行一个线程 bat_thread_kthread 获取电池相关的数据信息*/
						static int battery_probe(struct platform_device *dev)
						
							//注册字符设备
								/* Integrate with NVRAM */
							ret = alloc_chrdev_region(&adc_cali_devno, 0, 1, ADC_CALI_DEVNAME);//动态申请一个主设备号
							adc_cali_cdev = cdev_alloc();//分配一个字符设备
							adc_cali_cdev->owner = THIS_MODULE;//填充该字符设备owner成员
							adc_cali_cdev->ops = &adc_cali_fops;//填充该字符设备的ops成员
							ret = cdev_add(adc_cali_cdev, adc_cali_devno, 1);//注册字符设备,把 adc_cali_cdev 字符设备注册到内核中去
							
							//创建相关的sys系统节点,根据udev机制,在 class_create 创建sys相关联的类文件下调用 device_create 就可以创建对应的设备节点
							adc_cali_major = MAJOR(adc_cali_devno);
							adc_cali_class = class_create(THIS_MODULE, ADC_CALI_DEVNAME);
							class_dev = (struct class_device *)device_create(adc_cali_class,
													NULL,
													adc_cali_devno, NULL, ADC_CALI_DEVNAME);
							
							//与特定硬件的入口操作函数关联
							get_charging_control();
								static void get_charging_control(void)
								{
									battery_charging_control = chr_control_interface;//用来绑定内部的PMIC 或者外部充电芯片的操作函数入口
								}
								
									signed int chr_control_interface(CHARGING_CTRL_CMD cmd, void *data)
									
									{	signed int status;
									
										if (cmd < CHARGING_CMD_NUMBER) {
											if (charging_func[cmd] != NULL)
												status = charging_func[cmd](data);
											else {
												battery_log(BAT_LOG_CRTI, "[chr_control_interface]cmd:%d not supported\n", cmd);
												status = STATUS_UNSUPPORTED;
											}
										} else
											status = STATUS_UNSUPPORTED;
									
										return status;
									}
							
							batt_init_cust_data();//初始化一些客制化的信息
							
							//获得启动模式
							battery_charging_control(CHARGING_CMD_GET_PLATFORM_BOOT_MODE, &g_platform_boot_mode);
							
							/* Integrate with Android Battery Service 
							当probe函数注册完了字符设备后,函数进行的随后的进行的操作是在sys下面建立设备节点,
							总共建立了四个设备节点,分别为ac_main、usb_main、wireless_main和battery_main,
							这四个节点分别为使用适配器、USB、无线充电以及使用电池供电。电池电量发生变化的时候,
							会通过这些节点将数据上报给上层,也就是说上层是通过这些节点来读取底层电池电量变化的数据的。
							通过 power_supply_register() 函数在 /sys/class/power_supply 下分别注册创建 ac battery usb wireless 四个文件节点
							*/
							ret = power_supply_register(&(dev->dev), &ac_main.psy);
						
							ret = power_supply_register(&(dev->dev), &usb_main.psy);
						
							ret = power_supply_register(&(dev->dev), &wireless_main.psy);
						
							ret = power_supply_register(&(dev->dev), &battery_main.psy);
							
							//初始化电池状态结构体
							/* Initialization BMT Struct */
							BMT_status.bat_exist = KAL_TRUE;	/* phone must have battery */
							BMT_status.charger_exist = KAL_FALSE;	/* for default, no charger */
							BMT_status.bat_vol = 0;
							BMT_status.ICharging = 0;
							BMT_status.temperature = 0;
							BMT_status.charger_vol = 0;
							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;
							BMT_status.SOC = 0;
							BMT_status.UI_SOC = 0;
					
							BMT_status.bat_charging_state = CHR_PRE;
							BMT_status.bat_in_recharging_state = KAL_FALSE;
							BMT_status.bat_full = KAL_FALSE;
							BMT_status.nPercent_ZCV = 0;
							BMT_status.nPrecent_UI_SOC_check_point = battery_meter_get_battery_nPercent_UI_SOC();/N%同步点对应的开路电压以及UI电量
							
							/* battery kernel thread for 10s check and charger in/out event */
							/* Replace GPT timer by hrtime */
							battery_kthread_hrtimer_init();	//创建一个定时器,定时器启动 bat_thread_kthread 函数,用于定时唤醒下面的内核线程更新电量						
							kthread_run(bat_thread_kthread, NULL, "bat_thread_kthread");	//核心充电线程

								/*在这个线程中,每隔10s会去调用函数 BAT_Thread 去获取电池数据*/
								int bat_thread_kthread(void *x)
										/* Run on a process content */
										while (1) {								
											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();		

											wait_event(bat_thread_wq, (bat_thread_timeout == KAL_TRUE));// 睡眠等待唤醒 
											
											// 每 10s 启动一次
											hrtimer_start(&battery_kthread_timer, ktime, HRTIMER_MODE_REL);
											ktime = ktime_set(BAT_TASK_PERIOD, 0);	/* 10s, 10* 1000 ms */			/*#define BAT_TASK_PERIOD   10	/* 10sec */					
										}
									
									void BAT_thread(void)
										{
											static kal_bool battery_meter_initilized = KAL_FALSE;
										
											//当系统第一次启动的时候 battery_meter_initilized 为 KAL_FALSE ,
											 所以 BAT_thread 会调用 battery_meter_initial 函数。
											 battery_meter_initial 函数为系统启动时运行的,为电池充电做一些初始化操作。
											if (battery_meter_initilized == KAL_FALSE) {
												
												//第一次执行时运行,获得开机显示电量,初始化电池算法(oam)参数,
												//开机时就运行,只会运行一次,对电池算法(oam)方案进行初始化,并获得开机显示电量百分比
												/*第一次进该函数会进行一些初始化,如设置D0的值(初始电量,构建电池曲线等) table_init, oam_init*/
												//进行一系列的电池参数与温度对应关系的表格的初始化,并根据电池当前电压,hw ocv 取一个较合适值
												//取和合适值对应容量,再与RTC保持容量比较,选择一个合适量为开机电池容量,最后初始化oam算法参数
												battery_meter_initial();	/* move from battery_probe() to decrease booting time  */	
													//MTK有 AUXADC、 SW_FG 、 HW_FG 三种不同的电池算法方案,这三种方案分别初始化
													//接下去先主要分析 SW_FG 的流程。而 SW_FG 中主要是利用线性插值算法来重构zcv表格,利用积分算法求电池的当前电量。
													signed int battery_meter_initial(void)
													{													
													#if defined(SOC_BY_SW_FG)
															g_auxadc_solution = 1;
															table_init();
																void table_init(void)
																{
																	BATTERY_PROFILE_STRUCT_P profile_p;
																	R_PROFILE_STRUCT_P profile_p_r_table;
																
																	int temperature = force_get_tbat(KAL_FALSE);//求电池的温度,或者获取电池的温度
																		int force_get_tbat(kal_bool update)
																		{
																			static int pre_bat_temperature_val = -1;
																			if (update == KAL_TRUE || pre_bat_temperature_val == -1) {
																				/* Get V_BAT_Temperature */
																				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) {
																					
																					/* BattVoltToTemp 函数就是任何将ADC读出的电压值转换为温度值*/
																					bat_temperature_val = BattVoltToTemp(bat_temperature_volt);
																						int BattVoltToTemp(int dwVolt)
																						{
																							long long TRes_temp;
																							long long TRes;
																							int sBaTTMP = -100;
																						
																							/* TRes_temp = ((long long)RBAT_PULL_UP_R*(long long)dwVolt) / (RBAT_PULL_UP_VOLT-dwVolt); */
																							/* TRes = (TRes_temp * (long long)RBAT_PULL_DOWN_R)/((long long)RBAT_PULL_DOWN_R - TRes_temp); */
																						
																							TRes_temp = (batt_meter_cust_data.rbat_pull_up_r * (long long) dwVolt);
																							do_div(TRes_temp, (batt_meter_cust_data.rbat_pull_up_volt - dwVolt));
																						
																							/* convert register to temperature */
																							/*NTC 电阻就是通过与电阻的串联跟并联并且通过电压值来得到的。计算出系统当前NTC电阻的电阻值后,
																							然后就调用 BattThermistorConverTemp 函数进行查表,对比出当前系统的温度。而
																							BattThermistorConverTemp 函数是通过 alps/kernel-3.18/drivers/misc/mediatek/include/mt-plat/mt6580/include/mach/mt_battery_meter_table.h中的
																							Batt_Temperature_Table 结构体,然后根据电阻值落在哪个区间,根据线性插值的方法求出当前电池的温度*/
																							sBaTTMP = BattThermistorConverTemp((int)TRes);
																						
																							return sBaTTMP;
																						}										
																				}
																			return bat_temperature_val;
																		#endif
																		}																		
																		
																	/*MTK的 zcv电池参数表格会预先测得的在-10 0 25 50 摄氏度开路电压跟放电深度之间的关系。
																	 结合真实的温度值,系统会自己构建一张当前温度值的ZCV电池曲线表格*/
																	/* Re-constructure r-table profile according to current temperature */
																	profile_p_r_table = fgauge_get_profile_r_table(batt_meter_cust_data.temperature_t);//返回NTC电阻跟电压表格
																		R_PROFILE_STRUCT_P fgauge_get_profile_r_table(unsigned int temperature)
																		{
																			switch (temperature) {
																			case batt_meter_cust_data.temperature_t0:
																				return &r_profile_t0[g_fg_battery_id][0];
																				/*break;*/
																			case batt_meter_cust_data.temperature_t1:
																				return &r_profile_t1[g_fg_battery_id][0];
																				/*break;*/
																			case batt_meter_cust_data.temperature_t2:
																				return &r_profile_t2[g_fg_battery_id][0];
																				/*break;*/
																			case batt_meter_cust_data.temperature_t3:
																				return &r_profile_t3[g_fg_battery_id][0];
																				/*break;*/
																			case batt_meter_cust_data.temperature_t:
																				return &r_profile_temperature[0];
																				/*break;*/
																			default:
																				return NULL;
																				/*break;*/
																			}
																		}
																		/*调用 fgauge_get_profile_r_table 函数会根据上面读取到的温度来返回相对应的 r_profile_t 数组,
																		 r_profile_t 数组在 alps/kernel-3.18/drivers/misc/mediatek/include/mt-plat/mt6580/include/mach/mt_battery_meter_table.h
																		 中,而随后调用的 fgauge_construct_r_table_profile 函数也是先根据当前电池的温度确定是落入哪个温度范围,
																		 假如当前电池温度是落入打0到25度之间,然后根据开路电压值和NTC电阻值的不同采用线性平均法分别构建出两个数值,
																		 并以这两个值在构造出一个新的开路电压跟NTC电阻的动态的结构体。*/																		
																		
																	//动态构建一个NTC电阻跟电压关系的表格
																	fgauge_construct_r_table_profile(temperature, profile_p_r_table);//重构ZCV表格的方法是通过线性插值的方法重构的
																		void fgauge_construct_r_table_profile(signed int temperature, R_PROFILE_STRUCT_P temp_profile_p)
																		{
																				/* Interpolation for V_BAT */ //构建表格中的电压值
																			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 */ // 构建表格中的NTC电阻值
																			for (i = 0; i < saddles; i++) {
																				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)
																						);
																				}
																				}
																		}		
																						  
																	/* Re-constructure battery profile according to current temperature */
																	profile_p = fgauge_get_profile(batt_meter_cust_data.temperature_t);																
																	fgauge_construct_battery_profile(temperature, profile_p);利用同样的原理(线性插值法),动态生成了一个开路电压与放电深度关系的结构体
																}
															/*当 table_init 函数后执行 oam_init() 函数, 系统会对一些变量进行初始化操作,
															 包括在 dod_init 函数中对 oam_v_ocv_1 和 oam_v_ocv_2 进行初始化赋值,读取RTC实时时钟芯片的电量值等等,
															 经过这一系列操作后,就会进入 battery 系统一个最重要的部分,利用积分的方式来求电池的当前电量*/
															oam_init();//会调用 dod_init
															bm_print(BM_LOG_CRTI, "[battery_meter_initial] SOC_BY_SW_FG done\n");
													#endif													
															meter_initilized = KAL_TRUE;
													}
												
												/*MTK系统是通过 battery_meter_get_battery_percentage 函数来读取当前电池的电量的,然后在通过设备结点,通过给上层调用。
												battery_meter_get_battery_percentage 函数主要就是调用 oam_run 函数来实现电流的库伦算法。积分法是利用电流计算公式 
												I = Q/t来求的,它的优点时适用于各种电池,但缺点是初始电量无法获取。*/
												BMT_status.nPercent_ZCV = battery_meter_get_battery_nPercent_zcv(); 
												battery_meter_initilized = KAL_TRUE;
											}
										
											/**/
											mt_battery_charger_detect_check();//充电器型号,是否插入等方面的检测,
											mt_battery_GetBatteryData();//核心函数获取电池数据 //通过具体的充电芯片来电池信息、充电信息、获得电池电量百分比,//通过 oam 算法获得电量百分比。
											if (BMT_status.charger_exist == KAL_TRUE)
												check_battery_exist();
										
											mt_battery_thermal_check();//电池温度检测,如果温度超过60度,关机重启。
											mt_battery_notify_check();//检测电池电压,电流等 //电池状态检测,如果有问题,则会调用print()进行打印。
										
											if (BMT_status.charger_exist == KAL_TRUE) {
												
												// 检查电池状态,设置到 BMT_status.bat_charging_state 中 
												mt_battery_CheckBatteryStatus(); //充电异常检测
												
												// 充电策略,这里有两个文件: switch_charging.c 和 linear_charging.c 
												// 他们的关系是,如果定义了任一外部充电 IC,则选择 switch_charging.c 的函数,否则就是 linear_charging.c 的函数
												// 这里就是调用具体的芯片的充电相关函数进行充电
												mt_battery_charging_algorithm();//切换充电模式 Pre_CC->CC->CV->Full
													void mt_battery_charging_algorithm()
                                                        {
                                                             switch(BMT_status.bat_charging_state)
                                                            {            
                                                                case CHR_PRE :
                                                                    BAT_PreChargeModeAction();
                                                                    break;    
                                                                    
                                                                case CHR_CC :
                                                                    BAT_ConstantCurrentModeAction();
                                                                                /
                                                                                // MTK 充电是充 9s 停 1s 
                                                                                // Charging 9s and discharging 1s : start
                                                                                battery_charging_control(CHARGING_CMD_ENABLE,&charging_enable);
                                                                    break;    
                                                                    
                                                                case CHR_TOP_OFF :
                                                                    BAT_TopOffModeAction();
                                                                    break;              

                                                                case CHR_BATFULL:
                                                                    BAT_BatteryFullAction();
                                                                    break;
                                                                    
                                                            case CHR_HOLD:
                                                                BAT_BatteryHoldAction();
                                                                    break;
                                                                    
                                                                case CHR_ERROR:
                                                                    BAT_BatteryStatusFailAction();
                                                                    break;                
                                                            }    
                                                           
                                                        }		
											}
										
											mt_battery_update_status();//上报电池数据 //更新电池显示状态,更新设置节点的内容:sys/class/power_supply/文件节点(wireless_main/battery_main/ac_main/usb_main)
											mt_kpoc_power_off_check();
										}
										
											void mt_battery_GetBatteryData(void)
												SOC = battery_meter_get_battery_percentage();
												
													signed int battery_meter_get_battery_percentage(void)
														oam_run();
														
															void oam_run(void)
																
															首先通过adc读取电池 Vbat脚的电压值,
															然后通过这个闭路电压值查找 R—Table (mt_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电池显示的具体过程为:硬件ADC读取Battery的各路信息:包括温度,电压等。
然后利用MTK开发的电量算法分析得到的数据。Kernel层将电量信息通过写文件节点的方式更新,
并通过UEVENT通知上层。上层Service开启UEVENT LISTENER,监听到UEVENT后,读取battery相关文件节点,
获取电量信息。Service更新数据后,通过Broadcast通知所有开启了相关 listener 的 activities 。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人在路上……

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值