i.MX283开发板——LED子系统

前面的文章有讲过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设备,具体步骤如下:

  1. 根据硬件实现brightness_setbrightness_get方法;
  2. 定义一个led_classdev结构体,并填充主要成员;
  3. 编写init函数,注册LED设备;
  4. 编写exit函数,注销设备。

led_subsystem.c:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>

#include <mach/hardware.h>
#include <mach/system.h>
#include <mach/device.h>
#include <mach/regs-pwm.h>
#include <linux/gpio.h>

#include <../arch/arm/mach-mx28/mx28_pins.h>

#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子系统

猜你喜欢

转载自blog.csdn.net/qq_24835087/article/details/104388879