展讯7731C_M Android6.0 充电指示灯实现(一)------关机充电实现

前言:

            在手机充电中常常使用充电指示灯来观察手机充电状态,比如说将手机插上USB线充电时指示灯会亮,如果拔出USB,指示灯会灭,在充电时候通常我们设置电池电量0~90%时,指示灯为红色,电量为90%~100%时候,显示为绿色。当然充电又分为开机充电和

关机充电,本文着重从关机充电模式讲解guide-led的实现机制

一、关机充电下,指示灯实现整体流程框架

        在关机下,插入USB充电,系统会上电启动内核,并且加载相关的服务(Linux 用户空间进程),其中就有关机充电服务/sytem/bin/charge,其中服务端的启动定义在开机初始化话文件init.rc中,如下:

service charge /bin/charge
    user root
    oneshot
其中charge程序由 vendor/sprd/open-source/apps/charge/charge.c实现。

实现流程框架图如下:


   该图可以分为两个部分,Linux user和kernel 层两个部分,charge 执行从创建charge线程开始到调用kernel set_brightness完成

对guide-led的控制。

二、关机充电下,指示灯实现具体流程

 ==========linux user process部分==========

1. 电池充电主程序入口

文件:vendor/sprd/open-source/apps/charge/charge.c

int
main(int argc, char **argv) {
    .....
    ret = pthread_create(&t_1, NULL, charge_thread, NULL);
    if(ret){
        LOGE("thread:charge_thread creat failed\n");
        return -1;
    }


    LOGD("all thread start\n");


    pthread_join(t_1, NULL);


    .....
    LOGD("charge app exit\n");


    return EXIT_SUCCESS;
}
在主程序中,创建charge_thread用来检测电池状态,然后根据充电电量的变化来控制充电指示灯

2. 充电线程的定义

文件: vendor/sprd/open-source/apps/charge/ui.c
#define WakeFileName  "/sys/power/wait_for_fb_wake"
void *charge_thread(void *cookie)
{
    .....
    for (;!is_exit;) {


        fd = open(WakeFileName, O_RDONLY, 0);
        if (fd < 0) {
        LOGD("Couldn't open file /sys/power/wait_for_fb_wake\n");
        return NULL;
        }
        do {
            err = read(fd, &buf, 1);
            LOGD("return from WakeFileName err: %d errno: %s\n", err, strerror(errno));
        } while (err < 0 && errno == EINTR);
        close(fd);
        bat_level = battery_capacity();
        update_progress_locked(bat_level);
        usleep(500000);
    }
    ......
    usleep(200);
    return NULL;
}
改线程中有一个循环体,其中battery_capacity用来获取电池容量,
将电池容量不断的传入update_progress_locked方法中

3.update_progress_locked方法的实现

static void update_progress_locked(int level)
{
    ......
    draw_progress_locked(level);  // Draw only the progress bar


}

在update_progress_locked中,又调用draw_progress_locked方法

4.draw_process_locked方法的实现

#define LED_GREEN         1
#define LED_RED           2
#define LED_BLUE          3
static void draw_progress_locked(int level)
{
    .....
    if(level > 100)
        level = 100; //处理电池电量的上限
    else if (level < 0)//处理电池电量的下限
        level = 0;  
    if(level < 90){
        if(led_flag!= LED_RED){
            led_on(LED_RED);
            led_flag = LED_RED;   //如果电池电量低于90亮绿灯
        }


    }else{
        if(led_flag!= LED_GREEN){ //如果电池电量90~100 亮红灯
            led_on(LED_GREEN);  //调用亮灯函数
            led_flag = LED_GREEN;
        }
    }
    .....
}

5. 亮灯函数led_on的实现

文件:vendor/sprd/open-source/apps/charge/backlight.c
void led_on(int color)
{
    if(color == 1){
        eng_led_green_test(max_green_led/2);
        eng_led_red_test(0);
        eng_led_blue_test(0);
    }else if(color == 2){
        eng_led_red_test(max_red_led/2);
        eng_led_green_test(0);
        eng_led_blue_test(0);
    }else if(color == 3){
        eng_led_blue_test(0);
        eng_led_red_test(max_green_led/2);
        eng_led_green_test(max_red_led/2);
    }else
        SPRD_DBG("%s: color is %d invalid\n",__func__,color);
}
在亮灯函数led_on 中, 通过传入的参数clor 来区分不能颜色灯,这里以绿灯为例

6. 亮绿灯函数eng_led_green_test的实现

static int eng_led_green_test(int brightness)
{
    int fd;
    int ret;
    char buffer[8];


    fd = open(LED_GREEN_DEV, O_RDWR); //打开绿灯设备节点


    if(fd < 0) {
        SPRD_DBG("%s: open %s fail",__func__, LED_GREEN_DEV);
        return -1;
    }


    memset(buffer, 0, sizeof(buffer));
    sprintf(buffer, "%d", brightness);
    ret = write(fd, buffer, strlen(buffer)); //向节点中写入数据brightness值


    close(fd);


    return 0;
}
亮绿灯函数eng_led_green_test的实现非常容易,就是向指定的节点中写入数据brightness值,而brightness值的范围为0~255 ,这个值直接决定了pwm输入的占空比,进而影响灯的亮度。查看设备节点定义如下:
#define LED_GREEN_DEV                   "/sys/class/leds/green/brightness"
#define LED_RED_DEV                     "/sys/class/leds/red/brightness"
#define LED_BLUE_DEV                    "/sys/class/leds/blue/brightness"

上面的节点分别对应为红,绿,蓝三色灯对应的控制节点

 ==========linux driver kernel 部分==========

    也就是说当我们调用write接口后,应用层点灯过程就已经结束了,接下来write会通过Linux VFS调用底层的xxx_write函数,
由于这里定义为/sys 目录下的设备模型节点,所以对应写函数
应该为 xxx_store_xxx才对。

7.驱动层的led_on写函数实现

文件:kernel/drivers/leds/leds-sprd-bltc-rgb.c
static ssize_t store_on_off(struct device *dev,
                            struct device_attribute *attr, const char *buf, size_t size)
{
        struct led_classdev *led_cdev = dev_get_drvdata(dev);
        unsigned long state;
        ssize_t ret = -EINVAL;


        ret = kstrtoul(buf, 10, &state);
        PRINT_INFO("onoff_state_value:%1ld\n",state);
        onoff_value = state;
        led_cdev->flags = ONOFF;
        sprd_leds_bltc_rgb_set(led_cdev,state);
        return size;
}
在上述的写函数中又调用sprd_leds_bltc_rgb_set函数,并且将brightness值传入

8.sprd_leds_bltc_rgb_set的实现

static void sprd_leds_bltc_rgb_set(struct led_classdev *bltc_rgb_cdev,enum led_brightness value)
{
        struct sprd_leds_bltc_rgb *brgb;
        unsigned long flags;


                brgb = to_sprd_bltc_rgb(bltc_rgb_cdev);
                spin_lock_irqsave(&brgb->value_lock, flags);
                brgb->leds_flag = bltc_rgb_cdev->flags;
                brgb->value = value;
                spin_unlock_irqrestore(&brgb->value_lock, flags);


                if(1 == brgb->suspend) {
                        PRINT_WARN("Do NOT change brightness in suspend mode\n");
                        return;
                }
                if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_R]) == 0 || \
                    strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_G]) == 0 || \
                    strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_B]) == 0)
                        sprd_leds_rgb_work(brgb);
                else
                        sprd_leds_bltc_work(brgb);
}
在上述的写函数中又调用sprd_leds_bltc_work函数

9.sprd_leds_bltc_work的实现

static void sprd_leds_rgb_work(struct sprd_leds_bltc_rgb *brgb)
{
        unsigned long flags;


        mutex_lock(&brgb->mutex);
        spin_lock_irqsave(&brgb->value_lock, flags);
        if (brgb->value == LED_OFF) {
                spin_unlock_irqrestore(&brgb->value_lock, flags);
                sprd_leds_bltc_rgb_set_brightness(brgb);
                goto out;
        }
        spin_unlock_irqrestore(&brgb->value_lock, flags);
        sprd_leds_bltc_rgb_enable(brgb);
        PRINT_INFO("sprd_leds_bltc_rgb_work_for rgb!\n");


out:
        mutex_unlock(&brgb->mutex);
}
紧接着又调用sprd_leds_bltc_rgb_enable接口

10.sprd_leds_bltc_rgb_enable的实现

static void sprd_leds_bltc_rgb_enable(struct sprd_leds_bltc_rgb *brgb)
{
        sprd_bltc_rgb_init(brgb);


        if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_R]) == 0) {
                sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<0)|(0x1<<1));
                brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_R_PRESCL + BLTC_DUTY_OFFSET;
                sprd_leds_bltc_rgb_set_brightness(brgb);
        }
        if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_G]) == 0) {
                sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<4)|(0x1<<5));
                brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_G_PRESCL + BLTC_DUTY_OFFSET;
                sprd_leds_bltc_rgb_set_brightness(brgb);
        }
        if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_B]) == 0) {
                sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<8)|(0x1<<9));
                brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_B_PRESCL + BLTC_DUTY_OFFSET;
                sprd_leds_bltc_rgb_set_brightness(brgb);
        }
        .....


        PRINT_INFO("sprd_leds_bltc_rgb_enable\n");
        brgb->enable = 1;
}
紧接着又调用sprd_leds_bltc_rgb_set_brightness

11.sprd_leds_bltc_rgb_set_brightness的实现

static void sprd_leds_bltc_rgb_set_brightness(struct sprd_leds_bltc_rgb *brgb)
{
        unsigned long brightness = brgb->value;
        unsigned long pwm_duty;


        pwm_duty = brightness;
        if(pwm_duty > 255)
                pwm_duty = 255;
        sci_adi_write(brgb->bltc_addr, (pwm_duty<<8)|PWM_MOD_COUNTER,0xffff);
        PRINT_INFO("reg:0x%1LX set_val:0x%08X  brightness:%ld  brightness_level:%ld(0~15)\n", \
                   brgb->bltc_addr, sprd_leds_bltc_rgb_read(brgb->bltc_addr),brightness, pwm_duty);
}
这里使用最关键的一步使用 sci_adi_write 将ISINK 寄存器赋值,直接调整亮度 

三、总结

      本文着重讲解关机充电下,手机指示灯工作流程。整个流程非常清晰,只需要基本的IO基础知识即可,由于是在关机模式下,adbd 服务没有开启,所以我们不能直接查看节点的创建情况。调试过程中我们只需要通过跟踪串口log,追踪整个charge实现流程是否走到,也就是说能够程序走到上述的第6步,如果走到此处led仍然不能亮,那就要检查驱动或者硬件有无问题了。 

PS:本文侧重关机充电,当然也牵扯到部分Kernel 部分,至于kernel部分详细实现后面开机充电会详述说明。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值