前面的文章有讲过LED字符设备驱动,用户可以open “/dev/xxxLED”驱动文件,通过write或者ioctl接口去访问LED设备,实际
上,在Linux中,控制LED还有一种简便的方式,它不需要用户写程序,用户通过几个指令就可以控制,而且功能十分强大。这
就是本文接下来要讲的LED子系统。
LED 子系统的可以分为三部分:触发器、LED 设备和核心模块,如下图所示:
LED核心模块:管理LED设备和触发器,并为LED设备和触发器提供注册和注销接口。 LED设备:具体的设备,需要提供LED的控制接口。 触发器:LED触发方式,内核提供了none、mmc、nand-disk、heartbeat、timer等触发方式,none表示无触发,mmc表示插入SD卡时会触发LED,这些触发器可在配置内核时开启。 属性文件:LED 类会为每个挂在它下面的LED设备自动创建属性文件,用户操作这些属性文件就可以控制LED。LED子系统框架以及与用户层对应关系如下图所示:
LED子系统的代码位于内核源码/driver/leds文件夹下,其中:
led-class.c:是LED子系统的核心代码,其作用有两个:
维护 LED 子系统的所有触发器,为触发器的注册/注销提供操作函数; 维护 LED 子系统的所有 LED 设备,并为每个 LED 设备在/sys/class/leds/目录下实现操作接口;为 LED 设备的注册/注销提供操作函数。ledtrig-*.c:触发器的代码,例如 ledtrig-heartbeat.c 文件是心跳触发器的代码文件。这些触发器的代码文件的主要任务是初始化各自的触发器,然后注册到核心模块。
LED子系统还有个非常重要的结构体struct led_classdev:
struct led_classdev {
const char *name;
int brightness;
int max_brightness;
int flags;
/* Lower 16 bits reflect status */
#define LED_SUSPENDED (1 << 0)
/* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME (1 << 16)
/* Set LED brightness level */
/* Must not sleep, use a workqueue if needed */
void (*brightness_set)(struct led_classdev *led_cdev,
enum led_brightness brightness);
/* Get LED brightness level */
enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
/* Activate hardware accelerated blink, delays are in
* miliseconds and if none is provided then a sensible default
* should be chosen. The call can adjust the timings if it can't
* match the values specified exactly. */
int (*blink_set)(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off);
struct device *dev;
struct list_head node; /* LED Device list */
const char *default_trigger; /* Trigger to use */
#ifdef CONFIG_LEDS_TRIGGERS
/* Protects the trigger data below */
struct rw_semaphore trigger_lock;
struct led_trigger *trigger;
struct list_head trig_list;
void *trigger_data;
#endif
};
我们就是通过这个结构体描述一个具体的LED设备,然后利用led-class.c中提供的注册方法,把LED设备注册到内核中去。
下图是led_classdev结构体主要成员功能说明:
LED设备子系统的调用流程如下:
注意:上图提到的show、store、brightness_set和brightness_get都是函数指针,其中show和store方法在led-class中已经
实现,它们最终调用的都是brightness_get或者brightness_set方法,这两个函数需要我们根据硬件去实现。
下面就开始编写一个LED设备,具体步骤如下:
根据硬件实现brightness_set和brightness_get方法;
定义一个led_classdev结构体,并填充主要成员;
编写init函数,注册LED设备;
编写exit函数,注销设备。
led_subsystem.c:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LED_GPIO MXS_PIN_TO_GPIO(PINID_LCD_D23)
void mybrightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
gpio_direction_output(LED_GPIO, !brightness);
}
static struct led_classdev myled_dev =
{
.name = "myled",
.brightness_set = mybrightness_set,
.max_brightness = LED_FULL,
.default_trigger = "none", //触发器设置无
};
static int __init led_subsys_init(void)
{
int ret;
ret = led_classdev_register(NULL,&myled_dev);
if(ret < 0)
{
printk("led_classdev_register error %d \n",ret);
return ret;
}
gpio_free(LED_GPIO);
ret = gpio_request(LED_GPIO,"LED");
if(ret < 0)
{
printk("gpio_request error %d \n",ret);
led_classdev_unregister(&myled_dev);
return ret;
}
return 0;
}
static void __exit led_subsys_exit(void)
{
gpio_free(LED_GPIO);
led_classdev_unregister(&myled_dev);
}
module_init(led_subsys_init);
module_exit(led_subsys_exit);
MODULE_AUTHOR("xzx2020");
MODULE_DESCRIPTION("led subsystem test");
MODULE_LICENSE("GPL");
由于开发板上的led是直接接IO口的,LED的状态也只有亮和不亮两种状态,所以也就不存在设置亮度和获取亮度这一说,brightness_get不需要实现,brightness_set控制IO口拉低或者拉高即可,而且这里没有设置任何触发器。
将led_subsystem.c编译成.ko文件,在开发板上加载mod。
加载成功后,/sys/class/leds下面应该有我们刚刚添加的led设备,名字就是上面结构体的name字段——“myled”,进入myled目录下:
echo 1 > brightness //点亮LED
echo 0 > brightness //熄灭LED
cat brightness //查看亮度值
cat max_brightness //查看最大亮度
cat trigger //查看触发器
上面的例子是没有使用触发器的,下面就讲下触发器的使用。
1.首先需要配置内核,开启相关触发器。
make menuconfig ------> Device Drivers ------->LED support ---------> LED Triggers
开启LED Timer Trigger 和 LED Heartbeat Trigger
↵
2.为我们的LED添加触发器
方法一:修改led_classdev 结构体中default_trigger 字段,可选配置为“timer” 、“heartbeat”。
static struct led_classdev myled_dev =
{
.name = "myled",
.brightness_set = mybrightness_set,
.max_brightness = LED_FULL,
.default_trigger = "none", //触发器设置无
};
方法二:用户层通过trigger属性修改
echo heartbeat > trigger //系统心跳触发
echo timer > trigger //定时器来控制闪烁
echo mmc > trigger //SD卡触发
如果选择timer触发,也就是通过软件定时器控制LED,myled下会生成delay_on和delay_off两个文件,分别表示LED亮和灭的
时间,单位是毫秒。可以通过 echo 指令向这两个文件写入时间值来控制LED闪烁。但是如果实现了led_classdev 结构体中
blink_set函数时,就会执行blink_set函数控制LED闪烁。
参考文档和一些写的比较好的LED子系统的帖子:
1.周立功LED 子系统驱动简介
2.(linux)LED子系统
作者:Ruler.