相关概念

懒的排版了,直接上图 

相关文件关系

再来一发 

杂项,电池温度检测原理图

发 

充电流程

图样图森破 

核心函数特写


 
 
 

/* 概念: ZCV:开路电压OCV: 开路电压VC:闭路电压CAR:库伦计DOD: 放电深度,100-DOD 即电容容量Cmax/Qmax: 电池容量相关文件关系: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)                   // 充电状态控制,内部 PMICSwitch_charging.c (s:\i841\mediatek\kernel\drivers\power)                   // 充电状态控制,外部 Charge IC 硬件原理图:NTC 检测温度电路原理如下:Vu                                Ru:上拉电阻值           ---                               Rd: 下拉电阻值 |                                Rntc: NTC 温度电阻 阻值|||   Ru                          Vu: 上拉电压值 |                                Gnd: 地 ---------- -----Vntc                  Vntc: NTC 电压 |        |Rntc   |||      |||  Rd |        |----------                          Rntc = (Ru*Rd*Vntc) / (Vru * Rd - Vntc * Ru)|---------Gnd 【充电初始化流程】:module_init(battery_init)battery_init(void)// 注册平台设备platform_device_register(&battery_device)// 注册平台驱动platform_driver_register(&battery_driver)// 二者匹配调用 probe 函数 battery_probe()/// 注册字符设备alloc_chrdev_region(&adc_cali_devno, 0, 1, ADC_CALI_DEVNAME)cdev_add(adc_cali_cdev, adc_cali_devno, 1)/// 创建相关 sys 系统节点 class_create(THIS_MODULE, ADC_CALI_DEVNAME)device_create(adc_cali_class, NULL, adc_cali_devno, NULL, ADC_CALI_DEVNAME)// 与特定硬件的入口操作函数关联 get_charging_control()// 用来绑定内部 PMIC 或者外部充电芯片的操作函数入口battery_charging_control = chr_control_interface;相关充电芯片驱动实现的函数有:charging_hw_initcharging_dump_register     charging_enablecharging_set_cv_voltagecharging_get_currentcharging_set_currentcharging_set_input_current     charging_get_charging_status   charging_reset_watch_dog_timercharging_set_hv_thresholdcharging_get_hv_statuscharging_get_battery_statuscharging_get_charger_det_statuscharging_get_charger_typecharging_get_is_pcm_timer_triggercharging_set_platform_resetcharging_get_platfrom_boot_modecharging_set_power_off//// 获得启动模式 battery_charging_control(CHARGING_CMD_GET_PLATFORM_BOOT_MODE, &g_platform_boot_mode)                                            // 对应 pmic 的 charging_get_platfrom_boot_mode() charging_get_platfrom_boot_mode()get_boot_mode()/// Integrate with Android Battery Service // 一般移动设备的供电可来自外部即 AC 与 usb,内部电池供电 battery,// 所以需通过 power_supply_register()   函数在 /sys/class/power_supply 下分别// 注册 ac usb battery,注册完成可发现在设备目录 /sys/class/power_supply 下分别// 出现 ac usb battery 三个文件夹 power_supply_register(&(dev->dev), &ac_main.psy)power_supply_register(&(dev->dev), &usb_main.psy)power_supply_register(&(dev->dev), &wireless_main.psy)power_supply_register(&(dev->dev), &battery_main.psy)/// 初始化电池状态结构体 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();/////创建一个定时器,用于定时唤醒下面的内核线程更新电量battery_kthread_hrtimer_init();ktime = ktime_set(1, 0);    // 3s, 10* 1000 ms// 高分辨率kernel定时器初始化hrtimer_init(&battery_kthread_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);battery_kthread_timer.function = battery_kthread_hrtimer_func; // 定时器处理函数 battery_kthread_hrtimer_func()bat_thread_wakeup();  // 唤醒下面的 bat_thread_kthread() 线程wake_up(&bat_thread_wq); // 重置定时器 hrtimer_start(&battery_kthread_timer, ktime, HRTIMER_MODE_REL)//// 【核心充电线程】kthread_run(bat_thread_kthread, NULL, "bat_thread_kthread"); // 设置定时器超时时间ktime_t ktime = ktime_set(3, 0);  // 10s, 10* 1000 mswhile (1) {  // 这个是核心算法 BAT_thread();///// 0. 第一次执行时运行,获得开机显示电量,初始化电池算法 oam 参数 // 开机时就会运行,只会运行一次,对电池算法 oam 方案进行初始化, 并获得开机显示电量百分比 if(battery_meter_initilized == KAL_FALSE){// 进行的一系列的电池参数与温度对应关系的表格的初始化,并根据电池当前电压,hw ocv 取一个较合适值,// 取合适值对应容量,再与 RTC 保存容量比较,选择一个合适量,为开机电池容量,最后初始化 oam 算法参数 battery_meter_initial()}// 1. 判断是插入的是否充电器还是电脑 USB,看能不能进行充电// 如果连接的 USB 线为 USB 充电线,或者电脑 USB 线,则打开 USB,// 这里会通过  BC1.1 来判断是电脑 USB 还是 USB 充电器,来决定充电电流 // 否则连接的不是充电线或者 USB 作为一个从设备使用,要断开 USB?mt_battery_charger_detect_check();///// 2. 通过具体的充电芯片来获得电池信息,充电信息, 获得电池电量百分比 // 通过 oam 算法,获得电量百分比 mt_battery_GetBatteryData();// 3. 电池温度保护// 电池温度检查,如果温度超过 60 度,关机重启 mt_battery_thermal_check();/// 4. 电池状态检查//  对电池状态进行检查,如果有问题,则会调用 printk() 进行打印 mt_battery_notify_check();//// 5. 调用具本的硬件相关函数进行充电,充电时会进行 CC/CV 之类的状态机切换就是在这里进行的// 如果存在充电线,则调用具体充电芯片相关的函数进行充电  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();   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 : startbattery_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;                }    }}///// 6. 更新电池显示状态 // 更新设置节点的内容: //     /sys/class/power_supply/下的文件夹//                             wireless_main//                             battery_main//                             ac_main //                             usb_mainmt_battery_update_status();// 睡眠等待唤醒 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}/// 电池过充保护相关检测与初始化,他 2s 检测一次charger_hv_detect_sw_workaround_init();charger_hv_detect_thread = kthread_run(charger_hv_detect_sw_thread_handler, 0, "mtk charger_hv_detect_sw_workaround");// 这个函数是周期来检查电池电压是否超出限制,即过充保护,超出了就不能充电了 charger_hv_detect_sw_thread_handler()do{ktime = ktime_set(0, BAT_MS_TO_NS(2000));if(chargin_hw_init_done)// 高压检测,应该是电池超过这个电压时,就不可以充电了 battery_charging_control(CHARGING_CMD_SET_HV_THRESHOLD,&hv_voltage);// 对应 PMIC charging_set_hv_threshold()wait_event_interruptible(charger_hv_detect_waiter, (charger_hv_detect_flag == KAL_TRUE));// 如果检测到充电器,则检测下电池是否存在 检测电池是否存在,通过读取 PMIC 的 CHR_CON7if ((upmu_is_chr_det() == KAL_TRUE)){// 检测电池是否存在 check_battery_exist(); }hrtimer_start(&charger_hv_detect_timer, ktime, HRTIMER_MODE_REL); }while (!kthread_should_stop());//// battery notofy UIplatform_device_register(&MT_batteryNotify_device)platform_driver_register(&mt_batteryNotify_driver)mt_batteryNotify_probe()ret_device_file = device_create_file(&(dev->dev), &dev_attr_BatteryNotify);ret_device_file = device_create_file(&(dev->dev), &dev_attr_BN_TestMode);battery_dir = proc_mkdir("mtk_battery_cmd", NULL);电池测量模块初始化:module_init(battery_meter_init);battery_meter_init(void)platform_device_register(&battery_meter_device);platform_driver_register(&battery_meter_driver);// 调用对应的 probe 函数 battery_meter_probe()// 测量模块接口设置【核心在于 bm_ctrl_cmd 函数】battery_meter_ctrl = bm_ctrl_cmd;*/
module_init(battery_init);
static int __init battery_init(void)
{int ret;ret = platform_device_register(&battery_device);struct platform_device battery_device = {.name   = "battery",.id        = -1,};if (ret) {battery_xlog_printk(BAT_LOG_CRTI, "****[battery_driver] Unable to device register(%d)\n", ret);return ret;}ret = platform_driver_register(&battery_driver);static struct platform_driver battery_driver = {.probe         = battery_probe,.remove        = battery_remove,.shutdown      = battery_shutdown,.driver        = {.name = "battery",.pm   = &battery_pm_ops},};if (ret) {battery_xlog_printk(BAT_LOG_CRTI, "****[battery_driver] Unable to register driver (%d)\n", ret);return ret;}// battery notofy UIret = platform_device_register(&MT_batteryNotify_device);struct platform_device MT_batteryNotify_device = {.name   = "mt-battery",.id        = -1,};if (ret) {battery_xlog_printk(BAT_LOG_CRTI, "****[mt_batteryNotify] Unable to device register(%d)\n", ret);return ret;}    ret = platform_driver_register(&mt_batteryNotify_driver);static struct platform_driver mt_batteryNotify_driver = {.probe        = mt_batteryNotify_probe,.driver     = {.name = "mt-battery",},};if (ret) {battery_xlog_printk(BAT_LOG_CRTI, "****[mt_batteryNotify] Unable to register driver (%d)\n", ret);return ret;}battery_xlog_printk(BAT_LOG_CRTI, "****[battery_driver] Initialization : DONE !!\n");return 0;
}//
// 第一个调用的 probe,充电相关函数初始化
/* 相关文件关系: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)                       // 充电状态控制,内部 PMICSwitch_charging.c (s:\i841\mediatek\kernel\drivers\power)                       // 充电状态控制,外部 Charge IC */
static int battery_probe//(struct platform_device *dev)
{struct class_device *class_dev = NULL;int ret=0;battery_xlog_printk(BAT_LOG_CRTI, "******** battery driver probe!! ********\n" );/* Integrate with NVRAM */ret = alloc_chrdev_region(&adc_cali_devno, 0, 1, ADC_CALI_DEVNAME);  // #define ADC_CALI_DEVNAME "MT_pmic_adc_cali"if (ret) battery_xlog_printk(BAT_LOG_CRTI, "Error: Can't Get Major number for adc_cali \n");adc_cali_cdev = cdev_alloc();adc_cali_cdev->owner = THIS_MODULE;adc_cali_cdev->ops = &adc_cali_fops;static struct file_operations adc_cali_fops = {.owner        = THIS_MODULE,.unlocked_ioctl    = adc_cali_ioctl,.open        = adc_cali_open,.release    = adc_cali_release,    };ret = cdev_add(adc_cali_cdev, adc_cali_devno, 1);if(ret)battery_xlog_printk(BAT_LOG_CRTI, "adc_cali Error: cdev_add\n");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);battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] adc_cali prepare : done !!\n ");/* 与特定硬件的操作函数关联 */get_charging_control();static void get_charging_control(void){/* 这是一个函数指针,指向了硬件驱动对应的接口函数,这里指向的是 pmic 为例,后面调用 battery_charging_control(xxx)即是调用 chr_control_interface(xxx) typedef kal_int32 (*CHARGING_CONTROL)(CHARGING_CTRL_CMD cmd, void *data);*/battery_charging_control = chr_control_interface;// This function is called to set the charger hwkal_int32 chr_control_interface//(CHARGING_CTRL_CMD cmd, void *data){kal_int32 status;if(cmd < CHARGING_CMD_NUMBER)status = charging_func[cmd](data);/* 这是在特定充电芯片中实现的,如 pmic 的 */// 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// };elsereturn STATUS_UNSUPPORTED;return status;}}/* 获得启动模式 */battery_charging_control(CHARGING_CMD_GET_PLATFORM_BOOT_MODE, &g_platform_boot_mode);// 对应 pmic 的 charging_get_platfrom_boot_mode() static kal_uint32 charging_get_platfrom_boot_mode(void *data){kal_uint32 status = STATUS_OK;*(kal_uint32*)(data) = get_boot_mode();BOOTMODE get_boot_mode(void){return g_boot_mode;}battery_xlog_printk(BAT_LOG_CRTI, "get_boot_mode=%d\n", get_boot_mode());return status;}battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] g_platform_boot_mode = %d\n ", g_platform_boot_mode);wake_lock_init(&battery_suspend_lock, WAKE_LOCK_SUSPEND, "battery suspend wakelock");    /* Integrate with Android Battery Service 一般移动设备的供电可来自外部即 AC 与 usb,内部电池供电 battery,所以需通过 power_supply_register   函数在 /sys/class/power_supply 下分别注册 ac usb battery,注册完成可发现在设备目录 /sys/class/power_supply 下分别出现 ac usb battery 三个文件夹 */ret = power_supply_register(&(dev->dev), &ac_main.psy);/* 相关的核心结构体 struct power_supply {struct listnode list;       char name[256];             char type[32];bool online;bool valid;char cap_path[PATH_MAX];};*/if (ret){            battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register AC Fail !!\n");                    return ret;}             battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register AC Success !!\n");ret = power_supply_register(&(dev->dev), &usb_main.psy);if (ret){            battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register USB Fail !!\n");                    return ret;}             battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register USB Success !!\n");ret = power_supply_register(&(dev->dev), &wireless_main.psy);if (ret){            battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register WIRELESS Fail !!\n");                    return ret;}             battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register WIRELESS Success !!\n");ret = power_supply_register(&(dev->dev), &battery_main.psy);if (ret){battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register Battery Fail !!\n");return ret;}battery_xlog_printk(BAT_LOG_CRTI, "[BAT_probe] power_supply_register Battery Success !!\n");#if !defined(CONFIG_POWER_EXT)/* For EM */{int ret_device_file=0;ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Charger_Voltage);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_0_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_1_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_2_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_3_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_4_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_5_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_6_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_7_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_8_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_9_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_10_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_11_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_12_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_13_Slope);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_0_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_1_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_2_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_3_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_4_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_5_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_6_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_7_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_8_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_9_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_10_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_11_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_12_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_13_Offset);ret_device_file = device_create_file(&(dev->dev), &dev_attr_ADC_Channel_Is_Calibration);ret_device_file = device_create_file(&(dev->dev), &dev_attr_Power_On_Voltage);ret_device_file = device_create_file(&(dev->dev), &dev_attr_Power_Off_Voltage);ret_device_file = device_create_file(&(dev->dev), &dev_attr_Charger_TopOff_Value);ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_Battery_CurrentConsumption);ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_SW_CoulombCounter);ret_device_file = device_create_file(&(dev->dev), &dev_attr_Charging_CallState);}//battery_meter_initial();  //move to mt_battery_GetBatteryData() to decrease booting time/* 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();//battery kernel thread for 10s check and charger in/out event/* Replace GPT timer by hrtime 创建一个定时器,用于定时唤醒下面的内核线程更新电量 */battery_kthread_hrtimer_init();void battery_kthread_hrtimer_init(void){ktime_t ktime;ktime = ktime_set(1, 0);  // 3s, 10* 1000 ms// 高分辨率kernel定时器初始化hrtimer_init(&battery_kthread_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);battery_kthread_timer.function = battery_kthread_hrtimer_func;   enum hrtimer_restart battery_kthread_hrtimer_func(struct hrtimer *timer){bat_thread_wakeup(); void bat_thread_wakeup(void){battery_xlog_printk(BAT_LOG_CRTI, "******** battery : bat_thread_wakeup  ********\n" );bat_thread_timeout = KAL_TRUE;bat_meter_timeout = KAL_TRUE;// 唤醒下面的 bat_thread_kthread() 线程wake_up(&bat_thread_wq);    }return HRTIMER_NORESTART;}// 重置定时器 hrtimer_start(&battery_kthread_timer, ktime, HRTIMER_MODE_REL);battery_xlog_printk(BAT_LOG_CRTI, "battery_kthread_hrtimer_init : done\n" );}kthread_run(bat_thread_kthread, NULL, "bat_thread_kthread"); ///Internal API 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();void BAT_thread(void){static kal_bool  battery_meter_initilized = KAL_FALSE;_g_bat_sleep_total_time = 0;// 0. 第一次执行时运行,获得开机显示电量,初始化电池算法 oam 参数 /* 开机时就会运行,只会运行一次,对电池算法 oam 方案进行初始化, 并获得开机显示电量百分比 */if(battery_meter_initilized == KAL_FALSE){/* 进行的一系列的电池参数与温度对应关系的表格的初始化,并根据电池当前电压,hw ocv 取一个较合适值,取合适值对应容量,再与 rtc 容量比较,选择一个合适量,为开机电池容量,最后初始化 oam 算法参数 */battery_meter_initial(); //move from battery_probe() to decrease booting timekal_int32 battery_meter_initial(void){#if defined(CONFIG_POWER_EXT)return 0;#else/* 多电池支持 */#ifdef MTK_MULTI_BAT_PROFILE_SUPPORTfgauge_get_profile_id();#endif/* 针对 AUXADC SW_FG HW_FG 三种不同的电池算法方案,分别初始化,82平台使用的 SW_FG */#if defined(SOC_BY_AUXADC)g_auxadc_solution = 1;table_init();bm_print(BM_LOG_CRTI, "[battery_meter_initial] SOC_BY_AUXADC done\n");#endif#if defined(SOC_BY_HW_FG)fgauge_initialization();fgauge_algo_run_init();bm_print(BM_LOG_CRTI, "[battery_meter_initial] SOC_BY_HW_FG done\n");#endif#if defined(SOC_BY_SW_FG)g_auxadc_solution = 1;/* 温度-电池容量,温度-电池内阻,温度-NTC阻值表格等初始化,并获得了当前电池温度及所用掉的容量值  */table_init();void table_init(void){BATTERY_PROFILE_STRUC_P profile_p;R_PROFILE_STRUC_P profile_p_r_table;/* 获得当前电池温度*/int temperature = force_get_tbat();/* 通过获得当前 NTC 电压,查表并进行线性插值法,得到当前的温度值  */int force_get_tbat(void){#if defined(CONFIG_POWER_EXT) || defined(FIXED_TBAT_25)bm_print(BM_LOG_CRTI, "[force_get_tbat] fixed TBAT=25 t\n");return 25;#elseint bat_temperature_volt=0;int bat_temperature_val=0;int fg_r_value=0;kal_int32 fg_current_temp=0;kal_bool fg_current_state=KAL_FALSE;int bat_temperature_volt_temp=0;int ret=0;/* Get V_BAT_Temperature */bat_temperature_volt = 2; /* 对应 PMIC 的硬件接口函数,这个对应的函数是在下面  battery_meter_init 模块中设置的 获得电池 NTC 对应的电压 */ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_TEMP, &bat_temperature_volt); static kal_int32 read_adc_v_bat_temp(void *data){#if defined(CONFIG_POWER_EXT)*(kal_int32*)(data) = 0;#else#if defined(MTK_PCB_TBAT_FEATURE)int ret = 0, data[4], i, ret_value = 0, ret_temp = 0;int Channel=1;if( IMM_IsAdcInitReady() == 0 ){bm_print(BM_LOG_CRTI, "[get_tbat_volt] AUXADC is not ready");return 0;}i = times;while (i--){ret_value = IMM_GetOneChannelValue(Channel, data, &ret_temp);ret += ret_temp;bm_print(BM_LOG_FULL, "[get_tbat_volt] ret_temp=%d\n",ret_temp);}ret = ret*1500/4096 ;ret = ret/times;bm_print(BM_LOG_CRTI, "[get_tbat_volt] Battery output mV = %d\n",ret);*(kal_int32*)(data) = ret;#elsebm_print(BM_LOG_FULL, "[read_adc_v_charger] return PMIC_IMM_GetOneChannelValue(4,times,1);\n");/* 读 PMIC 对应的 ADC 的值,并将其转化为对应的电压值,这里即读取 BATON 引脚电压 */*(kal_int32*)(data) = PMIC_IMM_GetOneChannelValue(VBATTEMP_CHANNEL_NUMBER,*(kal_int32*)(data),1);//==============================================================================// PMIC-AUXADC //==============================================================================int PMIC_IMM_GetOneChannelValue(int dwChannel, int deCount, int trimd){kal_int32 ret_data; kal_int32 count=0;kal_int32 u4Sample_times = 0;kal_int32 u4channel=0;  kal_int32 adc_result_temp=0;kal_int32 r_val_temp=0;   kal_int32 adc_result=0;   kal_int32 ret=0;kal_int32 adc_reg_val=0;/*0 : BATON2 **1 : CH62 : THR SENSE2 **3 : THR SENSE14 : VCDT5 : BATON1          // 其实对应的就是硬件 PMIC  ADC36 : ISENSE7 : BATSNS8 : ACCDET    9-16 : audio*///do not suppport BATON2 and THR SENSE2 for sw workaroundif (dwChannel==0 || dwChannel==2)return 0;wake_lock(&pmicAuxadc_irq_lock);/* 采样通道号数,读取对应的 PMIC 寄存器的 ADC 的值 */                                                                                                                                                        do  {mutex_lock(&pmic_adc_mutex);/* 硬件上,ADC 通过一个 mux 数据选择器对各路模拟信号进行切换,这里检查哪里通知有数据了 */PMIC_IMM_PollingAuxadcChannel();void PMIC_IMM_PollingAuxadcChannel(void){kal_uint32 ret=0;//xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[PMIC_IMM_PollingAuxadcChannel] before:%d ",upmu_get_rg_adc_deci_gdly());if (upmu_get_rg_adc_deci_gdly()==1){while(upmu_get_rg_adc_deci_gdly()==1){unsigned long flags;spin_lock_irqsave(&pmic_adc_lock, flags);if (pmic_is_auxadc_busy()==0){//upmu_set_rg_adc_deci_gdly(0);ret=pmic_config_interface_nolock(AUXADC_CON19,0,PMIC_RG_ADC_DECI_GDLY_MASK,PMIC_RG_ADC_DECI_GDLY_SHIFT);}spin_unlock_irqrestore(&pmic_adc_lock, flags);}}//xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[PMIC_IMM_PollingAuxadcChannel] after:%d ",upmu_get_rg_adc_deci_gdly());}/* adc */if (dwChannel<9){upmu_set_rg_vbuf_en(1);//set 0ret=pmic_read_interface(AUXADC_CON22,&adc_reg_val,PMIC_RG_AP_RQST_LIST_MASK,PMIC_RG_AP_RQST_LIST_SHIFT);adc_reg_val = adc_reg_val & (~(1<<dwChannel));ret=pmic_config_interface(AUXADC_CON22,adc_reg_val,PMIC_RG_AP_RQST_LIST_MASK,PMIC_RG_AP_RQST_LIST_SHIFT);//set 1ret=pmic_read_interface(AUXADC_CON22,&adc_reg_val,PMIC_RG_AP_RQST_LIST_MASK,PMIC_RG_AP_RQST_LIST_SHIFT);adc_reg_val = adc_reg_val | (1<<dwChannel);ret=pmic_config_interface(AUXADC_CON22,adc_reg_val,PMIC_RG_AP_RQST_LIST_MASK,PMIC_RG_AP_RQST_LIST_SHIFT);}/* audio 相关 */else if(dwChannel>=9 && dwChannel<=16){ret=pmic_read_interface(AUXADC_CON23,&adc_reg_val,PMIC_RG_AP_RQST_LIST_RSV_MASK,PMIC_RG_AP_RQST_LIST_RSV_SHIFT);adc_reg_val = adc_reg_val & (~(1<<(dwChannel-9)));ret=pmic_config_interface(AUXADC_CON23,adc_reg_val,PMIC_RG_AP_RQST_LIST_RSV_MASK,PMIC_RG_AP_RQST_LIST_RSV_SHIFT);//set 1ret=pmic_read_interface(AUXADC_CON23,&adc_reg_val,PMIC_RG_AP_RQST_LIST_RSV_MASK,PMIC_RG_AP_RQST_LIST_RSV_SHIFT);adc_reg_val = adc_reg_val | (1<<(dwChannel-9));ret=pmic_config_interface(AUXADC_CON23,adc_reg_val,PMIC_RG_AP_RQST_LIST_RSV_MASK,PMIC_RG_AP_RQST_LIST_RSV_SHIFT);       }mutex_unlock(&pmic_adc_mutex);//Duo to HW limitationif(dwChannel!=8)msleep(1);count=0;ret_data=0;/* 根据不同通道选择,来读取对应的 PMIC 寄存器的值 */switch(dwChannel){         case 0:    while( upmu_get_rg_adc_rdy_baton2() != 1 ){msleep(1);if( (count++) > count_time_out){xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);break;}           }ret_data = upmu_get_rg_adc_out_baton2();                break;case 1:    while( upmu_get_rg_adc_rdy_ch6() != 1 ){msleep(1);if( (count++) > count_time_out){xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);break;}           }ret_data = upmu_get_rg_adc_out_ch6();               xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[upmu_get_rg_adc_out_ch6] 0x%x\n", ret_data);break;case 2:    while( upmu_get_rg_adc_rdy_thr_sense2() != 1 ){msleep(1);if( (count++) > count_time_out){xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);break;}           }ret_data = upmu_get_rg_adc_out_thr_sense2();                break;              case 3:    while( upmu_get_rg_adc_rdy_thr_sense1() != 1 ){msleep(1);if( (count++) > count_time_out){xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);break;}           }ret_data = upmu_get_rg_adc_out_thr_sense1();                break;case 4:    while( upmu_get_rg_adc_rdy_vcdt() != 1 ){msleep(1);if( (count++) > count_time_out){xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);break;}           }ret_data = upmu_get_rg_adc_out_vcdt();              break;/* 读取 PMIC 的 VBATON 引用的电压值*/case 5:    while( upmu_get_rg_adc_rdy_baton1() != 1 ){msleep(1);if( (count++) > count_time_out){xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);break;}           }ret_data = upmu_get_rg_adc_out_baton1();         kal_uint32 upmu_get_rg_adc_rdy_baton1(void){kal_uint32 ret=0;kal_uint32 val=0;pmic_lock();ret=pmic_read_interface( (kal_uint32)(AUXADC_ADC3),(&val),(kal_uint32)(PMIC_RG_ADC_RDY_BATON1_MASK),(kal_uint32)(PMIC_RG_ADC_RDY_BATON1_SHIFT));pmic_unlock();return val;}break;case 6:    while( upmu_get_rg_adc_rdy_isense() != 1 ){msleep(1);if( (count++) > count_time_out){xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);break;}           }ret_data = upmu_get_rg_adc_out_isense();                break;case 7:    while( upmu_get_rg_adc_rdy_batsns() != 1 ){msleep(1);if( (count++) > count_time_out){xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);break;}           }ret_data = upmu_get_rg_adc_out_batsns();                break; case 8:    while( upmu_get_rg_adc_rdy_ch5() != 1 );ret_data = upmu_get_rg_adc_out_ch5();               break;              case 9:    case 10:  case 11:  case 12:case 13:    case 14:  case 15:  case 16:        while( upmu_get_rg_adc_rdy_int() != 1 ){msleep(1);if( (count++) > count_time_out){xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[IMM_GetOneChannelValue_PMIC] (%d) Time out!\n", dwChannel);break;}           }ret_data = upmu_get_rg_adc_out_int();               break;              default:xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[AUXADC] Invalid channel value(%d,%d)\n", dwChannel, trimd);wake_unlock(&pmicAuxadc_irq_lock);return -1;break;}u4channel += ret_data;u4Sample_times++;if (Enable_BATDRV_LOG == 2){//debugxlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[AUXADC] u4channel[%d]=%d.\n", dwChannel, ret_data);}}while (u4Sample_times < deCount);/* Value averaging adc 采样值取平均值 */ adc_result_temp = u4channel / deCount;/* ADC 分辨率: 即 ADC 数据每加 1 所对应的电压变化,计算公式为 【测量电压范围/(2^AD位数-1)】*/switch(dwChannel){         case 0:                r_val_temp = 1;           adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;break;case 1:    r_val_temp = 1;adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;break;case 2:    r_val_temp = 1;adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;break;case 3:    r_val_temp = 1;adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;break;case 4:    r_val_temp = 1;adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;break;/* ADC 分辨率: 即 ADC 数据每加 1 所对应的电压变化,计算公式为 0-正数表示:     【测量电压范围/(2^AD位数)】 负数-正数表示: 【测量电压范围/(2^AD位数-1)】 获得 BATON 电压值 #define VOLTAGE_FULL_RANGE     1800  // 测量的电压范围#define ADC_PRECISE           32768  // 15 bits   2^15 = 32768*/case 5:    r_val_temp = 1;/* 这里得到将数字化数值转换为对应的模拟电压值 */adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;break;case 6:    r_val_temp = 4;     // 电阻分压比,因为测量 adc 量程不够,要采用电阻分压adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;break;case 7:    r_val_temp = 4;     // 电阻分压比,因为测量 adc 量程不够,要采用电阻分压adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;break;    case 8:    r_val_temp = 1;adc_result = (adc_result_temp*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;break;              case 9:    case 10:  case 11:  case 12:case 13:    case 14:  case 15:  case 16:        adc_result = adc_result_temp;break;  default:xlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[AUXADC] Invalid channel value(%d,%d)\n", dwChannel, trimd);wake_unlock(&pmicAuxadc_irq_lock);return -1;break;}if (Enable_BATDRV_LOG == 2){//debugxlog_printk(ANDROID_LOG_INFO, "Power/PMIC", "[AUXADC] adc_result_temp=%d, adc_result=%d, r_val_temp=%d.\n", adc_result_temp, adc_result, r_val_temp);}wake_unlock(&pmicAuxadc_irq_lock);return adc_result;}#endif#endifreturn STATUS_OK;}/* 如果获得的 PMIC 的  BATON 引脚电压值不为 0 */if(bat_temperature_volt != 0){   #if defined(SOC_BY_HW_FG)/* 获得配置的放电电阻值的大小 */fg_r_value = get_r_fg_value();int get_r_fg_value(void){return (R_FG_VALUE+CUST_R_FG_OFFSET);}ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_HW_FG_CURRENT, &fg_current_temp);   static kal_int32 fgauge_read_current(void *data){return STATUS_OK;}ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_HW_FG_CURRENT_SIGN, &fg_current_state);static kal_int32 fgauge_read_current_sign(void *data){return STATUS_OK;}fg_current_temp = fg_current_temp/10;if(fg_current_state==KAL_TRUE){bat_temperature_volt_temp = bat_temperature_volt;bat_temperature_volt = bat_temperature_volt - ((fg_current_temp*fg_r_value)/1000);}else{bat_temperature_volt_temp = bat_temperature_volt;// 这里对 NTC 电压进行补偿: NTC 实际温度对应电压 = 测量电压 + 负载电压*负载电阻   // 【这里的 1000 是将小数整数化】bat_temperature_volt = bat_temperature_volt + ((fg_current_temp*fg_r_value)/1000);}#endif////* 将 NTC 的电压通过查表获得对应温度值 */bat_temperature_val = BattVoltToTemp(bat_temperature_volt);int BattVoltToTemp(int dwVolt){kal_int64 TRes_temp;kal_int64 TRes;int sBaTTMP = -100;// TRes_temp = ((kal_int64)RBAT_PULL_UP_R*(kal_int64)dwVolt) / (RBAT_PULL_UP_VOLT-dwVolt); //TRes = (TRes_temp * (kal_int64)RBAT_PULL_DOWN_R)/((kal_int64)RBAT_PULL_DOWN_R - TRes_temp);/* 电路原理如下:Vu                                Ru:上拉电阻值           ---                               Rd: 下拉电阻值 |                                Rntc: NTC 温度电阻 阻值|||   Ru                          Vu: 上拉电压值 |                                Gnd: 地 ---------- -----Vntc                  Vntc: NTC 电压 |        |Rntc   |||      |||  Rd |        |----------                          Rntc = (Ru*Rd*Vntc) / (Vru * Rd - Vntc * Ru)|---------Gnd *//* TRes_temp = 上拉电阻值 * NTC电压 */TRes_temp = (RBAT_PULL_UP_R*(kal_int64)dwVolt); /* 上拉电阻分压 = 上拉电压 - NTC分压 = RBAT_PULL_UP_VOLT-dwVoltdo_div(): 做除法 (上拉电阻值 * NTC电压)/上拉电阻分压 = (上拉电阻值/上拉电压分压)*NTC电压   */do_div(TRes_temp, (RBAT_PULL_UP_VOLT-dwVolt));/* (上拉电阻值/上拉电压分压)*NTC电压*下拉电阻值 */TRes = (TRes_temp * RBAT_PULL_DOWN_R);/* (上拉电阻值/上拉电压分压)*NTC电压*下拉电阻值 / (下拉电阻值 - (上拉电阻值/上拉电压分压)*NTC电压)*/do_div(TRes, abs(RBAT_PULL_DOWN_R - TRes_temp));/* convert register to temperature 将得到的 NTC 阻值通过表格换算成温度 */sBaTTMP = BattThermistorConverTemp((int)TRes); int BattThermistorConverTemp//(int Res){int i=0;int RES1=0,RES2=0;int TBatt_Value=-200,TMP1=0,TMP2=0;/* 如果大于最低温度阻值,返回 -20 */if(Res>=Batt_Temperature_Table[0].TemperatureR) /* Cust_battery_meter_table.h (s:\i841\mediatek\custom\mt6582\kernel\battery\battery) */{TBatt_Value = -20;}/* 如果大于最高温度阻值,返回 60 */else if(Res<=Batt_Temperature_Table[16].TemperatureR){TBatt_Value = 60;}/* 其他温度 */else{RES1=Batt_Temperature_Table[0].TemperatureR;TMP1=Batt_Temperature_Table[0].BatteryTemp;/* 遍历 NTC 阻值与温度对应表,获得当前阻值所对应的表项 */for(i=0;i<=16;i++){if(Res>=Batt_Temperature_Table[i].TemperatureR){RES2=Batt_Temperature_Table[i].TemperatureR;TMP2=Batt_Temperature_Table[i].BatteryTemp;break;}else{RES1=Batt_Temperature_Table[i].TemperatureR;TMP1=Batt_Temperature_Table[i].BatteryTemp;}}/* Liner Interpolation Method: 线性插值法,方法详见 Battery_Charging Introduction for Customer.pdf 核心就是一条直接函数,已知两个点的 (X0,Y0) (X1,Y1),并知道第三个点的 X,就对应的 Y 计算公式为:Y = [(X-X0)*Y1 + (X1-X)*Y0] / (X1-X0) */TBatt_Value = (((Res-RES2)*TMP1)+((RES1-Res)*TMP2))/(RES1-RES2);}return TBatt_Value;    }return sBaTTMP;}}bm_print(BM_LOG_CRTI, "[force_get_tbat] %d,%d,%d,%d,%d,%d\n", bat_temperature_volt_temp, bat_temperature_volt, fg_current_state, fg_current_temp, fg_r_value, bat_temperature_val);return bat_temperature_val;    #endif    }// Re-constructure r-table profile according to current temperature/* 获得温度-电池内阻表 */profile_p_r_table = fgauge_get_profile_r_table(TEMPERATURE_T);R_PROFILE_STRUC_P fgauge_get_profile_r_table(kal_uint32 temperature){switch (temperature){case TEMPERATURE_T0:return &r_profile_t0[0];break;case TEMPERATURE_T1:return &r_profile_t1[0];break;case TEMPERATURE_T2:return &r_profile_t2[0];break;case TEMPERATURE_T3:return &r_profile_t3[0];break;case TEMPERATURE_T:return &r_profile_temperature[0];break;default:return NULL;break;}}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);void fgauge_construct_r_table_profile(kal_int32 temperature, R_PROFILE_STRUC_P temp_profile_p){R_PROFILE_STRUC_P low_profile_p, high_profile_p;kal_int32 low_temperature, high_temperature;int i, saddles;kal_int32 temp_v_1 = 0, temp_v_2 = 0;kal_int32 temp_r_1 = 0, temp_r_2 = 0;/* 这里跟 NTC 类似,获得在 温度上下限时的电池内阻表 */if (temperature <= TEMPERATURE_T1){low_profile_p    = fgauge_get_profile_r_table(TEMPERATURE_T0);high_profile_p   = fgauge_get_profile_r_table(TEMPERATURE_T1);low_temperature  = (-10);high_temperature = TEMPERATURE_T1;if(temperature < low_temperature){temperature = low_temperature;}}else if (temperature <= TEMPERATURE_T2){low_profile_p    = fgauge_get_profile_r_table(TEMPERATURE_T1);high_profile_p   = fgauge_get_profile_r_table(TEMPERATURE_T2);low_temperature  = TEMPERATURE_T1;high_temperature = TEMPERATURE_T2;if(temperature < low_temperature){temperature = low_temperature;}}else{low_profile_p    = fgauge_get_profile_r_table(TEMPERATURE_T2);  // 温度下限温度-电池内阻表high_profile_p   = fgauge_get_profile_r_table(TEMPERATURE_T3);  // 温度上限温度-电池内阻表low_temperature  = TEMPERATURE_T2;                              // 温度下限high_temperature = TEMPERATURE_T3;                              // 温度上限/* 防溢出温度区间 */if(temperature > high_temperature){temperature = high_temperature;}}/* 获得 温度-电池内阻表 的表格项数 */saddles = fgauge_get_saddles_r_table();int fgauge_get_saddles_r_table(void){return sizeof(r_profile_t2) / sizeof(R_PROFILE_STRUC);}/* Interpolation for V_BAT 插值得到当前温度对应的电池电压 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)                );}#if 0    //(temp_profile_p + i)->resistance = (high_profile_p + i)->resistance;(temp_profile_p + i)->voltage = temp_v_2 +(((temperature - low_temperature) * (temp_v_1 - temp_v_2)) / (high_temperature - low_temperature)                );#endif}/* Interpolation for R_BAT 插值得到当前温度对应的电池内阻 R_BAT */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)                );}#if 0    //(temp_profile_p + i)->voltage = (high_profile_p + i)->voltage;(temp_profile_p + i)->resistance = temp_r_2 +(((temperature - low_temperature) * (temp_r_1 - temp_r_2)) / (high_temperature - low_temperature)                );#endif}// Dumpt new r-table profilefor (i = 0; i < saddles ; i++){bm_print(BM_LOG_CRTI, "<Rbat,VBAT> at %d = <%d,%d>\r\n",temperature, (temp_profile_p+i)->resistance, (temp_profile_p+i)->voltage);}}// Re-constructure battery profile according to current temperature/* 获得当前温度对应的 温度-电池容量表格 */profile_p = fgauge_get_profile(TEMPERATURE_T);BATTERY_PROFILE_STRUC_P fgauge_get_profile(kal_uint32 temperature){switch (temperature){case TEMPERATURE_T0:return &battery_profile_t0[0];break;    case TEMPERATURE_T1:return &battery_profile_t1[0];break;case TEMPERATURE_T2:return &battery_profile_t2[0];break;case TEMPERATURE_T3:return &battery_profile_t3[0];break;case TEMPERATURE_T:return &battery_profile_temperature[0];break;default:return NULL;break;}}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);void fgauge_construct_battery_profile(kal_int32 temperature, BATTERY_PROFILE_STRUC_P temp_profile_p){BATTERY_PROFILE_STRUC_P low_profile_p, high_profile_p;kal_int32 low_temperature, high_temperature;int i, saddles;kal_int32 temp_v_1 = 0, temp_v_2 = 0;/* 获得对就温度所在温度上限/下限所对应的 温度-电池容量表格 */if (temperature <= TEMPERATURE_T1){low_profile_p    = fgauge_get_profile(TEMPERATURE_T0);high_profile_p   = fgauge_get_profile(TEMPERATURE_T1);low_temperature  = (-10);high_temperature = TEMPERATURE_T1;if(temperature < low_temperature){temperature = low_temperature;}}else if (temperature <= TEMPERATURE_T2){low_profile_p    = fgauge_get_profile(TEMPERATURE_T1);high_profile_p   = fgauge_get_profile(TEMPERATURE_T2);low_temperature  = TEMPERATURE_T1;high_temperature = TEMPERATURE_T2;if(temperature < low_temperature){temperature = low_temperature;}}else{low_profile_p    = fgauge_get_profile(TEMPERATURE_T2);  // 温度下限所对应的 温度-电池容量表格high_profile_p   = fgauge_get_profile(TEMPERATURE_T3);  // 温度上限所对应的 温度-电池容量表格low_temperature  = TEMPERATURE_T2;                      // 温度下限high_temperature = TEMPERATURE_T3;                      // 温度上限if(temperature > high_temperature){temperature = high_temperature;}}/* 获得 温度-电池容量 表格项数 */saddles = fgauge_get_saddles();/* 遍历表格,插值获得当前温度对应的容量 */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)                );}(temp_profile_p + i)->percentage = (high_profile_p + i)->percentage;#if 0        (temp_profile_p + i)->voltage = temp_v_2 +(((temperature - low_temperature) * (temp_v_1 - temp_v_2)) / (high_temperature - low_temperature)                );#endif}// Dumpt new battery profilefor (i = 0; i < saddles ; i++){bm_print(BM_LOG_CRTI, "<DOD,Voltage> at %d = <%d,%d>\r\n",temperature, (temp_profile_p+i)->percentage, (temp_profile_p+i)->voltage);}}}/* 首先获得电池电压,然后获得 hw ocv 电压,两者差值较小时,即 hw ocv 为准,然后以此计算出开机电池容量值,并且与 rtc 中记录的电量值进行比较,如果误差较小(小于 40%)则以 rtc 容量值为准,最后初始化 oam 算法的相关参数 */oam_init(); void oam_init(void){int ret=0;int vol_bat=0;kal_int32 vbat_capacity = 0;vol_bat = 5; //set avg times/* 读 PMIC 引用 BATSNS 的电压,即电池电压 */ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE, &vol_bat);      // battery_meter_ctrl() 被初化为 bm_ctrl_cmd()static kal_int32 read_adc_v_bat_sense(void *data){#if defined(CONFIG_POWER_EXT)*(kal_int32*)(data) = 4201;#else/* 读 PMIC 对应的 ADC 的值,并将其转化为对应的电压值,这里即读取 BATSNS 引脚电压 */*(kal_int32*)(data) = PMIC_IMM_GetOneChannelValue(VBAT_CHANNEL_NUMBER,*(kal_int32*)(data),1);#endifreturn STATUS_OK;}/* 这里应该是获得 hw ocv 电压 */ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_HW_OCV, &gFG_voltage);   static kal_int32 read_hw_ocv(void *data){/* 外部充电器的话直接固定值 */#if defined(CONFIG_POWER_EXT)*(kal_int32*)(data) = 3999;#else/* 这里应该是读取相应寄存器, 获得一个 hw ocv 电压  */*(kal_int32*)(data) = get_hw_ocv();int get_hw_ocv(void){#if defined(CONFIG_POWER_EXT)return 4001;    #elsekal_int32 adc_result_reg=0;kal_int32 adc_result=0;kal_int32 r_val_temp=4;    #if defined(SWCHR_POWER_PATH)adc_result_reg = upmu_get_rg_adc_out_wakeup_swchr();adc_result = (adc_result_reg*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;bm_print(BM_LOG_CRTI, "[oam] get_hw_ocv (swchr) : adc_result_reg=%d, adc_result=%d\n", adc_result_reg, adc_result);#else/* 这里应该是读取相应寄存器,获得充电电压值,因为从 PMIC 手册上说,只有在充电电压处于 4.3V <= x <= 7V 时才可以充电*/adc_result_reg = upmu_get_rg_adc_out_wakeup_pchr();kal_uint32 upmu_get_rg_adc_out_wakeup_pchr(void){kal_uint32 ret=0;kal_uint32 val=0;pmic_lock();ret=pmic_read_interface( (kal_uint32)(AUXADC_ADC8),(&val),(kal_uint32)(PMIC_RG_ADC_OUT_WAKEUP_PCHR_MASK),(kal_uint32)(PMIC_RG_ADC_OUT_WAKEUP_PCHR_SHIFT));pmic_unlock();return val;}/* 将得到的 adc 数值转换为对应的 模拟电压值 */adc_result = (adc_result_reg*r_val_temp*VOLTAGE_FULL_RANGE)/ADC_PRECISE;        bm_print(BM_LOG_CRTI, "[oam] get_hw_ocv (pchr) : adc_result_reg=%d, adc_result=%d\n", adc_result_reg, adc_result);#endifadc_result += g_hw_ocv_tune_value;return adc_result;#endif    }#endifreturn STATUS_OK;}/* 获取 hw ocv 电压对应的的电池剩余容量百分比 */gFG_capacity_by_v = fgauge_read_capacity_by_v(gFG_voltage);kal_int32 fgauge_read_capacity_by_v(kal_int32 voltage){    int i = 0, saddles = 0;BATTERY_PROFILE_STRUC_P profile_p;kal_int32 ret_percent = 0;/* 获得当前温度对应的 温度-电池容量表格 */profile_p = fgauge_get_profile(TEMPERATURE_T);if (profile_p == NULL){bm_print(BM_LOG_CRTI, "[FGADC] fgauge get ZCV profile : fail !\r\n");return 100;}/* 获得 温度-电池容量 表格项数 */saddles = fgauge_get_saddles();if (voltage > (profile_p+0)->voltage){return 100; // battery capacity, not dod}    if (voltage < (profile_p+saddles-1)->voltage){return 0; // battery capacity, not dod}/* 遍历表格,插值获得当前电压对应的用掉容量百分比 */for (i = 0; i < saddles - 1; i++){if ((voltage <= (profile_p+i)->voltage) && (voltage >= (profile_p+i+1)->voltage)){ret_percent = (profile_p+i)->percentage +((( ((profile_p+i)->voltage) - voltage ) * ( ((profile_p+i+1)->percentage) - ((profile_p + i)->percentage) ) ) /( ((profile_p+i)->voltage) - ((profile_p+i+1)->voltage) ));         break;}}/* 获得当前剩余电量值 */ret_percent = 100 - ret_percent;return ret_percent;}/* 获得当前电池电压对应的剩余容量百分比 */vbat_capacity = fgauge_read_capacity_by_v(vol_bat);kal_int32 fgauge_read_capacity_by_v(kal_int32 voltage){    int i = 0, saddles = 0;BATTERY_PROFILE_STRUC_P profile_p;kal_int32 ret_percent = 0;/* 获得当前温度对应的 温度-电池容量表格 */profile_p = fgauge_get_profile(TEMPERATURE_T);if (profile_p == NULL){bm_print(BM_LOG_CRTI, "[FGADC] fgauge get ZCV profile : fail !\r\n");return 100;}/* 获得 温度-电池容量 表格项数 */saddles = fgauge_get_saddles();if (voltage > (profile_p+0)->voltage){return 100; // battery capacity, not dod}    if (voltage < (profile_p+saddles-1)->voltage){return 0; // battery capacity, not dod}/* 遍历表格,插值获得当前电压对应的用掉容量百分比 */for (i = 0; i < saddles - 1; i++){if ((voltage <= (profile_p+i)->voltage) && (voltage >= (profile_p+i+1)->voltage)){ret_percent = (profile_p+i)->percentage +((( ((profile_p+i)->voltage) - voltage ) * ( ((profile_p+i+1)->percentage) - ((profile_p + i)->percentage) ) ) /( ((profile_p+i)->voltage) - ((profile_p+i+1)->voltage) ));         break;}}ret_percent = 100 - ret_percent;return ret_percent;}/* 如果充电器插上了,则比较使用当前电压与 hw ocv 电压计算的两都容量差,如果差值较小,继续使用 hw ocv 电压及对应容量为准 */if(bat_is_charger_exist() == KAL_TRUE)///Pulse Charging Algorithm // 判断充电是否插上了 ///kal_bool bat_is_charger_exist(void){return get_charger_detect_status();int get_charger_detect_status(void){kal_bool chr_status;/* 对应的充电芯片的硬件函数 */battery_charging_control(CHARGING_CMD_GET_CHARGER_DET_STATUS,&chr_status);static kal_uint32 charging_get_charger_det_status(void *data){kal_uint32 status = STATUS_OK;#if defined(CHRDET_SW_MODE_EN)kal_uint32 vchr_val=0;/* 读 PMIC 对应的 ADC 的值,并将其转化为对应的电压值,这里即读取 VCDT 引脚电压 */vchr_val = PMIC_IMM_GetOneChannelValue(4,5,1);vchr_val = (((330+39)*100*vchr_val)/39)/100;if( vchr_val > 4300 ){battery_xlog_printk(BAT_LOG_FULL, "[CHRDET_SW_WORKAROUND_EN] upmu_is_chr_det=Y (%d)\n", vchr_val);*(kal_uint32 *)data = KAL_TRUE;   }else{battery_xlog_printk(BAT_LOG_FULL, "[CHRDET_SW_WORKAROUND_EN] upmu_is_chr_det=N (%d)\n", vchr_val);*(kal_uint32 *)data = KAL_FALSE;}        #else/* 读取 CHR_CON0 寄存器 */*(kal_bool*)(data) = upmu_get_rgs_chrdet();kal_uint32 upmu_get_rgs_chrdet(void){kal_uint32 ret=0;kal_uint32 val=0;pmic_lock();ret=pmic_read_interface( (kal_uint32)(CHR_CON0),(&val),(kal_uint32)(PMIC_RGS_CHRDET_MASK),(kal_uint32)(PMIC_RGS_CHRDET_SHIFT));pmic_unlock();return val;}#endif/* 读取 CHR_CON0 寄存器 */if( upmu_get_rgs_chrdet() == 0 )g_charger_type = CHARGER_UNKNOWN;return status;}return chr_status;}}// 如果插入了充电器,hw_ocv 大部分情况是准的,插着 charger 的情况,采集的时候可能会不准{bm_print(BM_LOG_CRTI, "[oam_init_inf] gFG_capacity_by_v=%d, vbat_capacity=%d, \n",gFG_capacity_by_v,vbat_capacity);// to avoid plug in cable without battery, then plug in battery to make hw soc = 100% // if the difference bwtween ZCV and vbat is too large, using vbat instead ZCV /* 如果两者差值超过 30%,我们认为 hw ocv 存在偏差,会以 Vbat 作为 ocv如果两者差值在 30%,继续以 hw ocv 作为 ocv 的数值 */if(((gFG_capacity_by_v == 100) && (vbat_capacity < CUST_POWERON_MAX_VBAT_TOLRANCE)) ||(abs(gFG_capacity_by_v-vbat_capacity)>CUST_POWERON_DELTA_VBAT_TOLRANCE))  {bm_print(BM_LOG_CRTI, "[oam_init] fg_vbat=(%d), vbat=(%d), set fg_vat as vat\n", gFG_voltage,vol_bat);gFG_voltage = vol_bat;gFG_capacity_by_v = vbat_capacity;}       }    // 获得的比较真实的开机容量gFG_capacity_by_v_init = gFG_capacity_by_v;/* 这里与 rtc 中的电量进行比较,确定一个开机显示 UI 电量 */dod_init();void dod_init(void){#if defined(SOC_BY_HW_FG)int ret = 0;    //use get_hw_ocv-----------------------------------------------------------------  /* 获取 PMIC 硬件上的 hw ocv 电压 */ret=battery_meter_ctrl(BATTERY_METER_CMD_GET_HW_OCV, &gFG_voltage);// 对应 PMIC 硬件相关操作函数read_hw_ocv()get_hw_ocv()/* 根据 hw ocv 电压,查表获取对应的电池剩余电量百分比*/gFG_capacity_by_v = fgauge_read_capacity_by_v(gFG_voltage);bm_print(BM_LOG_CRTI, "[FGADC] get_hw_ocv=%d, HW_SOC=%d, SW_SOC = %d\n", gFG_voltage, gFG_capacity_by_v, gFG_capacity_by_v_init);// compare with hw_ocv & sw_ocv, check if less than or equal to 5% tolerance // 将软件 ocv 与硬件 ocv 比较,误差要少于 5%, 才使用之前计算的 sw hov?if ((abs(gFG_capacity_by_v_init - gFG_capacity_by_v) > 5) && (bat_is_charger_exist() == KAL_TRUE)){gFG_capacity_by_v = gFG_capacity_by_v_init;}//-------------------------------------------------------------------------------#endif#if defined(CONFIG_POWER_EXT)g_rtc_fg_soc = gFG_capacity_by_v;    #else/* 获取  DOD0 的数值,电池电量每 10s 写入 RTC 一次,开机会从 RTC 里面读取 DOD0 的值 保存在 rtc 数值,即上次关机的 UI 电量数值 */g_rtc_fg_soc = get_rtc_spare_fg_value();int get_rtc_spare_fg_value(void){//RTC_AL_HOU bit8~14u16 temp;unsigned long flags;spin_lock_irqsave(&rtc_lock, flags);temp = hal_rtc_get_register_status("FG");spin_unlock_irqrestore(&rtc_lock, flags);return temp;}#endif/* g_rtc_fg_soc: 为上次关机前的 UI 电量  gFG_capacity_by_v: 是经过 oam_init() 初始化后的电池真实电压由于存在换电池的风险, 会去判断两者的差值:1. 40% 以内,采用 rtc 电量,即继续显示上次 UI 的电量  2. 如果在 40% 以外,采用真实电池电压的电量,因为有可能更换了电池 设计的初衷: 因为电池特性,在确定 dod 会有误差,从而导致开关机电量跳变,因此采用 rtc 是为了防止电量跳变*/if(g_rtc_fg_soc >= gFG_capacity_by_v){if(((g_rtc_fg_soc != 0) && ((g_rtc_fg_soc-gFG_capacity_by_v) < CUST_POWERON_DELTA_CAPACITY_TOLRANCE) &&(( gFG_capacity_by_v > CUST_POWERON_LOW_CAPACITY_TOLRANCE || bat_is_charger_exist() == KAL_TRUE)))|| ((g_rtc_fg_soc != 0) &&(g_boot_reason == BR_WDT_BY_PASS_PWK || g_boot_reason == BR_WDT || g_boot_reason == BR_TOOL_BY_PASS_PWK || g_boot_reason == BR_2SEC_REBOOT || g_boot_mode == RECOVERY_BOOT))){gFG_capacity_by_v = g_rtc_fg_soc;            }}else{if(((g_rtc_fg_soc != 0) && ((gFG_capacity_by_v-g_rtc_fg_soc) < CUST_POWERON_DELTA_CAPACITY_TOLRANCE) &&(( gFG_capacity_by_v > CUST_POWERON_LOW_CAPACITY_TOLRANCE || bat_is_charger_exist() == KAL_TRUE)))|| ((g_rtc_fg_soc != 0) &&(g_boot_reason == BR_WDT_BY_PASS_PWK || g_boot_reason == BR_WDT || g_boot_reason == BR_TOOL_BY_PASS_PWK || g_boot_reason == BR_2SEC_REBOOT || g_boot_mode == RECOVERY_BOOT))){gFG_capacity_by_v = g_rtc_fg_soc;            }}        bm_print(BM_LOG_CRTI, "[FGADC] g_rtc_fg_soc=%d, gFG_capacity_by_v=%d\n", g_rtc_fg_soc, gFG_capacity_by_v);if (gFG_capacity_by_v == 0 && bat_is_charger_exist() == KAL_TRUE) {gFG_capacity_by_v = 1;bm_print(BM_LOG_CRTI, "[FGADC] gFG_capacity_by_v=%d\n", gFG_capacity_by_v);}//// 在这里最终定义的开机 UI 电量显示gFG_capacity = gFG_capacity_by_v;      // 电池剩余容量百分比gFG_capacity_by_c_init = gFG_capacity; gFG_capacity_by_c = gFG_capacity;gFG_DOD0 = 100 - gFG_capacity;         // 电池使用完电量百分比gFG_DOD1=gFG_DOD0;                      // 电池使用完电量百分比gfg_percent_check_point = gFG_capacity; // 容量检测百分比       #if defined(CHANGE_TRACKING_POINT) gFG_15_vlot = fgauge_read_v_by_capacity( (100-g_tracking_point) );bm_print(BM_LOG_CRTI, "[FGADC] gFG_15_vlot = %dmV\n", gFG_15_vlot);        #else//gFG_15_vlot = fgauge_read_v_by_capacity(86); //14%/* 通过当前使用掉的容量,获取当前电压 */gFG_15_vlot = fgauge_read_v_by_capacity( (100-g_tracking_point) );bm_print(BM_LOG_CRTI, "[FGADC] gFG_15_vlot = %dmV\n", gFG_15_vlot);        if( (gFG_15_vlot > 3800) || (gFG_15_vlot < 3600) ) {bm_print(BM_LOG_CRTI, "[FGADC] gFG_15_vlot(%d) over range, reset to 3700\n", gFG_15_vlot);gFG_15_vlot = 3700;}#endif}/* 先通过 battery_meter_get_battery_temperature() 获得电池温度,再通过 fgauge_get_Q_max() 计算电量 这里获得当前温度的电池的最大容量 */gFG_BATT_CAPACITY_aging = fgauge_get_Q_max(battery_meter_get_battery_temperature()); kal_int32 battery_meter_get_battery_temperature(void){/* 通过获得当前 NTC 电压,查表并进行线性插值法,得到当前的温度值  */return force_get_tbat(); }// 通过当前温度获插值得到当前最大容量kal_int32 fgauge_get_Q_max(kal_int16 temperature){kal_int32 ret_Q_max=0;kal_int32 low_temperature = 0, high_temperature = 0;kal_int32 low_Q_max = 0, high_Q_max = 0;/* 如果温度位于 -20< x <= 0  */if (temperature <= TEMPERATURE_T1){low_temperature = (-10);low_Q_max = Q_MAX_NEG_10;high_temperature = TEMPERATURE_T1;high_Q_max = Q_MAX_POS_0;if(temperature < low_temperature){temperature = low_temperature;}}/* 如果温度位于 0< x <= 25  */else if (temperature <= TEMPERATURE_T2){low_temperature = TEMPERATURE_T1;low_Q_max = Q_MAX_POS_0;high_temperature = TEMPERATURE_T2;high_Q_max = Q_MAX_POS_25;if(temperature < low_temperature){temperature = low_temperature;}}/* 如果温度位于 -25< x <=50  */else{low_temperature  = TEMPERATURE_T2;      // 低温下限low_Q_max = Q_MAX_POS_25;               // 低温最大容量high_temperature = TEMPERATURE_T3;      // 高温上限high_Q_max = Q_MAX_POS_50;              // 高温对应最大容量// 防止范围溢出if(temperature > high_temperature){temperature = high_temperature;}}/*// 核心算法:当前容量 = 低温下限最大容量 + (当前温度 - 低温下限) * [(高温容量 - 低温容量)/(高温上限-低温下限)] */ret_Q_max = low_Q_max +(((temperature - low_temperature) * (high_Q_max - low_Q_max)) / (high_temperature - low_temperature)                );bm_print(BM_LOG_FULL, "[fgauge_get_Q_max] Q_max = %d\r\n", ret_Q_max);return ret_Q_max;}//oam_v_ocv_1 = gFG_voltage;//oam_v_ocv_2 = gFG_voltage;/* 通过当前使用电池容量的百分比,反换算出当前的电压 */oam_v_ocv_init = fgauge_read_v_by_d(gFG_DOD0);kal_int32 fgauge_read_v_by_d(int d_val){    int i = 0, saddles = 0;BATTERY_PROFILE_STRUC_P profile_p;kal_int32 ret_volt = 0;profile_p = fgauge_get_profile(TEMPERATURE_T);if (profile_p == NULL){bm_print(BM_LOG_CRTI, "[fgauge_read_v_by_capacity] fgauge get ZCV profile : fail !\r\n");return 3700;}saddles = fgauge_get_saddles();if (d_val < (profile_p+0)->percentage){        return 3700;         }    if (d_val > (profile_p+saddles-1)->percentage){        return 3700;}for (i = 0; i < saddles - 1; i++){if ((d_val >= (profile_p+i)->percentage) && (d_val <= (profile_p+i+1)->percentage)){ret_volt = (profile_p+i)->voltage -((( d_val - ((profile_p+i)->percentage) ) * ( ((profile_p+i)->voltage) - ((profile_p+i+1)->voltage) ) ) /( ((profile_p+i+1)->percentage) - ((profile_p+i)->percentage) ));         break;}        }    return ret_volt;}oam_v_ocv_2 = oam_v_ocv_1 = oam_v_ocv_init;         // 通过当前使用电池容量的百分比,反换算出当前的电压g_vol_bat_hw_ocv = gFG_voltage;                     // hw ocv 电压//vbat = 5; //set avg times//ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE, &vbat);    //oam_r_1 = fgauge_read_r_bat_by_v(vbat);/* 通过当前 hw ocv 电压,获得的当前电池内阻 */oam_r_1 = fgauge_read_r_bat_by_v(gFG_voltage);oam_r_2 = oam_r_1;oam_d0 = gFG_DOD0;oam_d_5 = oam_d0;oam_i_ori = gFG_current;g_d_hw_ocv = oam_d0;if(oam_init_i == 0){bm_print(BM_LOG_CRTI, "[oam_init] oam_v_ocv_1,oam_v_ocv_2,oam_r_1,oam_r_2,oam_d0,oam_i_ori\n");oam_init_i=1;}bm_print(BM_LOG_CRTI, "[oam_init] %d,%d,%d,%d,%d,%d\n", oam_v_ocv_1, oam_v_ocv_2, oam_r_1, oam_r_2, oam_d0, oam_i_ori);bm_print(BM_LOG_CRTI, "[oam_init_inf] hw_OCV, hw_D0, RTC, D0, oam_OCV_init, tbat\n");bm_print(BM_LOG_CRTI, "[oam_run_inf] oam_OCV1, oam_OCV2, vbat, I1, I2, R1, R2, Car1, Car2,qmax, tbat\n");bm_print(BM_LOG_CRTI, "[oam_result_inf] D1, D2, D3, D4, D5, UI_SOC\n");bm_print(BM_LOG_CRTI, "[oam_init_inf] %d, %d, %d, %d, %d, %d\n",gFG_voltage, (100 - fgauge_read_capacity_by_v(gFG_voltage)), g_rtc_fg_soc, gFG_DOD0 ,oam_v_ocv_init, force_get_tbat());}bm_print(BM_LOG_CRTI, "[battery_meter_initial] SOC_BY_SW_FG done\n");#endifreturn 0;#endif}/* 获得要监控的电量,(用这个电量反换出电压来校验?)*/BMT_status.nPercent_ZCV = battery_meter_get_battery_nPercent_zcv();kal_int32 battery_meter_get_battery_nPercent_zcv(void)    {#if defined(CONFIG_POWER_EXT)return 3700;#elsereturn gFG_15_vlot; // 15% zcv,  15% can be customized by 100-g_tracking_point#endif  }battery_meter_initilized = KAL_TRUE;}// 1. 判断是插入的是否充电器还是电脑 USB,看能不能进行充电/* 如果连接的 USB 线为 USB 充电线,或者电脑 USB 线,则打开 USB,这里会通过  BC1.1 来判断是电脑 USB 还是 USB 充电器,来决定充电电流 否则连接的不是充电线或者 USB 作为一个从设备使用,要断开 USB? */mt_battery_charger_detect_check();static void mt_battery_charger_detect_check(void){/* 根据 USB 是主从状态,以及外部充电电压是否正常来判断是否允许充电,这里如果是 USB 作为从设备,允许充电    */if( upmu_is_chr_det() == KAL_TRUE )///PMIC PCHR Related APIs///kal_bool upmu_is_chr_det(void){#if defined(CONFIG_POWER_EXT)//return KAL_TRUE;return get_charger_detect_status();// 如果是外部充电芯片,则直接会调用外部芯片的充电相关函数,如 charging_hw_bq24196.c 中的battery_charging_control(CHARGING_CMD_GET_CHARGER_DET_STATUS,&chr_status);#else   kal_uint32 tmp32;/* 这里是 PMIC,调用 PMIC 的充电相关函数 charging_hw_pmic.c, 这里查询 PMIC 的 CHR_CON0 寄存器,检查充电状态 */tmp32=get_charger_detect_status();int get_charger_detect_status(void){kal_bool chr_status;battery_charging_control(CHARGING_CMD_GET_CHARGER_DET_STATUS,&chr_status);static kal_uint32 charging_get_charger_det_status(void *data){kal_uint32 status = STATUS_OK;#if defined(CHRDET_SW_MODE_EN)kal_uint32 vchr_val=0;// 获得 PMIC 的 VCDT 充电电压值vchr_val = PMIC_IMM_GetOneChannelValue(4,5,1);vchr_val = (((330+39)*100*vchr_val)/39)/100;if( vchr_val > 4300 ){battery_xlog_printk(BAT_LOG_FULL, "[CHRDET_SW_WORKAROUND_EN] upmu_is_chr_det=Y (%d)\n", vchr_val);*(kal_uint32 *)data = KAL_TRUE; }else{battery_xlog_printk(BAT_LOG_FULL, "[CHRDET_SW_WORKAROUND_EN] upmu_is_chr_det=N (%d)\n", vchr_val);*(kal_uint32 *)data = KAL_FALSE;}        #else*(kal_bool*)(data) = upmu_get_rgs_chrdet();#endif/* 读取 PMIC  CHR_CON0 寄存器 */if( upmu_get_rgs_chrdet() == 0 )g_charger_type = CHARGER_UNKNOWN;return status;}return chr_status;}if(tmp32 == 0){return KAL_FALSE;}else{/* 如果 USB 作为一个不是以从设备运行,是不允许充电的 */if( mt_usb_is_device() )// Usb20.c (s:\i841\mediatek\platform\mt6582\kernel\drivers\usb20) /* ================================ *//* connect and disconnect functions *//* ================================ */bool mt_usb_is_device(void){DBG(4,"called\n");/* 如果 mtk_musb 为 0 的话,表明 MTK 的 USB 在作为一个主机使用,不允许充电的 */if(!mtk_musb){DBG(0,"mtk_musb is NULL\n");return false; // don't do charger detection when usb is not ready} else {DBG(4,"is_host=%d\n",mtk_musb->is_host);}return !mtk_musb->is_host;}{battery_xlog_printk(BAT_LOG_FULL, "[upmu_is_chr_det] Charger exist and USB is not host\n");return KAL_TRUE;}else{battery_xlog_printk(BAT_LOG_FULL, "[upmu_is_chr_det] Charger exist but USB is host\n");return KAL_FALSE;}}#endif  }// 如果插入的是 USB 充电线或者 电脑的 USB 线,则打开 USB ,因为充电时 MTK 作为从设备,是允许充电的{wake_lock(&battery_suspend_lock);BMT_status.charger_exist = KAL_TRUE;// 无线充电支持宏#if defined(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        /* 这个结构体是在前面初始化的,所以第一次执行,该值未设置 = 0,在这里通过 BC1.1 协议检测外部连接的 USB 线类型,是主是从,还是充电/被充最后根据类型判断要不要启动 USB */if(BMT_status.charger_type == CHARGER_UNKNOWN){/* 调用 PMIC 硬件相关函数,检测充电类型, 这里应用了 BC1.1 协议来检测  */mt_charger_type_detection();CHARGER_TYPE mt_charger_type_detection(void){CHARGER_TYPE CHR_Type_num = CHARGER_UNKNOWN;mutex_lock(&charger_type_mutex);#if defined(MTK_WIRELESS_CHARGER_SUPPORT)battery_charging_control(CHARGING_CMD_GET_CHARGER_TYPE,&CHR_Type_num);BMT_status.charger_type = CHR_Type_num; #else/* 调用 PMIC 硬件相关函数,检测充电类型, 这里应用了 BC1.1 协议来检测  */if(BMT_status.charger_type == CHARGER_UNKNOWN){/* 调用 PMIC 硬件相关函数,检测充电类型, 这里应用了 BC1.1 协议来检测  */battery_charging_control(CHARGING_CMD_GET_CHARGER_TYPE,&CHR_Type_num);static kal_uint32 charging_get_charger_type(void *data){kal_uint32 status = STATUS_OK;#if defined(CONFIG_POWER_EXT)*(CHARGER_TYPE*)(data) = STANDARD_HOST;#else#if defined(MTK_WIRELESS_CHARGER_SUPPORT)int wireless_state = 0;/* 无线充电判断引脚,直接拉低就好 */wireless_state = mt_get_gpio_in(wireless_charger_gpio_number);if(wireless_state == WIRELESS_CHARGER_EXIST_STATE){*(CHARGER_TYPE*)(data) = WIRELESS_CHARGER;battery_xlog_printk(BAT_LOG_CRTI, "WIRELESS_CHARGER!\r\n");return status;}#endifif(g_charger_type!=CHARGER_UNKNOWN && g_charger_type!=WIRELESS_CHARGER){*(CHARGER_TYPE*)(data) = g_charger_type;battery_xlog_printk(BAT_LOG_CRTI, "return %d!\r\n", g_charger_type);return status;}charging_type_det_done = KAL_FALSE;/*//BC1.1 充电协议,主要用来区分是插入的是 USB 还是充电器,如果是 USB 只能提供 500ma 充电,如果是充电器,则可以大电流充电  *//********* Step initial  ***************/        hw_bc11_init();static void hw_bc11_init(void){msleep(300);Charger_Detect_Init();//RG_BC11_BIAS_EN=1    upmu_set_rg_bc11_bias_en(0x1);//RG_BC11_VSRC_EN[1:0]=00upmu_set_rg_bc11_vsrc_en(0x0);//RG_BC11_VREF_VTH = [1:0]=00upmu_set_rg_bc11_vref_vth(0x0);//RG_BC11_CMP_EN[1.0] = 00upmu_set_rg_bc11_cmp_en(0x0);//RG_BC11_IPU_EN[1.0] = 00upmu_set_rg_bc11_ipu_en(0x0);//RG_BC11_IPD_EN[1.0] = 00upmu_set_rg_bc11_ipd_en(0x0);//BC11_RST=1upmu_set_rg_bc11_rst(0x1);//BC11_BB_CTRL=1upmu_set_rg_bc11_bb_ctrl(0x1);//msleep(10);mdelay(50);if(Enable_BATDRV_LOG == BAT_LOG_FULL){battery_xlog_printk(BAT_LOG_FULL, "hw_bc11_init() \r\n");hw_bc11_dump_register();}  }/********* Step DCD ***************/  if(1 == hw_bc11_DCD()){/********* Step A1 ***************/if(1 == hw_bc11_stepA1()){*(CHARGER_TYPE*)(data) = APPLE_2_1A_CHARGER;battery_xlog_printk(BAT_LOG_CRTI, "step A1 : Apple 2.1A CHARGER!\r\n");}    else{*(CHARGER_TYPE*)(data) = NONSTANDARD_CHARGER;battery_xlog_printk(BAT_LOG_CRTI, "step A1 : Non STANDARD CHARGER!\r\n");}}else{/********* Step A2 ***************/if(1 == hw_bc11_stepA2()){/********* Step B2 ***************/if(1 == hw_bc11_stepB2()){*(CHARGER_TYPE*)(data) = STANDARD_CHARGER;battery_xlog_printk(BAT_LOG_CRTI, "step B2 : STANDARD CHARGER!\r\n");}else{*(CHARGER_TYPE*)(data) = CHARGING_HOST;battery_xlog_printk(BAT_LOG_CRTI, "step B2 :  Charging Host!\r\n");}}else{*(CHARGER_TYPE*)(data) = STANDARD_HOST;battery_xlog_printk(BAT_LOG_CRTI, "step A2 : Standard USB Host!\r\n");}}/********* Finally setting *******************************/hw_bc11_done();static void hw_bc11_done(void){//RG_BC11_VSRC_EN[1:0]=00upmu_set_rg_bc11_vsrc_en(0x0);//RG_BC11_VREF_VTH = [1:0]=0upmu_set_rg_bc11_vref_vth(0x0);//RG_BC11_CMP_EN[1.0] = 00upmu_set_rg_bc11_cmp_en(0x0);//RG_BC11_IPU_EN[1.0] = 00upmu_set_rg_bc11_ipu_en(0x0);//RG_BC11_IPD_EN[1.0] = 00upmu_set_rg_bc11_ipd_en(0x0);//RG_BC11_BIAS_EN=0upmu_set_rg_bc11_bias_en(0x0); Charger_Detect_Release();void Charger_Detect_Release(void){/* RG_USB20_BC11_SW_EN = 1'b0 */USBPHY_CLR8(0x1a, 0x80);udelay(1);//4 14. turn off internal 48Mhz PLL.usb_enable_clock(false);printk("Charger_Detect_Release\n");}if(Enable_BATDRV_LOG == BAT_LOG_FULL){battery_xlog_printk(BAT_LOG_FULL, "hw_bc11_done() \r\n");hw_bc11_dump_register();}}charging_type_det_done = KAL_TRUE;g_charger_type = *(CHARGER_TYPE*)(data);#endifreturn status;}BMT_status.charger_type = CHR_Type_num; }   #endifmutex_unlock(&charger_type_mutex);return BMT_status.charger_type;}/* 如果充电线是 标准主模式 或者 主充电模式, 连接上 USB  */if((BMT_status.charger_type==STANDARD_HOST) || (BMT_status.charger_type==CHARGING_HOST) ){/*  连接 USB,充电时可以连接 USB 的 */mt_usb_connect();void mt_usb_connect(void){printk("[MUSB] USB is ready for connect\n");DBG(3, "is ready %d is_host %d power %d\n",mtk_musb->is_ready,mtk_musb->is_host , mtk_musb->power);if (!mtk_musb || !mtk_musb->is_ready || mtk_musb->is_host || mtk_musb->power)return;DBG(0,"cable_mode=%d\n",cable_mode);/*     CABLE_MODE_CHRG_ONLY = 0, CABLE_MODE_NORMAL, CABLE_MODE_HOST_ONLY, CABLE_MODE_MAX 上面这些模式,应该是通过 BC1.1 来判断出来的 */if(cable_mode != CABLE_MODE_NORMAL){DBG(0,"musb_sync_with_bat, USB_CONFIGURED\n");musb_sync_with_bat(mtk_musb,USB_CONFIGURED);void musb_sync_with_bat(struct musb *musb,int usb_state){#ifndef FPGA_PLATFORMDBG(0,"BATTERY_SetUSBState, state=%d\n",usb_state);// linear_charging.c,线性充电BATTERY_SetUSBState(usb_state);void BATTERY_SetUSBState(int usb_state_value){#if defined(CONFIG_POWER_EXT)battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY_SetUSBState] in FPGA/EVB, no service\r\n");#elseif ( (usb_state_value < USB_SUSPEND) || ((usb_state_value > USB_CONFIGURED))){battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] BAT_SetUSBState Fail! Restore to default value\r\n");    usb_state_value = USB_UNCONFIGURED;} else {battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] BAT_SetUSBState Success! Set %d\r\n", usb_state_value);    g_usb_state = usb_state_value;    }#endif  }wake_up_bat();void wake_up_bat (void){battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] wake_up_bat. \r\n");chr_wake_up_bat = KAL_TRUE;    bat_thread_timeout = KAL_TRUE;wake_up(&bat_thread_wq);}#endif}mtk_musb->power = true;return;}if (!wake_lock_active(&mtk_musb->usb_lock))wake_lock(&mtk_musb->usb_lock);// Program the HDRC to start (enable interrupts, dma, etc.).// 启动 USB musb_start(mtk_musb);/*-------------------------------------------------------------------------*/ /** Program the HDRC to start (enable interrupts, dma, etc.).*/void musb_start(struct musb *musb){void __iomem    *regs = musb->mregs;int vbusdet_retry = 5;u8  intrusbe;DBG(0, "start, is_host=%d is_active=%d\n", musb->is_host, musb->is_active);if(musb->is_active) {if(musb->is_host) {DBG(0, "we are host now, add more interrupt devctl=%x\n", musb_readb(mtk_musb->mregs,MUSB_DEVCTL));musb->intrtxe = 0xffff;musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);musb->intrrxe = 0xfffe;musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);musb_writeb(regs,MUSB_INTRUSBE,0xf7);return;}}musb_platform_enable(musb);musb_generic_disable(musb);intrusbe= musb_readb(regs, MUSB_INTRUSBE);if (musb->is_host){musb->intrtxe = 0xffff;musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);musb->intrrxe = 0xfffe;musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);intrusbe = 0xf7;while(!musb_platform_get_vbus_status(musb)) {mdelay(100);if(vbusdet_retry--<=1) {DBG(0, "VBUS detection fail!\n");break;}}} else if(!musb->is_host){intrusbe |= MUSB_INTR_RESET; //device mode enable reset interrupt}musb_writeb(regs,MUSB_INTRUSBE,intrusbe);if (musb_speed) {/* put into basic highspeed mode and start session */musb_writeb(regs, MUSB_POWER, MUSB_POWER_SOFTCONN| MUSB_POWER_HSENAB/* ENSUSPEND wedges tusb */| MUSB_POWER_ENSUSPEND);} else {/* put into basic fullspeed mode and start session */musb_writeb(regs, MUSB_POWER, MUSB_POWER_SOFTCONN/* ENSUSPEND wedges tusb */| MUSB_POWER_ENSUSPEND);}musb->is_active = 1;}printk("[MUSB] USB connect\n");}}}#endif        battery_xlog_printk(BAT_LOG_CRTI, "[BAT_thread]Cable in, CHR_Type_num=%d\r\n", BMT_status.charger_type);}// 如果连接入的不是正常的充电线,或者此时 USB 接口以主机状态运行 ,则设置电池状态参数,断开 USB 连接?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_xlog_printk(BAT_LOG_CRTI, "[BAT_thread]Cable out \r\n");// 断开 USB 与电脑的连接  mt_usb_disconnect();void mt_usb_disconnect(void){printk("[MUSB] USB is ready for disconnect\n");if (!mtk_musb || !mtk_musb->is_ready || mtk_musb->is_host || !mtk_musb->power)return;musb_stop(mtk_musb);if (wake_lock_active(&mtk_musb->usb_lock))wake_unlock(&mtk_musb->usb_lock);DBG(0,"cable_mode=%d\n",cable_mode);if (cable_mode != CABLE_MODE_NORMAL) {DBG(0,"musb_sync_with_bat, USB_SUSPEND\n");musb_sync_with_bat(mtk_musb,USB_SUSPEND);mtk_musb->power = false;}printk("[MUSB] USB disconnect\n");}}}///// 2. 通过具体的充电芯片来获得电池信息,充电信息, 获得电池电量百分比 /* 通过 oam 算法,获得电量百分比 */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;// 获得 BATSNS 引脚电压 bat_vol = battery_meter_get_battery_voltage();kal_int32 battery_meter_get_battery_voltage(void){int ret=0;int val=5;val = 5; //set avg times// 获得 PMIC 的 BATSNS 引脚电压 ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE, &val);static kal_int32 read_adc_v_bat_sense(void *data){#if defined(CONFIG_POWER_EXT)*(kal_int32*)(data) = 4201;#else*(kal_int32*)(data) = PMIC_IMM_GetOneChannelValue(VBAT_CHANNEL_NUMBER,*(kal_int32*)(data),1);#endifreturn STATUS_OK;}g_sw_vbat_temp = val;return val;}/* 获得 PMIC 的  ISENSE 引脚电压*/Vsense = battery_meter_get_VSense();kal_int32 battery_meter_get_VSense(void){#if defined(CONFIG_POWER_EXT)return 0;#elseint ret=0;int val=0;val = 1; //set avg times/* 获得 PMIC 的  ISENSE 引脚电压*/ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_I_SENSE, &val);read_adc_v_i_sense(void *data)*(kal_int32*)(data) = PMIC_IMM_GetOneChannelValue(ISENSE_CHANNEL_NUMBER,*(kal_int32*)(data),1);return val;#endif}/* 获得充电电流 */ICharging = battery_meter_get_charging_current();kal_int32 battery_meter_get_charging_current(void){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/* 获得 PMIC 的 BATSNS 引脚电压 */ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE, &val);read_adc_v_bat_sense(void *data)*(kal_int32*)(data) = PMIC_IMM_GetOneChannelValue(VBAT_CHANNEL_NUMBER,*(kal_int32*)(data),1);ADC_BAT_SENSE_tmp[i] = val;val = 1; //set avg times/* 获得 PMIC 的 ISENSE 引脚电压 */ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_I_SENSE, &val);read_adc_v_i_sense(void *data)*(kal_int32*)(data) = PMIC_IMM_GetOneChannelValue(ISENSE_CHANNEL_NUMBER,*(kal_int32*)(data),1);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;}/* 获得充电器电压 */charger_vol = battery_meter_get_charger_voltage();kal_int32 battery_meter_get_charger_voltage(void){int ret=0;int val=0;val = 5; // set avg timesret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_CHARGER, &val);static kal_int32 read_adc_v_charger(void *data){    #if defined(CONFIG_POWER_EXT)*(kal_int32*)(data) = 5001;#elsekal_int32 val;/* 获取 PMIC 的 VCDT 引脚电压 */val = PMIC_IMM_GetOneChannelValue(VCHARGER_CHANNEL_NUMBER,*(kal_int32*)(data),1);val = (((R_CHARGER_1+R_CHARGER_2)*100*val)/R_CHARGER_2)/100;*(kal_int32*)(data) = val;#endifreturn STATUS_OK;}//val = (((R_CHARGER_1+R_CHARGER_2)*100*val)/R_CHARGER_2)/100;return val;}/* 通过获得当前 NTC 电压,查表并进行线性插值法,得到当前的温度值  */temperature = battery_meter_get_battery_temperature();return force_get_tbat();/* 这里用来获取电池 NTC 的电压 */temperatureV = battery_meter_get_tempV();ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_TEMP, &val);                                                                                                                           static kal_int32 read_adc_v_bat_temp(void *data){#if defined(CONFIG_POWER_EXT)*(kal_int32*)(data) = 0;#else#if defined(MTK_PCB_TBAT_FEATURE)int ret = 0, data[4], i, ret_value = 0, ret_temp = 0;int Channel=1;if( IMM_IsAdcInitReady() == 0 )return g_adc_init_flag;{bm_print(BM_LOG_CRTI, "[get_tbat_volt] AUXADC is not ready");return 0;}i = times;while (i--){ret_value = IMM_GetOneChannelValue(Channel, data, &ret_temp);ret += ret_temp;bm_print(BM_LOG_FULL, "[get_tbat_volt] ret_temp=%d\n",ret_temp);}ret = ret*1500/4096 ;ret = ret/times;bm_print(BM_LOG_CRTI, "[get_tbat_volt] Battery output mV = %d\n",ret);*(kal_int32*)(data) = ret;#elsebm_print(BM_LOG_FULL, "[read_adc_v_charger] return PMIC_IMM_GetOneChannelValue(4,times,1);\n");/* 读取 PMIC 的 BATON1 引脚电压 */*(kal_int32*)(data) = PMIC_IMM_GetOneChannelValue(VBATTEMP_CHANNEL_NUMBER,*(kal_int32*)(data),1);#endif#endifreturn STATUS_OK;}/* 获得 下拉电阻与 NTC 并并联的电压 */temperatureR = battery_meter_get_tempR(temperatureV);/* 上拉电压/下拉电压  = 上拉电阻/ 下拉电阻 */TRes = (RBAT_PULL_UP_R*dwVolt) / (RBAT_PULL_UP_VOLT-dwVolt);      /* bat_thread_wakeup() 每 10s 唤醒一次,唤醒时设置  bat_meter_timeout = KAL_TRUE这时候更新电池电量百分比 */if(bat_meter_timeout == KAL_TRUE || bat_spm_timeout == TRUE){/* oam 算法通过两种方式更新电压,去逼近真实的开路电压,最终查表获取近似真实的电量值百分比,方法 1,查表获得电池百分比,方法 2,库伦积分以方法2获得的参数补偿方法 1 的值,具体方法见 oam_run()*/SOC = battery_meter_get_battery_percentage();kal_int32 battery_meter_get_battery_percentage(void){#if defined(CONFIG_POWER_EXT)return 50;#elseif(bat_is_charger_exist() == KAL_FALSE)// 这里查询 PMIC 的 CHR_CON0 寄存器,检查充电状态return get_charger_detect_status();battery_charging_control(CHARGING_CMD_GET_CHARGER_DET_STATUS,&chr_status);static kal_uint32 charging_get_charger_det_status(void *data){kal_uint32 status = STATUS_OK;#if defined(CHRDET_SW_MODE_EN)kal_uint32 vchr_val=0;vchr_val = PMIC_IMM_GetOneChannelValue(4,5,1);vchr_val = (((330+39)*100*vchr_val)/39)/100;if( vchr_val > 4300 ){battery_xlog_printk(BAT_LOG_FULL, "[CHRDET_SW_WORKAROUND_EN] upmu_is_chr_det=Y (%d)\n", vchr_val);*(kal_uint32 *)data = KAL_TRUE; }else{battery_xlog_printk(BAT_LOG_FULL, "[CHRDET_SW_WORKAROUND_EN] upmu_is_chr_det=N (%d)\n", vchr_val);*(kal_uint32 *)data = KAL_FALSE;}        #else*(kal_bool*)(data) = upmu_get_rgs_chrdet();#endifif( upmu_get_rgs_chrdet() == 0 )g_charger_type = CHARGER_UNKNOWN;return status;}fg_qmax_update_for_aging_flag = 1;// AUX ADC算法指只依赖ADC读值,然后查表读取电量的算法#if defined(SOC_BY_AUXADC)return auxadc_algo_run();#endif//通过开路电压查表得到初始电量D0,后续电量通过电流积分累积,通用性强,依赖初始电量的精确度。#if defined(SOC_BY_HW_FG)if(g_auxadc_solution == 1){return auxadc_algo_run();}else    {fgauge_algo_run();    return gFG_capacity_by_c; // hw fg, //return gfg_percent_check_point; // voltage mode    }#endif/*//  6582 平台用的计量方法【在 Battery_Charging_Introduction_for_customer_V1.0.pdf】 SW FG算法和HW FG算法。事实上MTK平台项目通常采用的是混合型算法。 */#if defined(SOC_BY_SW_FG)oam_run();void oam_run(void){/* SW FG的核心 在于 通过两种方式更新电压,去逼近真实开路电压  最终查表获取近似真实的电量值。 ocv1 被假定为开路电压  ocv2则是闭路电压,D0 D1 D2 D3 D4 D5 代表不同的放电深度*/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();getrawmonotonic(&now_time);//delta_time = now_time - last_oam_run_time;delta_time = now_time.tv_sec - last_oam_run_time.tv_sec;bm_print(BM_LOG_FULL, "[oam_run_time] last time=%d, now time=%d, delta time=%d\n", last_oam_run_time.tv_sec, now_time.tv_sec, delta_time);last_oam_run_time = now_time;// Reconstruct table if temp changed;fgauge_construct_table_by_temp();void fgauge_construct_table_by_temp(void){#if defined(CONFIG_POWER_EXT)#elsekal_uint32 i;static kal_int32 init_temp = KAL_TRUE;static kal_int32 curr_temp, last_temp, avg_temp;static kal_int32 battTempBuffer[TEMP_AVERAGE_SIZE];static kal_int32 temperature_sum;static kal_uint8 tempIndex = 0;/* 通过获得当前 NTC 电压,查表并进行线性插值法,得到当前的温度值  */curr_temp = battery_meter_get_battery_temperature();// Temperature window initif (init_temp == KAL_TRUE){for (i=0; i<TEMP_AVERAGE_SIZE; i++){battTempBuffer[i] = curr_temp;            }last_temp = curr_temp;temperature_sum = curr_temp * TEMP_AVERAGE_SIZE;init_temp = KAL_FALSE;}// Temperature sliding window temperature_sum -= battTempBuffer[tempIndex];temperature_sum += curr_temp;battTempBuffer[tempIndex] = curr_temp;avg_temp = (temperature_sum)/TEMP_AVERAGE_SIZE;if (avg_temp != last_temp){bm_print(BM_LOG_FULL, "[fgauge_construct_table_by_temp] reconstruct table by temperature change from (%d) to (%d)\r\n", last_temp, avg_temp);/* 获得 温度-电池内阻 表格*/fgauge_construct_r_table_profile(curr_temp, fgauge_get_profile_r_table(TEMPERATURE_T));/* 由当前温度,通过 温度-电池容量表格,获得对应的 温度-电池容量 数据,即能得到当前温度所对应的用掉的电池容量 */fgauge_construct_battery_profile(curr_temp, fgauge_get_profile(TEMPERATURE_T));last_temp = avg_temp;}tempIndex = (tempIndex+1)%TEMP_AVERAGE_SIZE;#endif}vol_bat = 15; //set avg times/* 获得 PMIC 的 BATSNS 寄存器值这里获得 v_bat 当前电池电压值,这是有负载时的电压值 */ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_ADC_V_BAT_SENSE, &vol_bat);read_adc_v_bat_sense(void *data)*(kal_int32*)(data) = PMIC_IMM_GetOneChannelValue(VBAT_CHANNEL_NUMBER,*(kal_int32*)(data),1);//ret = battery_meter_ctrl(BATTERY_METER_CMD_GET_HW_OCV, &vol_bat_hw_ocv);//d_hw_ocv = fgauge_read_d_by_v(vol_bat_hw_ocv);/* 通过当前开路电压 - 负载时电压 = 当前电流 * 电池内阻 获得当前电流值 */oam_i_1 = (((oam_v_ocv_1-vol_bat)*1000)*10) / oam_r_1;    //0.1mAoam_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.1mAhoam_car_2 = (oam_i_2*delta_time/3600) + oam_car_2; //0.1mAh/* 这里使用的就是库伦积分法:D1 = D0 + (-CAR)/Cmax 获得当前的电池容量百分比 */oam_d_1 = oam_d0 + (oam_car_1*100/10)/gFG_BATT_CAPACITY_aging; // gFG_BATT_CAPACITY_aging: 当前温度对应的最大电池容量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;/*//// 整个程序的核心在这里, 他使用了两种方法更新电量:1. 使用补偿过的闭路电压,查表获得电量       【返回给用户的】2. 使用软件库伦积分,得到电量值             【用来校正的】两个方法相对独立,但是在此处,方法 1 使用了 方法 2 的电流来进行较正!!!!!!!!!!//mtk_imp_tracking() 对闭合电压补偿后,当作开路电压使用通过对当前有负载的电池电压进行补偿,获得当前开路电压 */oam_v_ocv_1 = vol_bat + mtk_imp_tracking(vol_bat, oam_i_2, 5);// ============================================================ // SW FG// 这个里面返回的是 I*R 的值,即 当前电流 * 当前负载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;  // 开路电压,第一次 = 闭路电压,后来都是补偿过 IR 的 int i = 0;/* 迭代 5 次,反复执行 闭合补偿得开路电压,开路电压查表得内阻,内阻补偿闭路电压 */for(i=0 ; i < recursion_time ; i++) {/* 将闭路电压当做开路电压查内阻 */gFG_resistance_bat = fgauge_read_r_bat_by_v(temp_voltage_2); /* 算出 IR drop */ret_compensate_value = ( (ori_current) * (gFG_resistance_bat + R_FG_VALUE)) / 1000;// ret_compensate是int型变量 做除法时取整处理 会引入较大误差, 加上这个值使结果四舍五入ret_compensate_value = (ret_compensate_value+(10/2)) / 10; /* 开路电压 = 当前电压 + IR drop   */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); /* 算出 IR drop */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; // I*R 补偿负载 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);    /* 该内阻 R* 电流 I 算出最终的电压补偿值 V Vbat 是闭路电压 SW ocv 是开路电压 Sw ocv = V + Vbat */return ret_compensate_value;}/* 通过补偿的开路电压,获得电池电量百分比 */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);    #if 0oam_d_4 = (oam_d_2+oam_d_3)/2;#elseoam_d_4 = oam_d_3;#endif// 从上一次运行本函数到现在当前电量变化的值gFG_columb = oam_car_2/10;  //mAh/* 判断充电状态 */if( (oam_i_1 < 0) || (oam_i_2 < 0) )gFG_Is_Charging = KAL_TRUE;elsegFG_Is_Charging = KAL_FALSE;#if 0if(gFG_Is_Charging == KAL_FALSE){d5_count_time = 60;         }else{charging_current = get_charging_setting_current();    charging_current = charging_current / 100;d5_count_time_rate = (((gFG_BATT_CAPACITY_aging*60*60/100/(charging_current-50))*10)+5)/10;if(d5_count_time_rate < 1)d5_count_time_rate = 1;d5_count_time = d5_count_time_rate;}#elsed5_count_time = 60;#endif    /// D5 与 D3 对比                                                                                                           /* 对获得的 D3 进行优化,有 1min 的限制,不会跳变,电量变化更平滑 1分钟内电量值不会改变,且每分钟电量的变化不会大于1%,这样用户体验会比较好。防止因为低电压时陡峭的电量曲线,以及比较耗电的应用,或突然的大电流引起的电量跳变。当然特殊应用下应该取消这个功能,否则会带来电量变化延时等问题*/if(d5_count >= d5_count_time)// 60s 执行一次{/* 限制电量变化,每 1 分钟,只能变化 1% *//* 放电状态 */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// 10s 执行一次本函数{d5_count = d5_count + 10;}bm_print(BM_LOG_CRTI, "[oam_run] %d,%d,%d,%d,%d,%d,%d,%d\n", d5_count, d5_count_time, oam_d_3_pre, oam_d_3, oam_d_4_pre, oam_d_4, oam_d_5, charging_current);    if(oam_run_i == 0){bm_print(BM_LOG_FULL, "[oam_run] oam_i_1,oam_i_2,oam_car_1,oam_car_2,oam_d_1,oam_d_2,oam_v_ocv_1,oam_d_3,oam_r_1,oam_v_ocv_2,oam_r_2,vol_bat,g_vol_bat_hw_ocv,g_d_hw_ocv\n");oam_run_i=1;}    bm_print(BM_LOG_FULL, "[oam_run] %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", oam_i_1,oam_i_2,oam_car_1,oam_car_2,oam_d_1,oam_d_2,oam_v_ocv_1,oam_d_3,oam_r_1,oam_v_ocv_2,oam_r_2,vol_bat,g_vol_bat_hw_ocv,g_d_hw_ocv);    bm_print(BM_LOG_FULL, "[oam_total] %d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",gFG_capacity_by_c, gFG_capacity_by_v, gfg_percent_check_point,oam_d_1, oam_d_2, oam_d_3, oam_d_4, oam_d_5, gFG_capacity_by_c_init, g_d_hw_ocv);bm_print(BM_LOG_CRTI, "[oam_total_s] %d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",gFG_capacity_by_c,        // 1gFG_capacity_by_v,        // 2gfg_percent_check_point,  // 3(100-oam_d_1),            // 4(100-oam_d_2),            // 5(100-oam_d_3),            // 6(100-oam_d_4),            // 9(100-oam_d_5),            // 10gFG_capacity_by_c_init,   // 7(100-g_d_hw_ocv)          // 8);          bm_print(BM_LOG_FULL, "[oam_total_s_err] %d,%d,%d,%d,%d,%d,%d\n",(gFG_capacity_by_c - gFG_capacity_by_v), (gFG_capacity_by_c - gfg_percent_check_point),(gFG_capacity_by_c - (100-oam_d_1)), (gFG_capacity_by_c - (100-oam_d_2)), (gFG_capacity_by_c - (100-oam_d_3)), (gFG_capacity_by_c - (100-oam_d_4)), (gFG_capacity_by_c - (100-oam_d_5)));bm_print(BM_LOG_CRTI, "[oam_init_inf] %d, %d, %d, %d, %d, %d\n",gFG_voltage, (100 - fgauge_read_capacity_by_v(gFG_voltage)), g_rtc_fg_soc, gFG_DOD0 ,oam_v_ocv_init, force_get_tbat());bm_print(BM_LOG_CRTI, "[oam_run_inf] %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", oam_v_ocv_1,oam_v_ocv_2,vol_bat,oam_i_1,oam_i_2,oam_r_1,oam_r_2,oam_car_1,oam_car_2,gFG_BATT_CAPACITY_aging,force_get_tbat(), oam_d0);  bm_print(BM_LOG_CRTI, "[oam_result_inf] %d, %d, %d, %d, %d, %d\n", oam_d_1, oam_d_2, oam_d_3, oam_d_4, oam_d_5, BMT_status.UI_SOC);  }// 这边的返回值将填充BMT_status.SOC ,这个参数再经过优化得到BMT_status.UI_SOC就是菜单栏看到的电池电量了。///* D5 是做过平滑,每 1min 电量变化只能是 1% 的,可能会有延时  */#if (OAM_D5 == 1)return (100-oam_d_5);#else/* D2 则是原版的,没有做平滑处理,电量变化可能会跳变,但是变化及时 */return (100-oam_d_2);#endif#endif#endif}bat_meter_timeout = KAL_FALSE;bat_spm_timeout = FALSE;}else{if (previous_SOC == -1)/* 通过两种方式更新电压,去逼近真实的开路电压,最终查表获取近似真实的电量值百分比 */SOC = battery_meter_get_battery_percentage();elseSOC = previous_SOC;     }ZCV = battery_meter_get_battery_zcv();return gFG_voltage; // 返回 hw ocv 电压 /* 更新电池状态 */BMT_status.ICharging = mt_battery_average_method(&batteryCurrentBuffer[0],ICharging, &icharging_sum, batteryIndex); BMT_status.bat_vol = mt_battery_average_method(&batteryVoltageBuffer[0],bat_vol, &bat_sum, batteryIndex);BMT_status.temperature = mt_battery_average_method(&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(BMT_status.charger_exist == KAL_FALSE){if(BMT_status.SOC > previous_SOC && previous_SOC >= 0)BMT_status.SOC = previous_SOC;}previous_SOC = BMT_status.SOC;batteryIndex++;if (batteryIndex >= BATTERY_AVERAGE_SIZE)batteryIndex = 0;battery_xlog_printk(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);    }// 3. 电池温度保护/* 电池温度检查,如果温度超过 60 度,关机重启 */mt_battery_thermal_check();static void mt_battery_thermal_check(void){if( (g_battery_thermal_throttling_flag==1) || (g_battery_thermal_throttling_flag==3) ){if(battery_cmd_thermal_test_mode == 1){BMT_status.temperature = battery_cmd_thermal_test_mode_value;battery_xlog_printk(BAT_LOG_FULL, "[Battery] In thermal_test_mode , Tbat=%d\n", BMT_status.temperature);}#if defined(MTK_JEITA_STANDARD_SUPPORT)//ignore default rule#else    if(BMT_status.temperature >= 60){#if defined(CONFIG_POWER_EXT)battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] CONFIG_POWER_EXT, no update battery update power down.\n");#else{if( (g_platform_boot_mode==META_BOOT) || (g_platform_boot_mode==ADVMETA_BOOT) || (g_platform_boot_mode==ATE_FACTORY_BOOT) ){battery_xlog_printk(BAT_LOG_FULL, "[BATTERY] boot mode = %d, bypass temperature check\n", g_platform_boot_mode);}else// 正常启动的话,超过温度,系统重启{struct battery_data *bat_data = &battery_main;struct power_supply *bat_psy = &bat_data->psy;battery_xlog_printk(BAT_LOG_CRTI, "[Battery] Tbat(%d)>=60, system need power down.\n", BMT_status.temperature);bat_data->BAT_CAPACITY = 0;power_supply_changed(bat_psy); if( BMT_status.charger_exist == KAL_TRUE ){// can not power down due to charger exist, so need reset system//battery_charging_control(CHARGING_CMD_SET_PLATFORM_RESET,NULL);}//avoid SW no feedbackbattery_charging_control(CHARGING_CMD_SET_POWER_OFF,NULL);static kal_uint32 charging_set_power_off(void *data){kal_uint32 status = STATUS_OK;battery_xlog_printk(BAT_LOG_CRTI, "charging_set_power_off=%d\n");/* */mt_power_off();void mt_power_off(void){printk("mt_power_off\n");/* pull PWRBB low */rtc_bbpu_power_down();void rtc_bbpu_power_down(void){unsigned long flags;spin_lock_irqsave(&rtc_lock, flags);hal_rtc_bbpu_pwdn();spin_unlock_irqrestore(&rtc_lock, flags);}while (1) {#if defined(CONFIG_POWER_EXT)//EVBprintk("EVB without charger\n");#else   //Phone printk("Phone with charger\n");if (pmic_chrdet_status() == KAL_TRUE)arch_reset(0, "power_off_with_charger");#endif}}return status;}//mt_power_off();}}#endif}#endif}}/// 4. 电池状态检查/*  对电池状态进行检查,如果有问题,则会调用 printk() 进行打印 */mt_battery_notify_check();void mt_battery_notify_check(void){g_BatteryNotifyCode = 0x0000;if(g_BN_TestMode == 0x0000) /* for normal case */{battery_xlog_printk(BAT_LOG_FULL, "[BATTERY] mt_battery_notify_check\n");mt_battery_notify_VCharger_check();mt_battery_notify_VBatTemp_check();mt_battery_notify_ICharging_check();mt_battery_notify_VBat_check();mt_battery_notify_TatalChargingTime_check();}   else  /* for UI test */{mt_battery_notify_UI_test();}}//// 5. 调用具本的硬件相关函数进行充电,充电时会进行 CC/CV 之类的状态机切换就是在这里进行的/* 如果存在充电线,则调用具体充电芯片相关的函数进行充电  */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();void mt_battery_charging_algorithm(){switch(BMT_status.bat_charging_state){            case CHR_PRE :BAT_PreChargeModeAction();break;    case CHR_CC :BAT_ConstantCurrentModeAction();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;                }    }}///// 6. 更新电池显示状态 /* 更新设置节点的内容: /sys/class/power_supply/下的文件夹wireless_mainbattery_mainac_main usb_main*/mt_battery_update_status();static void mt_battery_update_status(void){#if defined(CONFIG_POWER_EXT)battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] CONFIG_POWER_EXT, no update Android.\n");#else{wireless_update(&wireless_main);battery_update(&battery_main);          ac_update(&ac_main);usb_update(&usb_main);}#endif  }}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;/* 每 10s 启动一次 */hrtimer_start(&battery_kthread_timer, ktime, HRTIMER_MODE_REL);   ktime = ktime_set(BAT_TASK_PERIOD, 0);  // 10s, 10* 1000 ms/* 如果有充电线插入且 xxx */if( chr_wake_up_bat == KAL_TRUE && g_smartbook_update != 1)  // for charger plug in/ out{g_smartbook_update = 0;/* 重新计算当前电池电量,复位 oam 算法相关参数  */battery_meter_reset();kal_int32 battery_meter_reset(void){#if defined(CONFIG_POWER_EXT)return 0;#else/* 获得 ui 显示的百分比 */kal_uint32 ui_percentage = bat_get_ui_percentage();kal_uint32 bat_get_ui_percentage(void){//  for plugging out charger in recharge phase, using SOC as UI_SOCif(chr_wake_up_bat == KAL_TRUE)return BMT_status.SOC; elsereturn BMT_status.UI_SOC;//    typedef struct //    {//        kal_bool            bat_exist;//        kal_bool            bat_full;                 // 电池是否充满标志//        INT32           bat_charging_state;//        UINT32          bat_vol;         //        kal_bool            bat_in_recharging_state;    //        kal_uint32      Vsense;//        kal_bool            charger_exist;   //        UINT32          charger_vol;        //        INT32           charger_protect_status; //        INT32           ICharging;//        INT32           IBattery;//        INT32           temperature;//        INT32           temperatureR;//        INT32           temperatureV;//        UINT32          total_charging_time;//        UINT32          PRE_charging_time;//        UINT32          CC_charging_time;//        UINT32          TOPOFF_charging_time;//        UINT32          POSTFULL_charging_time;//        UINT32          charger_type;//        INT32           SOC;//        INT32           UI_SOC;                       // ui 显示电量百分比//        UINT32          nPercent_ZCV;//        UINT32          nPrecent_UI_SOC_check_point;//        UINT32          ZCV;//    } PMU_ChargerStruct;}/*  // 如果电池充满了,则更新电池最大容量  */if(bat_is_charging_full() == KAL_TRUE) // charge full// 判断电池是否充满kal_bool bat_is_charging_full(void){if((BMT_status.bat_full == KAL_TRUE) && (BMT_status.bat_in_recharging_state == KAL_FALSE))return KAL_TRUE;elsereturn KAL_FALSE;}{/* 如果 fg_qmax_update_for_aging_flag == 1 则更新电池电容量,并将 fg_qmax_update_for_aging_flag 置为 0 */if(fg_qmax_update_for_aging_flag == 1){fg_qmax_update_for_aging();void fg_qmax_update_for_aging(void){#if defined(CONFIG_POWER_EXT)#elsekal_bool hw_charging_done = bat_is_charging_full();/* 如果电池充满了,更新电池最大容量 */if(hw_charging_done == KAL_TRUE) // charging full, g_HW_Charging_Done == 1{/* gFG_DOD0 应该是 100%-ui 显示百分比 即当前使用完的容量百分比 */if(gFG_DOD0 > 85){// 表示在放电if(gFG_columb < 0) // gFG_columb: 从上一次运行本函数到现在当前电量变化的值gFG_columb = gFG_columb - gFG_columb*2;     //  absolute valuegFG_BATT_CAPACITY_aging = ( ( (gFG_columb*1000)+(5*gFG_DOD0) ) / gFG_DOD0 ) / 10; // gFG_BATT_CAPACITY_aging: 当前温度电池最大容量// tuninggFG_BATT_CAPACITY_aging = (gFG_BATT_CAPACITY_aging * 100) / AGING_TUNING_VALUE;/* 如果当前电池容量值为 0 */if(gFG_BATT_CAPACITY_aging == 0){/* 先通过 battery_meter_get_battery_temperature() 获得电池温度,再通过 fgauge_get_Q_max() 计算电量 这里获得当前电池的容量 */gFG_BATT_CAPACITY_aging = fgauge_get_Q_max(battery_meter_get_battery_temperature()); bm_print(BM_LOG_CRTI, "[fg_qmax_update_for_aging] error, restore gFG_BATT_CAPACITY_aging (%d)\n", gFG_BATT_CAPACITY_aging);}bm_print(BM_LOG_CRTI, "[fg_qmax_update_for_aging] need update : gFG_columb=%d, gFG_DOD0=%d, new_qmax=%d\r\n", gFG_columb, gFG_DOD0, gFG_BATT_CAPACITY_aging);}else{bm_print(BM_LOG_CRTI, "[fg_qmax_update_for_aging] no update : gFG_columb=%d, gFG_DOD0=%d, new_qmax=%d\r\n", gFG_columb, gFG_DOD0, gFG_BATT_CAPACITY_aging);}}/* 如果电池未充满 */else{bm_print(BM_LOG_CRTI, "[fg_qmax_update_for_aging] hw_charging_done=%d\r\n", hw_charging_done);}#endif    }fg_qmax_update_for_aging_flag=0;}}   /* car: 库伦计的缩写这里是复位库伦计 */reset_parameter_car();void reset_parameter_car(void){#if defined(SOC_BY_HW_FG)int ret = 0;/* 调用对应充电芯片的操作函数,复位硬件,这里对应 PMIC 的函数为  */ret = battery_meter_ctrl(BATTERY_METER_CMD_HW_RESET, NULL);    static kal_int32 fgauge_hw_reset//(void *data){return STATUS_OK;}gFG_columb = 0;#endif#if defined(SOC_BY_SW_FG)oam_car_1 = 0;oam_car_2 = 0;gFG_columb = 0;#endif}/* DOD: DOD: 放电深度,100-DOD 即电容容量 */reset_parameter_dod_full(ui_percentage); void reset_parameter_dod_full(kal_uint32 ui_percentage){#if defined(SOC_BY_HW_FG)bm_print(BM_LOG_CRTI, "[battery_meter_reset]1 DOD0=%d,DOD1=%d,ui=%d\n", gFG_DOD0, gFG_DOD1, ui_percentage);gFG_DOD0 = 100 - ui_percentage;gFG_DOD1 = gFG_DOD0;bm_print(BM_LOG_CRTI, "[battery_meter_reset]2 DOD0=%d,DOD1=%d,ui=%d\n", gFG_DOD0, gFG_DOD1, ui_percentage);#endif// 我们用的是这种,软件库伦积分#if defined(SOC_BY_SW_FG)bm_print(BM_LOG_CRTI, "[battery_meter_reset]1 oam_d0=%d,oam_d_5=%d,ui=%d\n", oam_d0, oam_d_5, ui_percentage);oam_d0 = 100 - ui_percentage;gFG_DOD0 = oam_d0;gFG_DOD1 = oam_d0;oam_d_1 = oam_d0;oam_d_2 = oam_d0;oam_d_3 = oam_d0;oam_d_4 = oam_d0;oam_d_5 = oam_d0;   // 相当于平滑处理过的 D3 bm_print(BM_LOG_CRTI, "[battery_meter_reset]2 oam_d0=%d,oam_d_5=%d,ui=%d\n", oam_d0, oam_d_5, ui_percentage);#endif}return 0;#endif}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;}battery_xlog_printk(BAT_LOG_CRTI, "[battery_probe] bat_thread_kthread Done\n");    // 电池过充保护相关检测与初始化,他 2s 检测一次charger_hv_detect_sw_workaround_init();void charger_hv_detect_sw_workaround_init(void){ktime_t ktime;ktime = ktime_set(0, BAT_MS_TO_NS(2000));hrtimer_init(&charger_hv_detect_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);charger_hv_detect_timer.function = charger_hv_detect_sw_workaround;    hrtimer_start(&charger_hv_detect_timer, ktime, HRTIMER_MODE_REL);// 这个函数是周期来检查电池电压是否超出限制,即过充保护,超出了就不能充电了 charger_hv_detect_thread = kthread_run(charger_hv_detect_sw_thread_handler, 0, "mtk charger_hv_detect_sw_workaround");// 这个函数是周期来检查电池电压是否超出限制,即过充保护,超出了就不能充电了 int charger_hv_detect_sw_thread_handler(void *unused){ktime_t ktime;kal_uint32 charging_enable;kal_uint32 hv_voltage = BATTERY_VOLT_07_000000_V;kal_bool hv_status; do{ktime = ktime_set(0, BAT_MS_TO_NS(2000));       if(chargin_hw_init_done)/* 高压检测,应该是电池超过这个电压时,就不可以充电了 */battery_charging_control(CHARGING_CMD_SET_HV_THRESHOLD,&hv_voltage);static kal_uint32 charging_set_hv_threshold(void *data){kal_uint32 status = STATUS_OK;kal_uint32 set_hv_voltage;kal_uint32 array_size;kal_uint16 register_value;kal_uint32 voltage = *(kal_uint32*)(data);array_size = GETARRAYNUM(VCDT_HV_VTH);set_hv_voltage = bmt_find_closest_level(VCDT_HV_VTH, array_size, voltage); register_value = charging_parameter_to_value(VCDT_HV_VTH, array_size ,set_hv_voltage);/* 设置 PMIC 的 CHR_CON1 */upmu_set_rg_vcdt_hv_vth(register_value);return status;}wait_event_interruptible(charger_hv_detect_waiter, (charger_hv_detect_flag == KAL_TRUE));/* 如果检测到充电器,则检测下电池是否存在 */if ((upmu_is_chr_det() == KAL_TRUE)){/* 检测电池是否存在 */check_battery_exist(); void check_battery_exist(void){#if defined(CONFIG_DIS_CHECK_BATTERY)battery_xlog_printk(BAT_LOG_CRTI, "[BATTERY] Disable check battery exist.\n");#elsekal_uint32 baton_count = 0;kal_uint32 charging_enable = KAL_FALSE;kal_uint32 battery_status;kal_uint32 i;for(i=0;i<3;i++){/* 检测电池是否存在,通过读取 PMIC 的 CHR_CON7  */battery_charging_control(CHARGING_CMD_GET_BATTERY_STATUS,&battery_status);static kal_uint32 charging_get_battery_status(void *data){kal_uint32 status = STATUS_OK;upmu_set_baton_tdet_en(1);upmu_set_rg_baton_en(1);*(kal_bool*)(data) = upmu_get_rgs_baton_undet();return status;}baton_count += battery_status;}if( baton_count >= 3){if( (g_platform_boot_mode==META_BOOT) || (g_platform_boot_mode==ADVMETA_BOOT) || (g_platform_boot_mode==ATE_FACTORY_BOOT) ){battery_xlog_printk(BAT_LOG_FULL, "[BATTERY] boot mode = %d, bypass battery check\n", g_platform_boot_mode);}else{battery_xlog_printk(BAT_LOG_FULL, "[BATTERY] Battery is not exist, power off FAN5405 and system (%d)\n", baton_count);//battery_charging_control(CHARGING_CMD_ENABLE,&charging_enable);//battery_charging_control(CHARGING_CMD_SET_PLATFORM_RESET,NULL);    }}    #endif}}charger_hv_detect_flag = KAL_FALSE;if(chargin_hw_init_done)/* 查看 PMIC 是否开启了高压保护? */battery_charging_control(CHARGING_CMD_GET_HV_STATUS,&hv_status);static kal_uint32 charging_get_hv_status(void *data){kal_uint32 status = STATUS_OK;*(kal_bool*)(data) = upmu_get_rgs_vcdt_hv_det();return status;}if(hv_status == KAL_TRUE){battery_xlog_printk(BAT_LOG_CRTI, "[charger_hv_detect_sw_thread_handler] charger hv\n");    charging_enable = KAL_FALSE;if(chargin_hw_init_done)battery_charging_control(CHARGING_CMD_ENABLE,&charging_enable);}else{battery_xlog_printk(BAT_LOG_FULL, "[charger_hv_detect_sw_thread_handler] upmu_chr_get_vcdt_hv_det() != 1\n");    }if(chargin_hw_init_done)battery_charging_control(CHARGING_CMD_RESET_WATCH_DOG_TIMER,NULL);hrtimer_start(&charger_hv_detect_timer, ktime, HRTIMER_MODE_REL);    } while (!kthread_should_stop());return 0;}if (IS_ERR(charger_hv_detect_thread)){battery_xlog_printk(BAT_LOG_FULL, "[%s]: failed to create charger_hv_detect_sw_workaround thread\n", __FUNCTION__);}battery_xlog_printk(BAT_LOG_CRTI, "charger_hv_detect_sw_workaround_init : done\n" );}/*LOG System Set*/init_proc_log();#endif   g_bat_init_flag = KAL_TRUE;return 0;}// 第二个调用的 probe
static int mt_batteryNotify_probe(struct platform_device *dev)
{int ret_device_file = 0;//struct proc_dir_entry *entry = NULL;struct proc_dir_entry *battery_dir = NULL;battery_xlog_printk(BAT_LOG_CRTI, "******** mt_batteryNotify_probe!! ********\n" );ret_device_file = device_create_file(&(dev->dev), &dev_attr_BatteryNotify);ret_device_file = device_create_file(&(dev->dev), &dev_attr_BN_TestMode);battery_dir = proc_mkdir("mtk_battery_cmd", NULL);if (!battery_dir){pr_err("[%s]: mkdir /proc/mtk_battery_cmd failed\n", __FUNCTION__);}else{#if 1proc_create("battery_cmd", S_IRUGO | S_IWUSR, battery_dir, &battery_cmd_proc_fops);battery_xlog_printk(BAT_LOG_CRTI, "proc_create battery_cmd_proc_fops\n");#elseentry = create_proc_entry("battery_cmd", S_IRUGO | S_IWUSR, battery_dir);if (entry){entry->read_proc = battery_cmd_read;entry->write_proc = battery_cmd_write;}#endif}battery_xlog_printk(BAT_LOG_CRTI, "******** mtk_battery_cmd!! ********\n" );    return 0;}// 电池测量模块初始化
module_init(battery_meter_init);
static int __init battery_meter_init(void)
{int ret;ret = platform_device_register(&battery_meter_device);struct platform_device battery_meter_device = {.name                = "battery_meter",.id                  = -1,};if (ret) {bm_print(BM_LOG_CRTI, "[battery_meter_driver] Unable to device register(%d)\n", ret);return ret;}ret = platform_driver_register(&battery_meter_driver);static struct platform_driver battery_meter_driver = {.probe        = battery_meter_probe,.remove       = battery_meter_remove,.shutdown     = battery_meter_shutdown,.suspend      = battery_meter_suspend,.resume       = battery_meter_resume,.driver       = {.name = "battery_meter",},};if (ret) {bm_print(BM_LOG_CRTI, "[battery_meter_driver] Unable to register driver (%d)\n", ret);return ret;}bm_print(BM_LOG_CRTI, "[battery_meter_driver] Initialization : DONE \n");return 0;}/
// 调用的 probe
// ============================================================ //
static int battery_meter_probe(struct platform_device *dev)
{int ret_device_file = 0;battery_meter_ctrl = bm_ctrl_cmd;bm_print(BM_LOG_CRTI, "[battery_meter_probe] probe\n");//select battery meter control methodbattery_meter_ctrl = bm_ctrl_cmd;//LOG System Setinit_proc_log_fg();//last_oam_run_time = rtc_read_hw_time(); getrawmonotonic(&last_oam_run_time);//Create File For FG UI DEBUGret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_Current);    ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_volt);ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_current);ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_zcv);ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_temp);ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_r);ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_car);ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_bat_qmax);ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_d0);ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_d1);ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_percentage);ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_percentage_fg);ret_device_file = device_create_file(&(dev->dev), &dev_attr_FG_g_fg_dbg_percentage_voltmode);return 0;
}

什么!还要了解?请看源码

地址:链接: http://pan.baidu.com/s/1kV3DKNX 密码: uiyb

MTK 充电逻辑总结相关推荐

  1. MTK充电温度保护机制

    问题点 发现低温箱关机状态下能充进去电.开机是正常的. 参考 Android 8.0 MTK平台 电池高低温提醒客制化 kernel充电温度保护机制 drivers/power/supply/medi ...

  2. MTK 充电基本流程

    1. trickle current(涓流充电)  VBAT<2.2V     T=5min   2.2V<VBAT<2.75V  (dead battery) 2.  pre_ch ...

  3. iphone 手机尺寸_iPhone是新的黑莓手机

    iphone 手机尺寸 Writer and philosopher George Santayana once said "Those who cannot remember the pa ...

  4. android 系统(34)--关机充电图标修改

    关机充电图标修改 关机充电图标修改 关机充电main函数 函数set_draw_anim_mode 函数pthread_mutex_init 函数bootlogo_init 函数alarm_contr ...

  5. MTK Fuel Gauge算法分析

    Battery 架构简析 MTK 平台 Battery 软件架构基本如上图所示. 具体过程: 硬件 ADC 读取 Battery 的各路信息:包括温度,电压等. MTK 开发的电量算法分析得到的数据. ...

  6. android关机充电流程、充电画面显示

    一.Android正常开机流程.关机充电流程 在写这篇文章之前我们先看两个流程:正常开机流程,关机充电系统启动流程 1.正常开机流程,按开机键. 可大致分成三部分 (1).OS_level:UBOOT ...

  7. charging hw bq25601充电驱动笔记

    charging hw bq25601充电驱动笔记 #include "../bq25601.h"/* ====================================== ...

  8. 高通SDX12:基于sgm4151x的充电IC代码架构

    基于高通SDX12平台的充电功能代码可分为LK阶段和Kernel阶段 一.LK阶段 通常,lk阶段的充电逻辑被用于实现关机充电功能 首先,在/bootable/bootloader/lk/kernel ...

  9. 充电宝全国产化电子元件推荐方案

    方案概述: 移动电源一般巾悝电芯或者干电池作为储电单元 , 而太阳能电池是多个太阳能电池片组装的组装件以实现光电转换.移动电源可以通过 USB电缆线使用在任何符合USB国际标准的设备, 其具有短路.过 ...

最新文章

  1. apicloud开发目标
  2. TCP分段与IP分片
  3. 制作npm插件vue-toast-m实例练习
  4. JZOJ 5424. 【NOIP2017提高A组集训10.25】凤凰院凶真
  5. java jce 授权_【Java加密】(一)JCE配置加密算法强度不受限授权的安装
  6. 西电计算机达标测试挂科保研,西电竞赛保研
  7. ZOJ Problem Set - 1009
  8. Python 绘图利器 —— ggplot
  9. mysql 自动复制_MySQL复制 自动监控脚本-阿里云开发者社区
  10. 简单的linux下docker的下载与安装
  11. windows7文本文档换成c语言,win10电脑新建文本文档默认编码是UTF-8怎么修改成ANSI编码...
  12. 泛泰A860 Andorid4.4.3 KTU84M (Omni) 图赏
  13. python列表获取最后一项_如何在Python中获取列表的最后一项?
  14. 失业七个月,面试六十家公司
  15. 蓝桥杯大赛——练习系统登录
  16. AndroidStudio:The number of method references in a .dex file cannot exceed 64K错误
  17. 本土回忆! 即使你走的再远,也不能忘了让你生长的老家文化~
  18. 兔子繁殖问题(C语言)
  19. oracle获取某年第一天和最后一天,Oracle取得本月、本年第一天和最后一天
  20. 阿里云之发送验证码(2)

热门文章

  1. 面试官:多线程硬核50问!能回答一半就让你过
  2. 深圳大数据培训:大数据开发之掌握Hive的静态分区与动态分区
  3. 三星手机直连电脑Samsung Flow
  4. 使用设备树给DM9000网卡_触摸屏指定中断
  5. ECMAScript 6 入门:字符串的新增方法
  6. xilinx 账户申请以及vivado 安装
  7. Flutter 小技巧之 3.7 性能优化background isolate
  8. 人与自然,《棕熊之王-下》
  9. 悦美向小琴:从女人到女BOSS
  10. 一文掌握Hibernate