.3背光驱动的实现
本RSU系统使用一个通用的背光层驱动和用户及内核的其他模块交互。该背光驱动作为PWM模块和用户/内核其他模块交互的中间层,接收来自用户或内核模块的命令,然后转化为对PWM的控制命令,如开启、关闭、调整等。
在用户接口方面,使用Linux 2.6内核特有的sysfs文件系统,实现内核与用户间的交互。Sysfs是一个可读写的文件系统,通过提供一些变量,内核输出设备的属性,如LCD背光的当前亮度值,最大亮度值等。用户可以通过改变这些变量的值,设置设备的属性。Sysfs文件系统提供的用户接口如下:
/* interface for exporting device attributes */
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
};
其中,attr是设备的标识,show和store是两个函数指针。内核通过show向用户输出设备属性,用户层则通过store设置设备的属性。在LCD背光驱动中,定义了以下结构:
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
static struct device_attribute bl_device_attributes[] = {
__ATTR(bl_power, 0644, backlight_show_power, backlight_store_power),
__ATTR(brightness, 0644, backlight_show_brightness, backlight_store_brightness),
__ATTR(actual_brightness, 0444, backlight_show_actual_brightness, NULL),
__ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
__ATTR_NULL,
};
如上,系统向用户层输出bl_power,brightness,actual_brightness,max_brightness四个属性值,其中前两个是可写的,后两个是只读的。例如,内核通过backlight_show_power向用户层输出属性bl_power的值。通过backlight_store_power接受来自用户层的输入值,然后将其设置为bl_power属性的当前值。
通过sysfs文件系统,本手持RSU系统为用户层提供两种背光调整方式。一为亮度等级的调整(bl_power),值为0~4,0为高亮,1、2、3次之,4为全灭。另一种是线性调整方式(brightness),其值为从0~TCNTB,其值越大,背光的亮度越高。
本系统通过通知链表(Notifier Chain)[8]技术,实现LCD背光驱动与内核其他模块(如LCD)的交互。通知链表是内核模块间的一种通讯方式,它的工作方式和中断类似。一旦某个模块有事件发生,它便通知已经注册到这个模块链表的其他模块。这样,其他模块就可以根据所发生的事件,调用相应的处理例程。
首先定义一个通知链表。LCD驱动中定义的通知链表如下:
static BLOCKING_NOTIFIER_HEAD(fb_notifier_list);
接着,外部模块注册到这个通知链表中,背光驱动注册如下:
blocking_notifier_chain_register(&fb_notifier_list, nb);
其中,参数nb是如下一个结构,由外部模块提供:
struct notifier_block {
int (*notifier_call)(struct notifier_block *, unsigned long, void *);
struct notifier_block *next;
int priority;
};
完成注册操作后,一旦LCD模块有事件发生,它便会以该事件(even)作为参数,调用如下函数:
blocking_notifier_call_chain(&fb_notifier_list, val, v);
该函数遍历在这个链表中注册过的notifier_block,并执行其中的处理例程。例如,当LCD发生UNBLANK事件时,它遍历相应的通知链表,执行背光系统注册的处理例程,将升压开关调整器的使能端置为0,关闭背光的输出。
综上,整个背光的控制流程如图4所示。
图4 LCD背光控制的框图
通过测试,可以得到如表2所示的和背光能耗相关一些数值。
表2 LCD功耗列表
TCMPB1
(TCNB1)
电流
(mA)
电压
(V)
功率
(mW)
1
113
20.0
2260.0
3/4
83
19.1
1585.3
1/2
55
18.0
990.0
1/4
23
16.9
388.7
1/8
10
16.2
162.0
1/16
5
15.9
79.5
在实际使用的过程中,设置1/2值的亮度即可,只有在特殊的场合才需要设置为高亮。而在系统等待的时候,可以将其值设置为1/16,这时背光的功耗只有79mW,约为最大功耗的1/28,大大节省了能耗。