本文转载自: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\n");
  7. return -1;
  8. }
  9. LOGD("all thread start\n");
  10. pthread_join(t_1, NULL);
  11. .....
  12. LOGD("charge app exit\n");
  13. return EXIT_SUCCESS;
  14. }

在主程序中,创建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. fd = open(WakeFileName, O_RDONLY, 0);
  7. if (fd < 0) {
  8. LOGD("Couldn't open file /sys/power/wait_for_fb_wake\n");
  9. return NULL;
  10. }
  11. do {
  12. err = read(fd, &buf, 1);
  13. LOGD("return from WakeFileName err: %d errno: %s\n", err, strerror(errno));
  14. } while (err < 0 && errno == EINTR);
  15. close(fd);
  16. bat_level = battery_capacity();
  17. update_progress_locked(bat_level);
  18. usleep(500000);
  19. }
  20. ......
  21. usleep(200);
  22. return NULL;
  23. }

改线程中有一个循环体,其中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. }

在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. }else{
  17. if(led_flag!= LED_GREEN){ //如果电池电量90~100 亮红灯
  18. led_on(LED_GREEN);  //调用亮灯函数
  19. led_flag = LED_GREEN;
  20. }
  21. }
  22. .....
  23. }

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\n",__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. fd = open(LED_GREEN_DEV, O_RDWR); //打开绿灯设备节点
  7. if(fd < 0) {
  8. SPRD_DBG("%s: open %s fail",__func__, LED_GREEN_DEV);
  9. return -1;
  10. }
  11. memset(buffer, 0, sizeof(buffer));
  12. sprintf(buffer, "%d", brightness);
  13. ret = write(fd, buffer, strlen(buffer)); //向节点中写入数据brightness值
  14. close(fd);
  15. return 0;
  16. }

亮绿灯函数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. ret = kstrtoul(buf, 10, &state);
  8. PRINT_INFO("onoff_state_value:%1ld\n",state);
  9. onoff_value = state;
  10. led_cdev->flags = ONOFF;
  11. sprd_leds_bltc_rgb_set(led_cdev,state);
  12. return size;
  13. }

在上述的写函数中又调用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. brgb = to_sprd_bltc_rgb(bltc_rgb_cdev);
  6. spin_lock_irqsave(&brgb->value_lock, flags);
  7. brgb->leds_flag = bltc_rgb_cdev->flags;
  8. brgb->value = value;
  9. spin_unlock_irqrestore(&brgb->value_lock, flags);
  10. if(1 == brgb->suspend) {
  11. PRINT_WARN("Do NOT change brightness in suspend mode\n");
  12. return;
  13. }
  14. if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_R]) == 0 || \
  15. strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_G]) == 0 || \
  16. strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_B]) == 0)
  17. sprd_leds_rgb_work(brgb);
  18. else
  19. sprd_leds_bltc_work(brgb);
  20. }

在上述的写函数中又调用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. mutex_lock(&brgb->mutex);
  5. spin_lock_irqsave(&brgb->value_lock, flags);
  6. if (brgb->value == LED_OFF) {
  7. spin_unlock_irqrestore(&brgb->value_lock, flags);
  8. sprd_leds_bltc_rgb_set_brightness(brgb);
  9. goto out;
  10. }
  11. spin_unlock_irqrestore(&brgb->value_lock, flags);
  12. sprd_leds_bltc_rgb_enable(brgb);
  13. PRINT_INFO("sprd_leds_bltc_rgb_work_for rgb!\n");
  14. out:
  15. mutex_unlock(&brgb->mutex);
  16. }

紧接着又调用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. if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_R]) == 0) {
  5. sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<0)|(0x1<<1));
  6. brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_R_PRESCL + BLTC_DUTY_OFFSET;
  7. sprd_leds_bltc_rgb_set_brightness(brgb);
  8. }
  9. if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_G]) == 0) {
  10. sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<4)|(0x1<<5));
  11. brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_G_PRESCL + BLTC_DUTY_OFFSET;
  12. sprd_leds_bltc_rgb_set_brightness(brgb);
  13. }
  14. if(strcmp(brgb->cdev.name,sprd_leds_rgb_name[SPRD_LED_TYPE_B]) == 0) {
  15. sci_adi_set(brgb->sprd_bltc_base_addr + BLTC_CTRL, (0x1<<8)|(0x1<<9));
  16. brgb->bltc_addr = brgb->sprd_bltc_base_addr + BLTC_B_PRESCL + BLTC_DUTY_OFFSET;
  17. sprd_leds_bltc_rgb_set_brightness(brgb);
  18. }
  19. .....
  20. PRINT_INFO("sprd_leds_bltc_rgb_enable\n");
  21. brgb->enable = 1;
  22. }

紧接着又调用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. pwm_duty = brightness;
  6. if(pwm_duty > 255)
  7. pwm_duty = 255;
  8. sci_adi_write(brgb->bltc_addr, (pwm_duty<<8)|PWM_MOD_COUNTER,0xffff);
  9. PRINT_INFO("reg:0x%1LX set_val:0x%08X  brightness:%ld  brightness_level:%ld(0~15)\n", \
  10. brgb->bltc_addr, sprd_leds_bltc_rgb_read(brgb->bltc_addr),brightness, pwm_duty);
  11. }

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

三、总结

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

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

展讯7731C_M Android6.0 充电指示灯实现(一)------关机充电实现【转】的更多相关文章

  1. 关于android各种双卡手机获取imei,imsi的处理(mtk,展讯,高通等)

    目前国内对于双卡智能手机的需求还是很大的,各种复杂的业务会涉及到双卡模块:而android标准的api又不提供对双卡的支持.导致国内双卡模块标准混乱,各个厂商各玩各的.目前我知道的双卡解决方案就有:m ...

  2. Android app 在线更新那点事儿(适配Android6.0、7.0、8.0)

    一.前言 app在线更新是一个比较常见需求,新版本发布时,用户进入我们的app,就会弹出更新提示框,第一时间更新新版本app.在线更新分为以下几个步骤: 1, 通过接口获取线上版本号,versionC ...

  3. 展讯sprd_battery.c 充电驱动

    sprd_battery.c 是充电驱动,这个是充电功能的核心内容,电量显示策略.温度检测策略.充电保护机制等功能在这里实现,功能实现与硬件细节剥离,调用通用接口实现逻辑控制: 1 sprdbat_p ...

  4. Android6.0运行时权限管理

    自从Android6.0发布以来,在权限上做出了很大的变动,不再是之前的只要在manifest设置就可以任意获取权限,而是更加的注重用户的隐私和体验,不会再强迫用户因拒绝不该拥有的权限而导致的无法安装 ...

  5. android6.0的坑

    虽然现在android已经出了7.0了.但是大部分人用的应该还是5.0和6.0的. 其中对于开发者来说,变化比较大的应该是6.0之前和6.0之后的版本. 因为以6.0为分界线多了一个比较坑的东西:权限 ...

  6. Android6.0动态获取权限

    Android6.0采用新的权限模型,只有在需要权限的时候,才告知用户是否授权,是在runtime时候授权,而不是在原来安装的时候 ,同时默认情况下每次在运行时打开页面时候,需要先检查是否有所需要的权 ...

  7. 编译可在Nexus5上运行的CyanogenMod13.0 ROM(基于Android6.0)

    编译可在Nexus5上运行的CyanogenMod13.0 ROM (基于Android6.0) 作者:寻禹@阿里聚安全 前言 下文中无特殊说明时CM代表CyanogenMod的缩写. 下文中说的“设 ...

  8. Android开发学习之路-Android6.0运行时权限

    在Android6.0以后开始,对于部分敏感的“危险”权限,需要在应用运行时向用户申请,只有用户允许的情况下这个权限才会被授予给应用.这对于用户来说,无疑是一个提升安全性的做法.那么对于开发者,应该怎 ...

  9. Android6.0动态权限申请

    goggle在Android6.0要求部分权限需要动态申请,直接下载AndroidManifest.xml中无效 6.0权限的基本知识,以下是需要单独申请的权限,共分为9组, 每组只要有一个权限申请成 ...

随机推荐

  1. vs2017创建支持多框架(net4.6.1;net4.6.2;netstandard2.0;netcoreapp2.0)版本

    1.新建netcore或netstandard或net4.6.1项目 2.编辑项目文件: <Project Sdk="Microsoft.NET.Sdk">   < ...

  2. 使用js Math.random()函数生成n到m间的随机数字

    何使用js生成n到m间的随机数字,主要目的是为后期的js生成验证码做准备,Math.random()函数返回0和1之间的伪随机数   摘要: 本文讲解如何使用js生成n到m间的随机数字,主要目的是为后 ...

  3. Windows10上安装Keras 和 TensorFlow-GPU

    安装环境: Windows 10 64bit GPU: GeForce gt 720 Python: 3.5.3 CUDA: 8 首先下载Anaconda3的Win10 64bit版,安装Python ...

  4. Life Winner Bo (博弈论)

    kind:维持让对手处于(奇数,奇数)的状态,就能赢. rook:维持让对手处于(A,A)相等的状态,就能赢. knight:画图找规律,没有到达终点的就是平局. queen:威佐夫博弈论,终点不一样 ...

  5. Python 第四阶段 学习记录之----多线程

    多线程 多线程例子, 注释部份即为多线程的使用 #-*- coding: utf-8 -*- # Wind clear raise # 2017/3/5 下午2:34 import socket im ...

  6. Python: 字典dict: zip()

    problem: 怎样在数据字典中执行一些计算操作(比如求最小值.最大值.排序等等)? answer: eg1: 考虑下面的股票名和价格映射字典: prices = {'ACME': 45.23,'A ...

  7. java 泛型E T ?的区别

    Java泛型中的标记符含义:  E - Element (在集合中使用,因为集合中存放的是元素) T - Type(Java 类) K - Key(键) V - Value(值) N - Number ...

  8. 【转】求职面试-HR会问你什么问题?

    前言 面试是程序员们经常探讨的话题,只要你通过前面的技术面,最后一面必然是HR面试,基本上到了这关你离Offer的距离应该不会太远了,但有的公司的HR是有刷入的权利,如果你并不能很好的应对HR的问题, ...

  9. 20165215 实验一 Java开发环境的熟悉

    20165215 实验一 Java开发环境的熟悉 一.实验报告封面 课程:Java程序设计 班级:1652班 姓名:张家佳 学号:20165215 指导教师:娄嘉鹏 实验日期:2018年4月2日 实验 ...

  10. golang学习笔记5 用bee工具创建项目 bee工具简介

    golang学习笔记5 用bee工具创建项目 bee工具简介 Bee 工具的使用 - beego: 简约 & 强大并存的 Go 应用框架https://beego.me/docs/instal ...