Android系统充电系统介绍-预防手机充电爆炸

1、锂电池介绍

锂离子电池由日本索尼公司于1990年最先开发成功。它是把锂离子嵌入碳(石油焦炭和石墨)中形成负极(传统锂电池用锂或锂合金作负极)。正极材料常用LixCoO2 ,也用 LixNiO2 ,和LixMnO4 ,电解液用LiPF6+二乙烯碳酸酯(EC)+二甲基碳酸酯(DMC)。

石油焦炭和石墨作负极材料无毒,且资源充足,锂离子嵌入碳中,克服了锂的高活性,解决了传统锂电池存在的安全问题,正极LixCoO2在充、放电性能和寿命上均能达到较高水平,使成本降低,总之锂离子电池的综合性能提高了。预计21世纪锂离子电池将会占有很大的市场。

锂离子二次电池充、放电时的反应式为LiCoO2+C=Li1-xCoO2+LixC 

Android系统充电系统介绍-预防手机充电爆炸_第1张图片

上图是锂电池容量和电压的对比图,可以看到当容量为0%时,电压最大,容量为100%时,电压最小,容量指的是电池还可以装下多少电量的意思。

过放:锂电池如果电压低于一定的门限,是不能够还原的,就假设,你有一个手机,放着几个月一直没有充电,如果电池电芯没有过放保护,那你的手机想再次充电就不行了。

过充:电池爆炸,大部分因为过充引起的,电芯做的不好,锂电池已经充满电了,没有做好门限保护,导致爆炸,但是爆炸的原因不只是这个,比如充电器短路,手机内部短路。

Android系统充电系统介绍-预防手机充电爆炸_第2张图片

2、Android电池管理框架

问题:

有时候我们发现我电量50%掉到30%用了一个小时,但是同样的使用方法20%到关机,可能只用了半个小时,这就是涉及电池曲线或者电量器的问题

电池曲线:
有点低成本手机,或者平板电脑,没有电量器,就只能用ADC的值通过自己的算法来调整电池百分比,如果这个电池曲线调整的不好,就会出现上述问题。

电量器:
电量器也是用来计算电量的,但是有个芯片专门做这个事情,理论肯定比上面没有电量器的效果好

Android电池整体框架

Android系统充电系统介绍-预防手机充电爆炸_第3张图片

2.1. Kernel 层

本层属于电池的驱动部分,负责与硬件进行交互,当电池电量信息发生变化时,生成相应的uevent,上报给用户层。

主要相关代码路径:

2.2. Healthd守护进程

本层在Android中属于Native层,healthd中运行一个系统服务batteryproperties,负责监听Kernel中上报的uevent,对电池电量进行实时监控。

主要相关代码路径:

Android系统充电系统介绍-预防手机充电爆炸_第4张图片

2.3. BatteryService系统服务

本层提供了C++/Java两套接口来访问batteryproperties系统服务。 
本层的系统服务battery使用Java代码写成,运行在fwk的中SystemServer进程。 
该系统服务的主要作用是:监听batteryproperties服务中的电池信息变化消息,并将该消息以系统广播的形式转发至Android系统中各处。

主要相关代码路径:

\frameworks\native\services\batteryservice\IBatteryPropertiesRegistrar.cpp
\frameworks\native\services\batteryservice\IBatteryPropertiesListener.cpp
\frameworks\native\services\batteryservice\BatteryProperties.cpp
\frameworks\base\core\java\android\os\IBatteryPropertiesRegistrar.aidl
\frameworks\base\core\java\android\os\IBatteryPropertiesListener.aidl
\frameworks\base\core\java\android\os\BatteryProperties.java
\frameworks\base\services\core\java\com\android\server\BatteryService.java

2.4. SystemUI 应用

该部分属于电量上报的最后的环节。其主要工作是:监听系统广播Intent.ACTION_BATTERY_CHANGED,并对UI作出相应更新。

主要相关代码路径

\frameworks\base\packages\SystemUI\src\com\android\systemui\power\PowerUI.java

3、u-boot到kernel关机充电流程

Android充电有很多场景,关机充电是比较重要的一个需要了解的。

开机流程:

Android系统充电系统介绍-预防手机充电爆炸_第5张图片

充电检测开机流程:

Android系统充电系统介绍-预防手机充电爆炸_第6张图片

u-boot代码:

在u-boot里面,我们很多时候需要把一些信息传给kernel,目前用到的方法是command_line,

开机方式也是这样的。u-boot代码如下代码如下,kernel解析部分代码请到init/main.c下面去找

以后抽个文章专门说明下

510 #ifdef CONFIG_RK_SDCARD_BOOT_EN
511     if (StorageSDCardUpdateMode()) { /* sdcard undate */
512         snprintf(command_line, sizeof(command_line),
513                 "%s %s", command_line, "sdfwupdate");
514     }
515 #endif
516 
517 #ifdef CONFIG_RK_UMS_BOOT_EN
518     if (StorageUMSUpdateMode()) { /* ums update */
519         snprintf(command_line, sizeof(command_line),
520                 "%s %s", command_line, "usbfwupdate");
521     }
522 #endif
523 
524 #ifdef CONFIG_POWER_RK818
525     if (is_rk81x_fg_init() != 0) {
526         snprintf(command_line, sizeof(command_line),
527                 "%s %s", command_line, "loader_charged");
528     }
529 #endif
530     if (charge) {
531         snprintf(command_line, sizeof(command_line),
532                 "%s %s", command_line, "androidboot.mode=charger");
533     }
534 
535 #if defined(CONFIG_LCD) && defined(CONFIG_RK_FB_DDREND)
536     /*
537      * uboot fb commandline: uboot_logo=@
[:] 538      * size - fb size, address - fb address, offset - kernel bmp logo offset. 539      * offset is optional, depend on resource image has kernel_logo.bmp. 540      */ 541     if (g_logo_on_state != 0) { 542         snprintf(command_line, sizeof(command_line), 543                 "%s uboot_logo=0x%08x@0x%08lx", command_line, CONFIG_RK_LCD_SIZE, gd->fb_base); 544 #if defined(CONFIG_KERNEL_LOGO) 545         if (g_rk_fb_size != -1) 546             snprintf(command_line, sizeof(command_line), 547                     "%s:0x%08x", command_line, g_rk_fb_size); 548 #endif /* CONFIG_KERNEL_LOGO */ 549     } 550 #endif /* CONFIG_RK_FB_DDREND */ 551  552 #if defined(CONFIG_RK_DEVICEINFO) 553     if (g_is_devinfo_load) 554         snprintf(command_line, sizeof(command_line), 555              "%s stb_devinfo=0x%08x@0x%08x", 556              command_line, SZ_8K, CONFIG_RKHDMI_PARAM_ADDR); 557 #endif /* CONFIG_RK_DEVICEINFO*/ 558  559     snprintf(command_line, sizeof(command_line), "./common/cmd_bootrk.c" 709L, 19309C        

4、充电电流

电池充电有几个阶段

Android系统充电系统介绍-预防手机充电爆炸_第7张图片

在软件上需要根据电池厂家的的不同阶段来给设置充电电流大小。

举个栗子:

我们用USB先连接PC机给手机充电,这时候适配器不是DC模式,充电电流如果设置过大,就会导致PC蓝屏。

而不同的电源适配器,D+ D- 的状态不同,被识别的状态也不一样,流程也会不同。

之前做的一个功能是,在恒压充电下,为了提高充电速度,我每间隔50ma提高充电电流,同时去检查电池两端的电压大小,如果电压降低到一定程度,就不会再增加充电电流。

5、kernel充电曲线代码

上面提到的问题,如果没有电量器的情况下,我们需要用数组来计算电池百分比,贴上这部分代码给大家看看,这部分代码可以适用于很多地方。

static struct batt_vol_cal  batt_table[BATT_NUM] = {
	{3400,3520},
	{3610,3715},
	{3672,3790},
	{3705,3825},
	{3734,3841},
	{3764,3864},
	{3808,3930},
	{3845,3997},
	{3964,4047},
	{4034,4144},
	{4120,4200},
};
static int rk29_adc_battery_voltage_to_capacity(struct rk29_adc_battery_data *bat, int BatVoltage)
{
    int i = 0;
    int capacity = 0;

    struct batt_vol_cal *p;
    p = batt_table;

    if (rk29_adc_battery_get_charge_level(bat)){  //charge
        if(BatVoltage >= (p[BATT_NUM - 1].charge_vol)){
            capacity = 100;
        }   
        else{
            if(BatVoltage <= (p[0].charge_vol)){
                capacity = 0;
            }
            else{
                for(i = 0; i < BATT_NUM - 1; i++){

                    if(((p[i].charge_vol) <= BatVoltage) && (BatVoltage < (p[i+1].charge_vol))){
                        capacity =  i * 10 + ((BatVoltage - p[i].charge_vol) * 10) / (p[i+1].charge_vol- p[i].charge_vol);
                        break;
                    }
                }
            }  
        }

    }
    else{  //discharge
        if(BatVoltage >= (p[BATT_NUM - 1].dis_charge_vol)){
            capacity = 100;
        }   
        else{
            if(BatVoltage <= (p[0].dis_charge_vol)){
                capacity = 0;
            }
            else{
                for(i = 0; i < BATT_NUM - 1; i++){
                    if(((p[i].dis_charge_vol) <= BatVoltage) && (BatVoltage < (p[i+1].dis_charge_vol))){
                        capacity =   i * 10 + ((BatVoltage - p[i].dis_charge_vol) * 10) / (p[i+1].dis_charge_vol- p[i].dis_charge_vol); ;
                        break;
                    }
                }
            }  

        }


    }
    return capacity;
}

static void rk29_adc_battery_capacity_samples(struct rk29_adc_battery_data *bat)
{
    int capacity = 0;
    struct rk29_adc_battery_platform_data *pdata = bat->pdata;

    //充放电状态变化后,Buffer填满之前,不更新
    if (bat->bat_status_cnt < NUM_VOLTAGE_SAMPLE)  {
        bat->gBatCapacityDisChargeCnt = 0;
        bat->gBatCapacityChargeCnt    = 0;
        return;
    }

    capacity = rk29_adc_battery_voltage_to_capacity(bat, bat->bat_voltage);

    if (rk29_adc_battery_get_charge_level(bat)){
        if (capacity > bat->bat_capacity){
            //实际采样到的容量比显示的容量大,逐级上升
            if (++(bat->gBatCapacityDisChargeCnt) >= NUM_CHARGE_MIN_SAMPLE){
                bat->gBatCapacityDisChargeCnt  = 0;
                if (bat->bat_capacity < 99){
                    bat->bat_capacity++;
                    bat->bat_change  = 1;
                }
            }
            bat->gBatCapacityChargeCnt = 0;
        }
        else{  //   实际的容量比采样比 显示的容量小
                    bat->gBatCapacityDisChargeCnt = 0;
                    (bat->gBatCapacityChargeCnt)++;

            if (pdata->charge_ok_pin != INVALID_GPIO){
                if (gpio_get_value(pdata->charge_ok_pin) == pdata->charge_ok_level){
                //检测到电池充满标志,同时长时间内充电电压无变化,开始启动计时充电,快速上升容量
                    if (bat->gBatCapacityChargeCnt >= NUM_CHARGE_MIN_SAMPLE){
                        bat->gBatCapacityChargeCnt = 0;
                        if (bat->bat_capacity < 99){
                            bat->bat_capacity++;
                            bat->bat_change  = 1;
                        }
                    }
                }
                else{
#if 0                    
                    if (capacity > capacitytmp){
                    //过程中如果电压有增长,定时器复位,防止定时器模拟充电比实际充电快
                        gBatCapacityChargeCnt = 0;
                    }
                    else if (/*bat->bat_capacity >= 85) &&*/ (gBatCapacityChargeCnt > NUM_CHARGE_MAX_SAMPLE)){
                        gBatCapacityChargeCnt = (NUM_CHARGE_MAX_SAMPLE - NUM_CHARGE_MID_SAMPLE);

                        if (bat->bat_capacity < 99){
                        bat->bat_capacity++;
                        bat->bat_change  = 1;
                        }
                    }
                }
#else            //  防止电池老化后出现冲不满的情况,
                    if (capacity > bat->capacitytmp){
                    //过程中如果电压有增长,定时器复位,防止定时器模拟充电比实际充电快
                        bat->gBatCapacityChargeCnt = 0;
                    }
                    else{

                        if ((bat->bat_capacity >= 85) &&((bat->gBatCapacityChargeCnt) > NUM_CHARGE_MAX_SAMPLE)){
                            bat->gBatCapacityChargeCnt = (NUM_CHARGE_MAX_SAMPLE - NUM_CHARGE_MID_SAMPLE);

                            if (bat->bat_capacity < 99){
                                bat->bat_capacity++;
                                bat->bat_change  = 1;
                            }
                        }
                    }
                }
#endif

            }
            else{
            //没有充电满检测脚,长时间内电压无变化,定时器模拟充电
                if (capacity > bat->capacitytmp){
                //过程中如果电压有增长,定时器复位,防止定时器模拟充电比实际充电快
                    bat->gBatCapacityChargeCnt = 0;
                }
                else{

                    if ((bat->bat_capacity >= 85) &&(bat->gBatCapacityChargeCnt > NUM_CHARGE_MAX_SAMPLE)){
                        bat->gBatCapacityChargeCnt = (NUM_CHARGE_MAX_SAMPLE - NUM_CHARGE_MID_SAMPLE);

                        if (bat->bat_capacity < 99){
                            bat->bat_capacity++;
                            bat->bat_change  = 1;
                        }
                    }
                }


            }            
        }
    }    
    else{   
    //放电时,只允许电压下降
        if (capacity < bat->bat_capacity){
            if (++(bat->gBatCapacityDisChargeCnt) >= NUM_DISCHARGE_MIN_SAMPLE){
                bat->gBatCapacityDisChargeCnt = 0;
                if (bat->bat_capacity > 0){
                    bat->bat_capacity-- ;
                    bat->bat_change  = 1;
                }
            }
        }
        else{
            bat->gBatCapacityDisChargeCnt = 0;
        }
        bat->gBatCapacityChargeCnt = 0;
    }
        bat->capacitytmp = capacity;
}   

好了今天就这么多,具体问题还是要具体分析去看代码

推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

嵌入式Linux

微信扫描二维码,关注我的公众号

你可能感兴趣的:(Android系统充电系统介绍-预防手机充电爆炸)