前言:

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

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

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

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

service charge /bin/chargeuser rootoneshot

其中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);}elseSPRD_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);elsesprd_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部分详细实现后面开机充电会详述说明。

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

  1. 展讯7731C_M Android6.0 充电指示灯实现

    http://blog.csdn.net/xiaopangzi313/article/details/52199407 上一节已经了解了展讯7731C_M Android6.0 充电指示灯实的关机部分 ...

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

    本文转载自:https://blog.csdn.net/m0_37870649/article/details/80566131 前言: 在手机充电中常常使用充电指示灯来观察手机充电状态,比如说将手机 ...

  3. android 10.0 展讯 MTK内置可卸载app

    1.概述 在进行10.0的产品定制化开发中,展讯内置可卸载恢复的app 其实把apk编译到system/proloadapp 目录下即可 2.展讯 MTK内置可卸载app的实现步骤 实现方式: 1 预 ...

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

    2019独角兽企业重金招聘Python工程师标准>>> 关于android各种双卡手机获取imei,imsi的处理(mtk,展讯,高通等) 目前国内对于双卡智能手机的需求还是很大的, ...

  5. Android 系统(98)---Android app 在线更新那点事儿(适配Android6.0、7.0、8.0)

    Android app 在线更新那点事儿(适配Android6.0.7.0.8.0) 一.前言 app在线更新是一个比较常见需求,新版本发布时,用户进入我们的app,就会弹出更新提示框,第一时间更新新 ...

  6. 展讯sprd_battery.c 充电驱动

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

  7. 7 展讯Sprd设置-电池-关联自启动-跟踪代码

    1. UI-关联自启动 2. 源码走读-上层接口调用 2.1 字符串 strings_ex.xml <string name="app_as_lunch">关联启动&l ...

  8. 小米屏和展讯屏幕调试参考 写的较好(有发送速率)

    MIPI LCD调试总结 http://blog.csdn.net/richu123/article/details/51394464 近来在用SSD2828驱动小米屏,没有代码,没有技术支持,自己写 ...

  9. 展讯走出困境开始爬坡

    2009年8月19日展讯发布Q2财报,虽然依然亏随1310万美元,不过从近几个季度财报分析,展讯已经走出2008年以来的低谷,开始迅速爬坡,按照展讯公布的财报预计2009年Q3营收将超过3100万美元 ...

最新文章

  1. AI产业链全景图!【物联网智商精选】
  2. Layer 2 Tunneling Protocol
  3. EF框架对数据库的操作
  4. JS学习笔记:防止发生命名冲突
  5. 80211 发送速率选择算法分析
  6. 腾讯视频怎么禁止别人登录我的会员
  7. rowdata java_Java RowDataUtil.addRowData方法代碼示例
  8. 任正非:华为欲出售5G技术制造竞争对手
  9. 制图折断线_无锡春华教育AutoCAD家具制图/机械/工程制图
  10. PosgreSQL快速参数调优和sysbench压测
  11. 当前读和快照读是什么 区别
  12. 201111-W-网络技术-基础理论与应用说明
  13. WordPress漏洞扫描器wpscan
  14. (八)冰点还原安装及使用
  15. WEB学习第四天(网页模型
  16. android6.0相机假对焦,android相机对焦
  17. webscraper多页爬取_Web Scraper 高级用法——Web Scraper 抓取多条内容 | 简易数据分析 07...
  18. 安装html5 win7,win7官方正版64位系统安装教程
  19. HTML px em pt长度单位(像素 相对长度 点)
  20. pygame战棋游戏制作之战棋光标设置上(三)

热门文章

  1. 幻数java题_幻数
  2. 0x0000007b电脑蓝屏的解决方法
  3. 离散信号内插和抽取Matlab,抽取与内插的频谱分析
  4. 【Dos】常见的Dos攻击
  5. DFRobot行业AI开发者大赛--LattePandaDelta
  6. SQL 登录注入脚本_vBulletin再修复高危RCE和SQL注入漏洞
  7. 疫情引发橡胶产业市场动荡,企业如何重构供应生态打破劣局?
  8. 浅层砂过滤器的原理是什么,滤料是什么,需要不需要定期?
  9. ph1 android p,ph1(安卓之父ph1参数)
  10. 方格网的填方和挖方计算