记录:闪烁灯的颜色问题

【问题背景】
        在手机来电通知等场景下,灯要设置为白色闪烁

一、代码流程分析

上层代码:

1.frameworks/base/services/core/java/com/android/server/BatteryService.java

led类里的通过 LIGHT_ID_BATTERY 得到 mBatteryLight ,然后可通过 updateLightsLocked()方法去根据电池状态去设置led的颜色;

 颜色参数:qssi12/frameworks/base/core/res/res/values/config.xml

 颜色看后6位,分别表示RGB

大概看一下图

 

 

 

 一路带着color ,onMS  闪烁的亮的时间,offMS 闪烁灭的时间, 这些都写在了config.xml 文件里,来到了lights.c

 lights.c

根据模式比如充电指示灯,闪烁,来对诸如 “/sys/class/leds/%s/trigger”的目录文件执行文件操作,相当于我们在调试led的时候执行的ehco 1 > sys/class/leds/red/brightness

1.根据 LIGHT_ID_BATTERY 给set_light 赋值set_light_battery,这样就走了充电指示灯和notify的逻辑

 

 

 set_speaker_light_locked 就是包含了主要逻辑的函数

这里先通过位操作,来把config.xml 里定义的十六进制的rgb参数分解出来,这里也可以看出后六位分别表示了红色绿色蓝色

然后获取onMS 和 offMS

 

然后分了三个逻辑,呼吸,闪烁和普通的brightness

呼吸对应 "/sys/class/leds/%s/breath"

闪烁对应 "/sys/class/leds/%s/trigger" 这个项目是通过设置timer触发器在软件上来实现的闪烁功能,有的项目也可能是已经的blink

 

三种逻辑最后都通过write_int 来进行文件操作

 

记录一下 set_rgb_led_timer_trigger 闪烁模式

 

这里对led子系统中的触发器进行选择,选择为 timer,相当于我们调试时的 echo timer > sys/class/leds/red/trigger

这个时候你去cat sys/class/leds/red/trigger 会看到在timer那加了个"[]",形如 “[tmer]” ,表示当前选择的是timer触发器

这个时候也会在sys/class/leds/red/ 目录下创建delay_on delay_off两个属性

函数接下来的操作进行对这两个属性写入上层一路传下来的onMS和offMS,闪烁亮灭时间

这里注意一下,闪烁模式里并没有用到颜色的参数,那颜色的参数怎么来的呢,这需要去里一下blink软件实现的逻辑

led 子系统

主要文件:

          driver/leds/led-class.c

          driver/leds/led-core.c

          driver/leds/led-triggers.c

          include/linux/leds.h

触发器文件:有很多触发器文件,这里记录知道的用于实现闪烁的timer触发器

          ledtrig-timer.c

--------------------------------------------------------------------------------------------------------------------------→

1.先看一下led子系统中表示一个led设备的数据结构,比如一个红色的灯

leds.h: led_classdev

 

前面定义了name,我们可以通过这个来区别它是什么灯,这个项目在驱动里直接创建了"red","green","blue"三个led_classdev
定义了brightness ,这个就是会写 到sys/class/leds/red/brightness的值了
定义了max_brightness, 存了最大值,一般都会给255
然后是很多原子位,对应了work_flags里的位,比如 LED_BLINK_SW表示你可以通过work_flags的第0位去判断是不是设置了软件的blink(字面意思),所有我们可以用上面没用的位去做一些操作

 

然后就是一些回调函数,你在注册的时候可以选择性的赋值,这个项目只用到brightness_set和brightness_get,这两个函数就是底层实现对应的brightness的方法
在你cat sys/class/leds/red/brightness的时候就会跑red这个led_classdevbrightness_get,反之echo跑brightness_setbrightness_set里面写的就是把你的0~255怎么通过读写寄存器设置电流来表现出来
具体还得看驱动代码

 

2.led-class.c  在/sys/class/ 下创建leds类目录,并为这个类下的设备创建好属性文件,提供了led_classdev的注册接口

入口:leds_init

 

brightness max_brightness等属性的创建

 

 

提供register接口

 

当驱动调用led_classdev_register注册了一个设备,那么就会在/sys/class/leds目录下创建xxx设备,并这个xxx目录下创建一系列attr属性文件,如brightness max_brightness trigger等

 

注册这个函数很长,我用到的有用的信息;1.work_flags注册的时候赋值为0;2.没有设置max_brightness就默认设置为LED_FULL(255);

3.led_init_core

 

这个函数里初始化了一个队列,这个队列会通过调用led_classdev设备的set_brightness来设置brightness
还初始化了一个定时器,这个定时器就是用来实现软件的闪烁blink的,这个后面再看看

 

cat 或者echo 属性文件时

在文件系统下读写这些属性文件时,就会调用这些属性文件的show和store方法.
比如cat /sys/class/leds/xxx/brightness时会调用led-class.c中的brightness_show函数,最后会调用实际驱动注册的led_classdev的brightness_get函数

 

 

当用户echo 100 > /sys/class/leds/xxx/brightness时会调用led-class.c中的brightness_store函数,最后会调用实际驱动注册的led_classdev的brightness_set函数


led_set_brightness 里会去调度上面初始化的队列 set_brightness_delayed
flush_work(&led_cdev->set_brightness_work); 的意思是等待 队列执行完毕

 

3.driver/leds/led-triggers.c 提供了一些trigger相关的符号,这里记录 led_trigger_store和 led_trigger_show

在driver/leds/led-class.c 里为设备注册属性的时候也注册了trigger属性,也就是我们ls sys/class/leds/red/ 里的那个trigger

 

这里很明显当我们echo / cat sys/class/leds/red/trigger 的时候会跑 led_trigger_store和 led_trigger_show

led_trigger_show 就是为了把你cat出来的形式表现出来,就是你现在是用的什么触发器,那这个驱动器会有个"[ ]"表示选中,比如"[ timer ]",没有就是"[ none ]"

 

led_trigger_store 主要关注一下led_trigger_set 这个函数会跑对应trigger的activated函数,然后激活此trigger

 

timer 触发器

这个触发器就是来实现软件上的blink的,也就是让灯泡 亮,灭,亮,灭,并且你要设置颜色

要是你的led_classdev注册了硬件闪烁的接口led_cdev→blink_set那说明你的可以用硬件闪烁,这个项目没有用到这个接口

先回忆一下之前在lights.c里跑闪烁流程的这个函数里 set_rgb_led_timer_trigger 

 

通过echo timer /sys/class/leds/xxx/trigger 去设置使用timer 触发器从而跑timer的activate 函数

 

然后设置了亮灭时间

并注意这里没用到上层传下来的brightness,
所以此时red green blue 三个灯的状态是多变的,
比如要是这个时候是低电量,那red的brightness为255,green,blue的就为0;要是灯根本没亮,那你要是用wife adb去设置闪烁,此时的三个灯的brightness就都是0了
等等等

所以一个很大的疑问就是,闪烁灯的颜色是怎么实现的,比如这个项目要求这个闪烁得是白色

一步步来看看代码:


上面那个if看英文直译是当设置了默认触发器的时候跑的一个逻辑,pattern_init里是设置了一下delay_on和delay_off,具体设置的是啥还是用到上面传下来的onMS和offMS,就不纠结了,反正最后用到的是onMS和offMS
flush_work(&led_cdev→set_brightness_work); 等待上一个设置brightness的队列跑完
继续看led_blink_set

 


del_timer_sync 删了这个blink的定时器并保证没有任何其他地方再跑
位操作置0 LED_BLINK_SW 这个表示是不是再跑blink ,另外两个没用过
继续看led_blink_setup

 
这里重点看一下当delay_on 和 delay_off都没设置的时候会设置为500
任何看led_set_software_blink 看名字就知道这个函数要开始blink了

 
主要看红框部分
led_get_brightness 很简单就是 return led_cdev->brightness;  
这里获得当前led_classdev 的brightness 

然后 if 判断 ,如果有就存到 blink_brightness ,这里 blink_brightness可以理解为闪烁的时候这个灯要设置的0~255,因为闪烁就是一下亮一下灭,亮的值就存在blink_brightness
由此想到要是手机低电量,红灯是255,绿灯和蓝灯都是0,那红灯来着存好了它的闪烁的blink_brightness为255,绿灯和蓝灯因为是0,就没存了   当然这里还有很多情况,也有可能三个灯都有brightnes

然后又判断 led_cdev→blink_brightness,这里的意思就是你要是没存到,你就存最大值255作为你的blink_brightness

接下来设置delay_on   delay_off

中间两个和delay_on   delay_off相关的if ,很好理解,!delay_on就是不亮,那就关灯;!delay_off就是不灭,那就长亮
led_set_brightness_nosleep 这个函数最后也是调度那个队列去设置brightness,最后跑的也是led_classdev 的set_brightness

设置 LED_BLINK_SW为1,表示开始了blink

mod_timer(&led_cdev->blink_timer, jiffies + 1); 激活定时器

然后就会跑到定时器这边的回调 led_timer_function

 
红框前一些检查就不看了,到了红框的位置就说明blink条件满足了,可以进行软件的闪烁了

 
主要看看它到底是怎么实现闪烁的,记住闪烁就是一下亮一下灭,思路就是通过led_classdev 的set_brightness来开灯关灯,当你的brightness为0的就关灯

首先他有去取一次当前led_classdev 的brightness

然后 if 判断,
如果没有亮度,也就是brightness为0,就把之前在led_set_software_blink 存的blink_brightness拿出来给 brightness,这个brightness就是最后用到的值,也就是开灯了,这里可以没用上new_blink_brightness那边,通过log打印没走这,具体的得理解一下LED_BLINK_BRIGHTNESS_CHANGE
如果有亮度,也就是brightness有值,存好到blink_brightness里然后设置 brightness为LED_OFF(0),也就是关灯了
所以这里走的是反的,你有brightness就灭了,然后存好,你下次进来因为是灭的就是0,那就把存好的值拿出来就亮了

delay相关的就不说了

最后也是调用 led_set_brightness_nosleep(led_cdev, brightness); 来把 brightness 写进去的

mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay)); 最后通过这个在delay的时间后再来一遍

而对于底层led驱动大概就是注册好三个led_classdev 设备,红灯,绿灯,蓝灯,然后写好他们的set_brightness接口,当闪烁  led_set_brightness_nosleep 里通过调度队列来最终跑这个设备的set_brightness的时候,或者是当调试的时候我们echo 255 > sys/class/leds/xxx/brightness的时候,驱动里红灯,绿灯,蓝灯,他们的set_brightness接口里就会把这个0~255的数字已电流的形式在硬件上表现出来

这里还有个breath,呼吸灯,这个在项目里是实现在具体的底层驱动leds-aw2016.c里的,结合手册看还是很容易理解的,以后的breath也会实现的底层驱动里吗?

二、问题解决方法

【调试方法】

echo timer > sys/class/leds/red/trigger
echo timer > sys/class/leds/green/trigger
echo timer > sys/class/leds/blue/trigger
echo 500 > sys/class/leds/red/delay_on
echo 500 > sys/class/leds/green/delay_on
echo 500 > sys/class/leds/blue/delay_on
echo 2500 > sys/class/leds/red/delay_off
echo 2500 > sys/class/leds/green/delay_off
echo 2500 > sys/class/leds/blue/delay_off

【问题分析】
要闪烁成白色就要把对应的红灯,绿灯,蓝灯的brightness设置好,也就是要找到一个合适的RGB参数使灯为白色,这个可以通过echo sys/class/leds/xxx/brightness来调试,这个项目这里我感觉看起来舒服点的是(138,82,22)

通过对使用timer触发器在软件上实现闪烁的代码逻辑的梳理,
我们可以发现bringhtness 是通过led_get_brightness来得到的,而led_get_brightness只是简单的返回led_classdev 的brightness属性,
然后如果brightness不为0,就把得到brightness存到blink_brightness;brightness 为0,就给blink_brightness 给255,  最后把 blink_brightness 用于闪烁

也就是说,要是led_get_brightness 返回的值大于0,那这个值就是闪烁的时候亮灯用到的值,灭灯就不用说了,大家都是0

因为来电显示的时候手机可能处于多种状态,在充电就有低电,中电,高电三个状态,每个状态的RGB参数都不一样,还有一般手拿着手机的时候灯不亮,那就是三个灯都是0
所以我们的思路是,在进入闪烁之前就应该把灯写成白色,也就是把RGB参数设置好,这样他就会按照白色闪烁了

有了思路改的方法就很多了,我改的地方是 led_blink_setup 函数


考虑到在进入led_set_software_blink 前就写好,这样后面也不用去判断led_set_software_blink 返回的brightness的是否为0来设置blink_brightness 的逻辑了,也就是不用考虑后面blink_brightness 的设置问题
且在进去之前设置好,不会影响闪烁功能函数的运行时间,因为有通过字符串比较的逻辑,这个会影响效率,导致出现三个灯没能一起亮或者没有一起灭,就可能出现白灯灭了后还有别的颜色,比如白灯灭了后还有蓝色灯残留一会才熄灭

 

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值