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


    本文转载自:https://blog.csdn.net/m0_37870649/article/details/80566131

    前言:

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

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

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

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

    1. service charge /bin/charge  
    2.     user root  
    3.     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

    1. int  
    2. main(int argc, char **argv) {  
    3.     .....  
    4.     ret = pthread_create(&t_1, NULL, charge_thread, NULL);  
    5.     if(ret){  
    6.         LOGE("thread:charge_thread creat failed ");  
    7.         return -1;  
    8.     }  
    9.   
    10.   
    11.     LOGD("all thread start ");  
    12.   
    13.   
    14.     pthread_join(t_1, NULL);  
    15.   
    16.   
    17.     .....  
    18.     LOGD("charge app exit ");  
    19.   
    20.   
    21.     return EXIT_SUCCESS;  
    22. }  

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

    2. 充电线程的定义

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

    1. #define WakeFileName  "/sys/power/wait_for_fb_wake"  
    2. void *charge_thread(void *cookie)  
    3. {  
    4.     .....  
    5.     for (;!is_exit;) {  
    6.   
    7.   
    8.         fd = open(WakeFileName, O_RDONLY, 0);  
    9.         if (fd < 0) {  
    10.         LOGD("Couldn't open file /sys/power/wait_for_fb_wake ");  
    11.         return NULL;  
    12.         }  
    13.         do {  
    14.             err = read(fd, &buf, 1);  
    15.             LOGD("return from WakeFileName err: %d errno: %s ", err, strerror(errno));  
    16.         } while (err < 0 && errno == EINTR);  
    17.         close(fd);  
    18.         bat_level = battery_capacity();  
    19.         update_progress_locked(bat_level);  
    20.         usleep(500000);  
    21.     }  
    22.     ......  
    23.     usleep(200);  
    24.     return NULL;  
    25. }  

    改线程中有一个循环体,其中battery_capacity用来获取电池容量,
    将电池容量不断的传入update_progress_locked方法中

    3.update_progress_locked方法的实现

    1. static void update_progress_locked(int level)  
    2. {  
    3.     ......  
    4.     draw_progress_locked(level);  // Draw only the progress bar  
    5.   
    6.   
    7. }  

    在update_progress_locked中,又调用draw_progress_locked方法

    4.draw_process_locked方法的实现

    1. #define LED_GREEN         1  
    2. #define LED_RED           2  
    3. #define LED_BLUE          3  
    4. static void draw_progress_locked(int level)  
    5. {  
    6.     .....  
    7.     if(level > 100)  
    8.         level = 100; //处理电池电量的上限  
    9.     else if (level < 0)//处理电池电量的下限  
    10.         level = 0;    
    11.     if(level < 90){  
    12.         if(led_flag!= LED_RED){  
    13.             led_on(LED_RED);  
    14.             led_flag = LED_RED;   //如果电池电量低于90亮绿灯  
    15.         }  
    16.   
    17.   
    18.     }else{  
    19.         if(led_flag!= LED_GREEN){ //如果电池电量90~100 亮红灯  
    20.             led_on(LED_GREEN);  //调用亮灯函数  
    21.             led_flag = LED_GREEN;  
    22.         }  
    23.     }  
    24.     .....  
    25. }  

    5. 亮灯函数led_on的实现

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

    1. void led_on(int color)  
    2. {  
    3.     if(color == 1){  
    4.         eng_led_green_test(max_green_led/2);  
    5.         eng_led_red_test(0);  
    6.         eng_led_blue_test(0);  
    7.     }else if(color == 2){  
    8.         eng_led_red_test(max_red_led/2);  
    9.         eng_led_green_test(0);  
    10.         eng_led_blue_test(0);  
    11.     }else if(color == 3){  
    12.         eng_led_blue_test(0);  
    13.         eng_led_red_test(max_green_led/2);  
    14.         eng_led_green_test(max_red_led/2);  
    15.     }else  
    16.         SPRD_DBG("%s: color is %d invalid ",__func__,color);  
    17. }  

    在亮灯函数led_on 中, 通过传入的参数clor 来区分不能颜色灯,这里以绿灯为例

    6. 亮绿灯函数eng_led_green_test的实现

    1. static int eng_led_green_test(int brightness)  
    2. {  
    3.     int fd;  
    4.     int ret;  
    5.     char buffer[8];  
    6.   
    7.   
    8.     fd = open(LED_GREEN_DEV, O_RDWR); //打开绿灯设备节点  
    9.   
    10.   
    11.     if(fd < 0) {  
    12.         SPRD_DBG("%s: open %s fail",__func__, LED_GREEN_DEV);  
    13.         return -1;  
    14.     }  
    15.   
    16.   
    17.     memset(buffer, 0, sizeof(buffer));  
    18.     sprintf(buffer, "%d", brightness);  
    19.     ret = write(fd, buffer, strlen(buffer)); //向节点中写入数据brightness值  
    20.   
    21.   
    22.     close(fd);  
    23.   
    24.   
    25.     return 0;  
    26. }  

    亮绿灯函数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

    1. static ssize_t store_on_off(struct device *dev,  
    2.                             struct device_attribute *attr, const char *buf, size_t size)  
    3. {  
    4.         struct led_classdev *led_cdev = dev_get_drvdata(dev);  
    5.         unsigned long state;  
    6.         ssize_t ret = -EINVAL;  
    7.   
    8.   
    9.         ret = kstrtoul(buf, 10, &state);  
    10.         PRINT_INFO("onoff_state_value:%1ld ",state);  
    11.         onoff_value = state;  
    12.         led_cdev->flags = ONOFF;  
    13.         sprd_leds_bltc_rgb_set(led_cdev,state);  
    14.         return size;  
    15. }  

    在上述的写函数中又调用sprd_leds_bltc_rgb_set函数,并且将brightness值传入

    8.sprd_leds_bltc_rgb_set的实现

    1. static void sprd_leds_bltc_rgb_set(struct led_classdev *bltc_rgb_cdev,enum led_brightness value)  
    2. {  
    3.         struct sprd_leds_bltc_rgb *brgb;  
    4.         unsigned long flags;  
    5.   
    6.   
    7.                 brgb = to_sprd_bltc_rgb(bltc_rgb_cdev);  
    8.                 spin_lock_irqsave(&brgb->value_lock, flags);  
    9.                 brgb->leds_flag = bltc_rgb_cdev->flags;  
    10.                 brgb->value = value;  
    11.                 spin_unlock_irqrestore(&brgb->value_lock, flags);  
    12.   
    13.   
    14.                 if(1 == brgb->suspend) {  
    15.                         PRINT_WARN("Do NOT change brightness in suspend mode ");  
    16.                         return;  
    17.                 }  
    18.                 if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_R]) == 0 ||   
    19.                     strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_G]) == 0 ||   
    20.                     strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_B]) == 0)  
    21.                         sprd_leds_rgb_work(brgb);  
    22.                 else  
    23.                         sprd_leds_bltc_work(brgb);  
    24. }  

    在上述的写函数中又调用sprd_leds_bltc_work函数

    9.sprd_leds_bltc_work的实现

    1. static void sprd_leds_rgb_work(struct sprd_leds_bltc_rgb *brgb)  
    2. {  
    3.         unsigned long flags;  
    4.   
    5.   
    6.         mutex_lock(&brgb->mutex);  
    7.         spin_lock_irqsave(&brgb->value_lock, flags);  
    8.         if (brgb->value == LED_OFF) {  
    9.                 spin_unlock_irqrestore(&brgb->value_lock, flags);  
    10.                 sprd_leds_bltc_rgb_set_brightness(brgb);  
    11.                 goto out;  
    12.         }  
    13.         spin_unlock_irqrestore(&brgb->value_lock, flags);  
    14.         sprd_leds_bltc_rgb_enable(brgb);  
    15.         PRINT_INFO("sprd_leds_bltc_rgb_work_for rgb! ");  
    16.   
    17.   
    18. out:  
    19.         mutex_unlock(&brgb->mutex);  
    20. }  

    紧接着又调用sprd_leds_bltc_rgb_enable接口

    10.sprd_leds_bltc_rgb_enable的实现

    1. static void sprd_leds_bltc_rgb_enable(struct sprd_leds_bltc_rgb *brgb)  
    2. {  
    3.         sprd_bltc_rgb_init(brgb);  
    4.   
    5.   
    6.         if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_R]) == 0) {  
    7.                 sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<0)|(0x1<<1));  
    8.                 brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_R_PRESCL + BLTC_DUTY_OFFSET;  
    9.                 sprd_leds_bltc_rgb_set_brightness(brgb);  
    10.         }  
    11.         if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_G]) == 0) {  
    12.                 sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<4)|(0x1<<5));  
    13.                 brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_G_PRESCL + BLTC_DUTY_OFFSET;  
    14.                 sprd_leds_bltc_rgb_set_brightness(brgb);  
    15.         }  
    16.         if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_B]) == 0) {  
    17.                 sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<8)|(0x1<<9));  
    18.                 brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_B_PRESCL + BLTC_DUTY_OFFSET;  
    19.                 sprd_leds_bltc_rgb_set_brightness(brgb);  
    20.         }  
    21.         .....  
    22.   
    23.   
    24.         PRINT_INFO("sprd_leds_bltc_rgb_enable ");  
    25.         brgb->enable = 1;  
    26. }  

    紧接着又调用sprd_leds_bltc_rgb_set_brightness

    11.sprd_leds_bltc_rgb_set_brightness的实现

    1. static void sprd_leds_bltc_rgb_set_brightness(struct sprd_leds_bltc_rgb *brgb)  
    2. {  
    3.         unsigned long brightness = brgb->value;  
    4.         unsigned long pwm_duty;  
    5.   
    6.   
    7.         pwm_duty = brightness;  
    8.         if(pwm_duty > 255)  
    9.                 pwm_duty = 255;  
    10.         sci_adi_write(brgb->bltc_addr, (pwm_duty<<8)|PWM_MOD_COUNTER,0xffff);  
    11.         PRINT_INFO("reg:0x%1LX set_val:0x%08X  brightness:%ld  brightness_level:%ld(0~15) ",   
    12.                    brgb->bltc_addr, sprd_leds_bltc_rgb_read(brgb->bltc_addr),brightness, pwm_duty);  
    13. }  

    这里使用最关键的一步使用 sci_adi_write 将ISINK 寄存器赋值,直接调整亮度 

    三、总结

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

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

  • 相关阅读:
    Django 前后端数据传输、ajax、分页器
    项目分层
    The Jaisalmer Desert Festival 2017/2/9
    Slacklining 2017/2/7
    Slacklining 2017/2/6
    SnowKiting 2017/1/24
    ADO1
    SnowKiting
    CSS-学习笔记六
    D Vitamin
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/10245441.html
Copyright © 2020-2023  润新知