zoukankan      html  css  js  c++  java
  • 【转】android 电池(二):android关机充电流程、充电画面显示

    关键词:android 电池关机充电 androidboot.mode charger关机充电 充电画面显示
    平台信息:
    内核:linux2.6/linux3.0
    系统:android/android4.0 
    平台:S5PV310(samsungexynos 4210) 

    作者:xubin341719(欢迎转载,请注明作者)

    欢迎指正错误,共同学习、共同进步!!

    android 电池(一):锂电池基本原理篇

    android 电池(二):android关机充电流程、充电画面显示

    android 电池(三):android电池系统

    android电池(四):电池 电量计(MAX17040)驱动分析篇

    android电池(五):电池 充电IC(PM2301)驱动分析篇

    上一篇我们讲了锂电池的充放电的流程和电池的一些特性,这一节我们重点说一下android关机充电是怎么、充电画面显示是怎么实现的,这个在工作中也比较有用,我们开始做这一块的时候也走了不少的弯路。我记得我们做adnroid2.3的时候,关机状态和充电logo显示是在uboot中做的。应该是有两种做法,回头我再看下uboot中做画面显示那一块是怎么做的,这一节我们重点说系统中的充电logo显示。

    一、android正常开机流程、关机充电流程

    在写这篇文章之前我们先看两个流程:正常开机流程,关机充电系统启动流程

    1、正常开机流程,按开机键。

    可大致分成三部分

    (1)、OS_level:UBOOT、kenrel、init这三步完成系统启动;

    (2)、Android_level:这部分完成android部的初始化;

    (3)、Home Screen:这部分就是我们看到的launcher部分。

    2、关机充电系统启动流程

           与前面相比,这个流程只走到init这一部分,就没有往后走了,这部分我们会在后面的代码中分析。


    二、关机充电逻辑硬件逻辑

    1、插入DC,charger IC从硬件上唤醒系统,相当于长按开机键开机。


    下面这部分是charger IC连接系统的控制部分。


    三、软件逻辑。

    DC插入,其实相当于关机状态下“按开机键”开机。第一步要走UBOOT、kernel 、android init这一流程。

    1、UBOOT

           UBOOT启动代码我们不在这里详细分析,这里我们只要注意二个问题:

    a:如何判断是DC插入;

    b:设定setenv("bootargs", "androidboot.mode=charger"),androidboot.mode这个参数相当重要,这个参数决定系统是正常启动、还是关机充电状态。

    Uboot/board/samsung/smdk4212/smkd4212.c

    int board_late_init (void)
    {
        int keystate = 0;
        printf("check start mode
    ");
      if ((*(int *)0x10020800==0x19721212) || (*(int *)0x10020804==0x19721212)
    || (*(int *)0x10020808==0x19721212)) //(1)、检查是否有DC插入;
    {
        setenv ("bootargs", "");//(2)、没有DC插入;
      } else  {//DC插入
            int tmp=*(int *)0x11000c08;
        *(int *)0x10020800=*(int *)0x10020804=0x19721212;
        *(int *)0x11000c08=(tmp&(~0xc000))|0xc000;
        udelay(10000);
        if ((*(int *)0x11000c04 & 0x80)!=0x80 && INF_REG4_REG != 0xf) {
            setenv ("bootargs", "androidboot.mode=charger");//(3)、设定bootargs为charger状态
            printf("charger mode
    ");
        } else {
            setenv ("bootargs", "");
        }
        *(int *)0x11000c08=tmp;
      }
    #ifdef CONFIG_CPU_EXYNOS4X12
        int charge_status=CheckBatteryLow();//(4)、检查电池电量;
        keystate=board_key_check();//(5)、检查按键状态;
        // fuse bootloader
        if(second_boot_info != 0) {
            boot_symbol=1;
            INF_REG2_REG =0x8;
            run_command(CONFIG_BOOTCMD_FUSE_BOOTLOADER, NULL);
        }
        if((INF_REG4_REG == 0xd)) {
            // reboot default
            char buf[10];
            sprintf(buf, "%d", CONFIG_BOOTDELAY);
            setenv ("bootdelay", buf);
            setenv ("reserved", NULL);
            saveenv();
        } else if((INF_REG4_REG == 0xe) || keystate == (0x1 | 0x2)) {//(6)、按键进入fastboot模式;
            // reboot bootloader
            boot_symbol=1;
            INF_REG2_REG =0x8;
            printf("BOOTLOADER - FASTBOOT
    ");
            setenv ("reserved", "fastboot");
            setenv ("bootdelay", "0");
        } else if((INF_REG4_REG == 0xf) || keystate == (0x1 | 0x2 | 0x4)) {//(7)、按键进入recovery模式;
            // reboot recovery
            printf("BOOTLOADER - RECOVERY
    ");
            boot_symbol=1;
            INF_REG2_REG =0x8;
            setenv ("reserved", CONFIG_BOOTCMD_RECOVERY);
            setenv ("bootdelay", "0");
        } else
        if(keystate == (0x1 | 0x4) || second_boot_info != 0 || partition_check()) {//(8)、按键进入卡升级模式;
            // 2nd boot
            printf("BOOTLOADER - 2ND BOOT DEVICE
    ");
            boot_symbol=1;
            INF_REG2_REG =0x8;
            setenv ("bootcmd", CONFIG_BOOTCOMMAND);
            setenv ("reserved", CONFIG_BOOTCMD_FUSE_RELEASE);
            setenv ("bootdelay", "0");
        } else {//(9)、正常启动;
            // normal case
            char buf[10];
            sprintf(buf, "%d", CONFIG_BOOTDELAY);
            setenv ("bootdelay", buf);
        }
        INF_REG4_REG = 0;
        return 0;
    }

    (1)、检查是否有DC插入;

      if ((*(int *)0x10020800==0x19721212) || (*(int *)0x10020804==0x19721212)|| (*(int *)0x10020808==0x19721212)) 

    这部分检查寄存器的值。

    (2)、没有DC插入;

    (3)、设定bootargs为charger状态

    if ((*(int *)0x11000c04 & 0x80)!=0x80 && INF_REG4_REG != 0xf) {
            setenv ("bootargs", "androidboot.mode=charger");

    这是这部分的重点,如果能过寄存器判断是DC插入,把androidboot.mode设定为charger状态。

    以下这部分根据需要加入,通过判断不同的情况进入不同的功能,如fastboot evovery…………,这部分不做详细解释。

    (4)、检查电池电量;

        这个在正常开机状态下,如果检测电量太低,则不开机,这部分代码就不做分析。

    (5)、检查按键状态;

          我们这个平台有几种模式:fastboot ecovery卡升级等……

    (6)、按键进入fastboot模式;

    (7)、按键进入recovery模式;

    (8)、按键进入卡升级模式

    (9)、正常启动;

    2、kernel

    这部分和正常启动是一样的。

    3、init

    前面所有的描述其实只有一点和正常启动不太一样,那就是在UBOOT中把androidboot.mode设定为charger状态,内核正常流程启动,然后到init时要对charger这种状态处理。

    systemcoreinitinit.c

    int main(int argc, char **argv)
    {
        ………………
        action_for_each_trigger("early-init", action_add_queue_tail);
    
        queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
        queue_builtin_action(property_init_action, "property_init");
        queue_builtin_action(keychord_init_action, "keychord_init");
        queue_builtin_action(console_init_action, "console_init");  //(1)、显示initlogo.rle,也就是android第二张图片;
        queue_builtin_action(set_init_properties_action, "set_init_properties");
    
        /* execute all the boot actions to get us started */
        action_for_each_trigger("init", action_add_queue_tail);
    
        /* skip mounting filesystems in charger mode */
        if (strcmp(bootmode, "charger") != 0) {//(2)、这里就是UBOOT中设定的bootmode,如果是charger模式,跳过下面初始化;
            action_for_each_trigger("early-fs", action_add_queue_tail);
            action_for_each_trigger("fs", action_add_queue_tail);
            action_for_each_trigger("post-fs", action_add_queue_tail);
            action_for_each_trigger("post-fs-data", action_add_queue_tail);
        }
    
        queue_builtin_action(property_service_init_action, "property_service_init");
        queue_builtin_action(signal_init_action, "signal_init");
        queue_builtin_action(check_startup_action, "check_startup");
    
        if (!strcmp(bootmode, "charger")) {//(3)、如果为charger,则调用charger.c。
            action_for_each_trigger("charger", action_add_queue_tail);
        } else {
            action_for_each_trigger("early-boot", action_add_queue_tail);
            action_for_each_trigger("boot", action_add_queue_tail);
        }
    ……………………
    }

    (1)、显示initlogo.rle,也就是android第二张图片;

    queue_builtin_action(console_init_action,"console_init");调用console_init_action

    static int console_init_action(int nargs, char **args)
    {
        int fd;
        char tmp[PROP_VALUE_MAX];
        if (console[0]) {
            snprintf(tmp, sizeof(tmp), "/dev/%s", console);
            console_name = strdup(tmp);
        }
        fd = open(console_name, O_RDWR);
        if (fd >= 0)
            have_console = 1;
        close(fd);
        if( load_565rle_image(INIT_IMAGE_FILE) ) {//这里定义rle文件的名称#define INIT_IMAGE_FILE    "/initlogo.rle"
            fd = open("/dev/tty0", O_WRONLY);
            if (fd >= 0) {//如果没有这张图片,就显示android字样,在屏幕左上角;
                const char *msg;
                    msg = "
    "
                "
    "
                "
    "  // console is 40 cols x 30 lines
                "
    "
                "
    "
                "
    "
                "
    "
                "
    "
                "
    "
                "
    "
                "             A N D R O I D ";
                write(fd, msg, strlen(msg));
                close(fd);
            }
        }
        return 0;
    }

    (2)、这里就是UBOOT中设定的bootmode,如果是charger模式,跳过下面初始化;

        /* skip mounting filesystems in charger mode */
        if (strcmp(bootmode, "charger") != 0) {
            action_for_each_trigger("early-fs", action_add_queue_tail);
            action_for_each_trigger("fs", action_add_queue_tail);
            action_for_each_trigger("post-fs", action_add_queue_tail);
            action_for_each_trigger("post-fs-data", action_add_queue_tail);
        }

    (3)、如果为charger,则调用charger.c

    action_for_each_trigger("charger", action_add_queue_tail);

    我们在后面细分charger这部分。

    4、charger.c

    这部分就是我们充电部分,充电画面显示的实现。

    systemcorechargercharger.c

    int main(int argc, char **argv)
    {
    ………………
        klog_set_level(CHARGER_KLOG_LEVEL);
        dump_last_kmsg();
        LOGI("--------------- STARTING CHARGER MODE ---------------
    ");
    
        gr_init();
        gr_font_size(&char_width, &char_height); //(1)、初始化graphics,包括buf大小;
    
        ev_init(input_callback, charger);//(2)初始化按键;
       
    fd = uevent_open_socket(64*1024, true);
        if (fd >= 0) {
            fcntl(fd, F_SETFL, O_NONBLOCK);
            ev_add_fd(fd, uevent_callback, charger);
        }
    
        charger->uevent_fd = fd;
        coldboot(charger, "/sys/class/power_supply", "add");//(3)、创建/sys/class/power_supply结点,把socket信息通知应用层;
        
    ret = res_create_surface("charger/battery_fail", &charger->surf_unknown);
        if (ret < 0) {
            LOGE("Cannot load image
    ");
            charger->surf_unknown = NULL;
        }
        for (i = 0; i < charger->batt_anim->num_frames; i++) {//(4)、这里是显示charger logo,res_create_surface显示图片函数;
            struct frame *frame = &charger->batt_anim->frames[i];
            ret = res_create_surface(frame->name, &frame->surface);
            if (ret < 0) {
                LOGE("Cannot load image %s
    ", frame->name);
                /* TODO: free the already allocated surfaces... */
                charger->batt_anim->num_frames = 0;
                charger->batt_anim->num_cycles = 1;
                break;
            }
        }
    ev_sync_key_state(set_key_callback, charger);
        gr_fb_blank(true);
    
        charger->next_screen_transition = now - 1;
        charger->next_key_check = -1;
        charger->next_pwr_check = -1;
        reset_animation(charger->batt_anim);
        kick_animation(charger->batt_anim);
        event_loop(charger);//(5)、event_loop循环,电池状态,检测按键是否按下;
        return 0;
    
    }

    (1)、初始化graphics,包括buf大小

    android/bootable/recovery/minui/graphics.c

    gr_init():minui/graphics.c[settty0 to graphic mode, open fb0],设制tty0为图形模式,打开fb0;

    int gr_init(void)
    {
        gglInit(&gr_context);
        GGLContext *gl = gr_context;
        gr_init_font();
        gr_vt_fd = open("/dev/tty0", O_RDWR | O_SYNC);
        if (gr_vt_fd < 0) {
            // This is non-fatal; post-Cupcake kernels don't have tty0.
            perror("can't open /dev/tty0");
    
        } else if (ioctl(gr_vt_fd, KDSETMODE, (void*) KD_GRAPHICS)) {
            // However, if we do open tty0, we expect the ioctl to work.
            perror("failed KDSETMODE to KD_GRAPHICS on tty0");
            gr_exit();
            return -1;
        }
        gr_fb_fd = get_framebuffer(gr_framebuffer);
        if (gr_fb_fd < 0) {
            gr_exit();
            return -1;
        }
        get_memory_surface(&gr_mem_surface);
        fprintf(stderr, "framebuffer: fd %d (%d x %d)
    ",
                gr_fb_fd, gr_framebuffer[0].width, gr_framebuffer[0].height);
            /* start with 0 as front (displayed) and 1 as back (drawing) */
        gr_active_fb = 0;
        set_active_framebuffer(0);
        gl->colorBuffer(gl, &gr_mem_surface);
        gl->activeTexture(gl, 0);
        gl->enable(gl, GGL_BLEND);
        gl->blendFunc(gl, GGL_SRC_ALPHA, GGL_ONE_MINUS_SRC_ALPHA);
        gr_fb_blank(true);
        gr_fb_blank(false);
        return 0;
    
    }

    (2)android/bootable/recovery/minui/events.c

    ev_init():minui/events.c[open /dev/input/event*]打开 /dev/input/event*

    这部分是在,充电状态下,按键操作的初始化,比如:短按显示充电logo,长按开机,初始化代码如下。

    int ev_init(ev_callback input_cb, void *data)
    {
        DIR *dir;
        struct dirent *de;
        int fd;
        dir = opendir("/dev/input");//打开驱动结点;
        if(dir != 0) {
            while((de = readdir(dir))) {
                unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];
    //            fprintf(stderr,"/dev/input/%s
    ", de->d_name);
                if(strncmp(de->d_name,"event",5)) continue;
                fd = openat(dirfd(dir), de->d_name, O_RDONLY);
                if(fd < 0) continue;
                /* read the evbits of the input device */
                if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) < 0) {
                    close(fd);
                    continue;
                }
                /* TODO: add ability to specify event masks. For now, just assume
                 * that only EV_KEY and EV_REL event types are ever needed. */
                if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits)) {
                    close(fd);
                    continue;
                }
                ev_fds[ev_count].fd = fd;
                ev_fds[ev_count].events = POLLIN;
                ev_fdinfo[ev_count].cb = input_cb;
                ev_fdinfo[ev_count].data = data;
                ev_count++;
                ev_dev_count++;
                if(ev_dev_count == MAX_DEVICES) break;
            }
        }
        return 0;
    }

    (3)、创建/sys/class/power_supply结点,把socket信息通知应用层

    uevent_open_socket这个函数是通过kobject_uevent的方式通知的应用层,就是往一个socket广播一个消息,只需要在应用层打开socket监听NETLINK_KOBJECT_UEVENT组的消息,就可以收到了,主要是创建了socket接口获得uevent的文件描述符,然后触发/sys/class/power_supply目录及其子目录下的uevent,然后接受并创建设备节点,至此设备节点才算创建。

    (4)、这里显示charger logo,res_create_surface显示图片函数;

    res_create_surface:minui/resource.c[create surfaces for all bitmaps used later, include icons, bmps]

    创建surface为所以的位图,包括图标、位图。  这些图片的位置为:systemcorechargerimages

    (5)、event_loop循环,电池状态,检测按键是否按下;

    5、event_loop

           这个函数判断按键状态,DC是否插拔。如果长按开机:执行android_reboot(ANDROID_RB_RESTART,0, 0);如果拔出DC:执行android_reboot(ANDROID_RB_POWEROFF,0, 0);

    static void event_loop(struct charger *charger)
    {
        int ret;
        while (true) {
            int64_t now = curr_time_ms();//(1)、获得当前时间;
            LOGV("[%lld] event_loop()
    ", now);
            handle_input_state(charger, now);//(2)、检查按键状态;
            handle_power_supply_state(charger, now);// (3)、检查DC是否拔出; 
            /* do screen update last in case any of the above want to start
             * screen transitions (animations, etc)
             */
            update_screen_state(charger, now);//(4)、对按键时间状态标志位的判断,显示不同电量的充电logo; 
            wait_next_event(charger, now);
        }
    }

    (1)、获得当前时间;

       int64_t now = curr_time_ms();

           这个时间来判断,有没有屏幕超时,如果超时关闭屏幕充电logo显示。

    (2)、检查按键状态;

    static void handle_input_state(struct charger *charger, int64_t now)
    {
        process_key(charger, KEY_POWER, now);
        if (charger->next_key_check != -1 && now > charger->next_key_check)
            charger->next_key_check = -1;
    }
    我们再看下:process_key(charger, KEY_POWER, now);
    static void process_key(struct charger *charger, int code, int64_t now)
    {
    ………………
        if (code == KEY_POWER) {
            if (key->down) {
                int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME;
                if (now >= reboot_timeout) {//如果长按power键,就重新启动,也就是重启开机;
                    LOGI("[%lld] rebooting
    ", now);
                    android_reboot(ANDROID_RB_RESTART, 0, 0);//重启命令;
                }
        ………………
        }
    
        key->pending = false;
    }

    (3)、检查DC是否拔出;

    handle_power_supply_state(charger, now); 

    static void handle_power_supply_state(struct charger *charger, int64_t now)
    {
        if (charger->num_supplies_online == 0) {
            if (charger->next_pwr_check == -1) {
                charger->next_pwr_check = now + UNPLUGGED_SHUTDOWN_TIME;
                LOGI("[%lld] device unplugged: shutting down in %lld (@ %lld)
    ",
                     now, UNPLUGGED_SHUTDOWN_TIME, charger->next_pwr_check);
            } else if (now >= charger->next_pwr_check) {
                LOGI("[%lld] shutting down
    ", now);
                android_reboot(ANDROID_RB_POWEROFF, 0, 0);//如果DC拔出,则关机;
            } 
    ………………
    }

    (4)、对按键时间状态标志位的判断,显示不同电量的充电logo;

      update_screen_state(charger, now);

    这个函数比较长了,其实做用就是:我们在状态的过程中,充电logo的电量是要增加的,比如电量是20%时,要从第一格开始闪烁;如果是80%时,则要从第三格开始闪烁,电量显示就是通过这个函数来计算实现的。

    static void update_screen_state(struct charger *charger, int64_t now)
    {
        struct animation *batt_anim = charger->batt_anim;
        int cur_frame;
        int disp_time;
    
        if (!batt_anim->run || now < charger->next_screen_transition)
            return;
    
        /* animation is over, blank screen and leave */
        if (batt_anim->cur_cycle == batt_anim->num_cycles) {
            reset_animation(batt_anim);
            charger->next_screen_transition = -1;
            gr_fb_blank(true);
            LOGV("[%lld] animation done
    ", now);
            return;
        }
    
        disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;
    
        /* animation starting, set up the animation */
        if (batt_anim->cur_frame == 0) {
            int batt_cap;
            int ret;
    
            LOGV("[%lld] animation starting
    ", now);
            batt_cap = get_battery_capacity(charger);
            if (batt_cap >= 0 && batt_anim->num_frames != 0) {
                int i;
    
                /* find first frame given current capacity */
                for (i = 1; i < batt_anim->num_frames; i++) {
                    if (batt_cap < batt_anim->frames[i].min_capacity)
                        break;
                }
                batt_anim->cur_frame = i - 1;
    
                /* show the first frame for twice as long */
                disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 2;
            }
    
            batt_anim->capacity = batt_cap;
        }
    
        /* unblank the screen  on first cycle */
        if (batt_anim->cur_cycle == 0)
            gr_fb_blank(false);
    
        /* draw the new frame (@ cur_frame) */
        redraw_screen(charger);
    
        /* if we don't have anim frames, we only have one image, so just bump
         * the cycle counter and exit
         */
        if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) {
            LOGV("[%lld] animation missing or unknown battery status
    ", now);
            charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
            batt_anim->cur_cycle++;
            return;
        }
    
        /* schedule next screen transition */
        charger->next_screen_transition = now + disp_time;
    
        /* advance frame cntr to the next valid frame
         * if necessary, advance cycle cntr, and reset frame cntr
         */
        batt_anim->cur_frame++;
    
        /* if the frame is used for level-only, that is only show it when it's
         * the current level, skip it during the animation.
         */
        while (batt_anim->cur_frame < batt_anim->num_frames &&
               batt_anim->frames[batt_anim->cur_frame].level_only)
            batt_anim->cur_frame++;
        if (batt_anim->cur_frame >= batt_anim->num_frames) {
            batt_anim->cur_cycle++;
            batt_anim->cur_frame = 0;
    
            /* don't reset the cycle counter, since we use that as a signal
             * in a test above to check if animation is over
             */
        }
    }

    下面是不能容量时显示logo的函数:

    static struct frame batt_anim_frames[] = {
        {
            .name = "charger/battery_0",
            .disp_time = 750,
            .min_capacity = 0,
        },
        {
            .name = "charger/battery_1",
            .disp_time = 750,
            .min_capacity = 20,
        },
        {
            .name = "charger/battery_2",
            .disp_time = 750,
            .min_capacity = 40,
        },
        {
            .name = "charger/battery_3",
            .disp_time = 750,
            .min_capacity = 60,
        },
        {
            .name = "charger/battery_4",
            .disp_time = 750,
            .min_capacity = 80,
            .level_only = true,
        },
        {
            .name = "charger/battery_5",
            .disp_time = 750,
            .min_capacity = BATTERY_FULL_THRESH,
        },
    };

  • 相关阅读:
    文件的复制
    反射基础知识
    蓝牙连接 返回的命令
    WebRoot 与 webContent的区别
    时间判断
    java ecplise配置
    异常org.xml.sax.SAXParseException; lineNumber: 5; columnNumber: 11; 注释中不允许出现字符串 "--"。的原因
    F、CSL 的神奇序列 【规律】 (“新智认知”杯上海高校程序设计竞赛暨第十七届上海大学程序设计春季联赛)
    E、CSL 的魔法 【模拟】 (“新智认知”杯上海高校程序设计竞赛暨第十七届上海大学程序设计春季联赛)
    D、CSL 的字符串 【栈+贪心】 (“新智认知”杯上海高校程序设计竞赛暨第十七届上海大学程序设计春季联赛)
  • 原文地址:https://www.cnblogs.com/cslunatic/p/3637842.html
Copyright © 2011-2022 走看看