颜色问题"/>
记录:闪烁灯的颜色问题
【问题背景】
在手机来电通知等场景下,灯要设置为白色闪烁
一、代码流程分析
上层代码:
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_classdev的brightness_get,反之echo跑brightness_set,brightness_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 的设置问题
且在进去之前设置好,不会影响闪烁功能函数的运行时间,因为有通过字符串比较的逻辑,这个会影响效率,导致出现三个灯没能一起亮或者没有一起灭,就可能出现白灯灭了后还有别的颜色,比如白灯灭了后还有蓝色灯残留一会才熄灭
更多推荐
记录:闪烁灯的颜色问题
发布评论