展訊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中,如下:

 

[cpp]  view plain  copy
  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

 

[cpp]  view plain  copy
  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\n");  
  7.         return -1;  
  8.     }  
  9.   
  10.   
  11.     LOGD("all thread start\n");  
  12.   
  13.   
  14.     pthread_join(t_1, NULL);  
  15.   
  16.   
  17.     .....  
  18.     LOGD("charge app exit\n");  
  19.   
  20.   
  21.     return EXIT_SUCCESS;  
  22. }  

在主程序中,創建charge_thread用來檢測電池狀態,然后根據充電電量的變化來控制充電指示燈

2. 充電線程的定義

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

[cpp]  view plain  copy
  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\n");  
  11.         return NULL;  
  12.         }  
  13.         do {  
  14.             err = read(fd, &buf, 1);  
  15.             LOGD("return from WakeFileName err: %d errno: %s\n", 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方法的實現

[cpp]  view plain  copy
  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方法的實現

[cpp]  view plain  copy
  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

[cpp]  view plain  copy
  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\n",__func__,color);  
  17. }  

在亮燈函數led_on 中, 通過傳入的參數clor 來區分不能顏色燈,這里以綠燈為例

6. 亮綠燈函數eng_led_green_test的實現

[cpp]  view plain  copy
  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

[cpp]  view plain  copy
  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\n",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的實現

[cpp]  view plain  copy
  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\n");  
  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的實現

[cpp]  view plain  copy
  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!\n");  
  16.   
  17.   
  18. out:  
  19.         mutex_unlock(&brgb->mutex);  
  20. }  

緊接着又調用sprd_leds_bltc_rgb_enable接口

10.sprd_leds_bltc_rgb_enable的實現

[cpp]  view plain  copy
  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\n");  
  25.         brgb->enable = 1;  
  26. }  

緊接着又調用sprd_leds_bltc_rgb_set_brightness

11.sprd_leds_bltc_rgb_set_brightness的實現

[cpp]  view plain  copy
  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)\n", \  
  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部分詳細實現后面開機充電會詳述說明。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM