基于sysfs的LED驱动编写以及应用程序的编写
2012-05-01 11:45
423 查看
***************************************************************************************************************************
作者:EasyWave 时间:2012.05.01
类别:linux驱动开发 声明:转载,请保留链接
***************************************************************************************************************************
编写基于sysfs的驱动,在linux内核中有很多可以参考的代码,我们只需要将其移植到自己的平台上即可。由于需要在linux内核启动时,LED一直闪烁,等到linux启动完毕之后,整个LED的控制权就交给应用程序,因此是需要用到timer_list的结构体的。
一:timer_list结构体
使用时钟,先声明一个timer_list结构,调用init_timer对它进行初始化。 time_list结构里expires是标明这个时钟的周期,单位采用jiffies的单位。function就是时间到了以后的回调函数,它的参数就是timer_list中的data。 data这个参数在初始化时钟的时候赋值,一般赋给它设备的device结构指针。在预置时间到系统调用function,同时系统把这个time_list从定时队列里清除。所以如果需要一直使用定时函数,要在function里再次调用add_timer()把这个timer_list加进定时队列。
管理定时器的接口
创建定时器需要先定义它:
struct timer_list leds_report_timer;
接着需要通过一个辅助函数初始化定时器数据结构的内部值,初始化必须在使用其他定时器管理函数对定时器进行操作前完成。
init_timer(&leds_report_timer);
现在可以填充结构中需要的值了(与定时器直接相关的三个参数):
leds_report_timer.expires = jiffies + delay; // 定时器超时时的节拍数
leds_report_timer.data = 0; // 给定时器处理函数传入参数0
leds_report_timer.function = my_function; // 定时器超时时调用的函数
leds_report_timer.expires表示超时时间,它是以节拍为单位的绝对计数值。如果当前jiffies计数等于或大于leds_report_timer.expires,那么leds_report指向的处理函数就会开始执行,另外该函数还要使用长整型参数 leds_report_timer.data。如果你不需要这个参数,可以简单地传递0(或任何其他值)给处理函数。
最后,必须激活定时器:
add_timer(&leds_report_timer);
大功告成,定时器可以工作了!但请注意定时值的重要性。当前节拍计数大于或等于指定的超时值时,内核就开始执行定时器处理函数。虽然内核可以保证不会在超时时间到期前运行定时器处理函数,但是有可能延误定时器的执行 。一般来说,定时器都在超时后马上就回执行,但是也有可能被推迟到一下时钟节拍才能运行,所以不能用定时器来实现任何硬实时任务。
有时可能需要更改已经激活的定时器超时时间,所以内核通过函数mod_timer()来实现该功能,该函数可以改变指定的定时器超时时间:
mod_timer(&leds_report_timer, jiffies+new_delay);
mod_timer()函数也可操作那些已经初始化,但还没有被激活的定时器,如果定时器未被激活,mod_timer()会激活它。如果调用时定时器未被激活,该函数返回0;否则返回1。但不论哪种情况,一旦从mod_timer()函数返回,定时器 都将被激活而且设置了新的定时值。
如果需要在定时器超时前停止定时器,可以使用del_timer()函数:
del_timer(&leds_report_timer);
被激活或未被激活的定时器都可以使用该函数,如果定时器还未被激活,该函数返回0;否则返回1。注意,你不需要为已经超时的定时器调用该函数,因为它们超时后会自动被删除。
当删除定时器时,必须小心一个潜在的竞争条件。当del_timer()返回后,可以保真的只是: 定时器不会再被激活(也就是,将来不会执行),但是在多处理机器上定时器中断可能已经在其他处理器上运行了,所以删除定时器时需要等 待可能在其他处理器上运行的定时器处理程序都退出,这时就要使用del_timer_sync()函数执行删除工作:
del_timer_sync(&leds_report_timer);
和del_timer()函数不同,del_timer_sync()函数不能在中断上下文中使用。
二:LED驱动
因为我用的NUC950,而NUC950的linux2.6.35的内核中并没有包含LED的驱动程序,因此就自己写了一个基于sysfs的LED驱动,同时需要添加了reg-leds.h文件,具体代码如下:
同时还需要添加平台设备platform_device,代码如下:
最后是LED驱动代码的编写,整个代码参考了三星的LED驱动,针对NUC950修改了代码。具体的代码如下:
三:应用程序如何控制LED
就是这么简单。就可以控制sysfs的LED驱动的关与开。
作者:EasyWave 时间:2012.05.01
类别:linux驱动开发 声明:转载,请保留链接
***************************************************************************************************************************
编写基于sysfs的驱动,在linux内核中有很多可以参考的代码,我们只需要将其移植到自己的平台上即可。由于需要在linux内核启动时,LED一直闪烁,等到linux启动完毕之后,整个LED的控制权就交给应用程序,因此是需要用到timer_list的结构体的。
一:timer_list结构体
使用时钟,先声明一个timer_list结构,调用init_timer对它进行初始化。 time_list结构里expires是标明这个时钟的周期,单位采用jiffies的单位。function就是时间到了以后的回调函数,它的参数就是timer_list中的data。 data这个参数在初始化时钟的时候赋值,一般赋给它设备的device结构指针。在预置时间到系统调用function,同时系统把这个time_list从定时队列里清除。所以如果需要一直使用定时函数,要在function里再次调用add_timer()把这个timer_list加进定时队列。
管理定时器的接口
创建定时器需要先定义它:
struct timer_list leds_report_timer;
接着需要通过一个辅助函数初始化定时器数据结构的内部值,初始化必须在使用其他定时器管理函数对定时器进行操作前完成。
init_timer(&leds_report_timer);
现在可以填充结构中需要的值了(与定时器直接相关的三个参数):
leds_report_timer.expires = jiffies + delay; // 定时器超时时的节拍数
leds_report_timer.data = 0; // 给定时器处理函数传入参数0
leds_report_timer.function = my_function; // 定时器超时时调用的函数
leds_report_timer.expires表示超时时间,它是以节拍为单位的绝对计数值。如果当前jiffies计数等于或大于leds_report_timer.expires,那么leds_report指向的处理函数就会开始执行,另外该函数还要使用长整型参数 leds_report_timer.data。如果你不需要这个参数,可以简单地传递0(或任何其他值)给处理函数。
最后,必须激活定时器:
add_timer(&leds_report_timer);
大功告成,定时器可以工作了!但请注意定时值的重要性。当前节拍计数大于或等于指定的超时值时,内核就开始执行定时器处理函数。虽然内核可以保证不会在超时时间到期前运行定时器处理函数,但是有可能延误定时器的执行 。一般来说,定时器都在超时后马上就回执行,但是也有可能被推迟到一下时钟节拍才能运行,所以不能用定时器来实现任何硬实时任务。
有时可能需要更改已经激活的定时器超时时间,所以内核通过函数mod_timer()来实现该功能,该函数可以改变指定的定时器超时时间:
mod_timer(&leds_report_timer, jiffies+new_delay);
mod_timer()函数也可操作那些已经初始化,但还没有被激活的定时器,如果定时器未被激活,mod_timer()会激活它。如果调用时定时器未被激活,该函数返回0;否则返回1。但不论哪种情况,一旦从mod_timer()函数返回,定时器 都将被激活而且设置了新的定时值。
如果需要在定时器超时前停止定时器,可以使用del_timer()函数:
del_timer(&leds_report_timer);
被激活或未被激活的定时器都可以使用该函数,如果定时器还未被激活,该函数返回0;否则返回1。注意,你不需要为已经超时的定时器调用该函数,因为它们超时后会自动被删除。
当删除定时器时,必须小心一个潜在的竞争条件。当del_timer()返回后,可以保真的只是: 定时器不会再被激活(也就是,将来不会执行),但是在多处理机器上定时器中断可能已经在其他处理器上运行了,所以删除定时器时需要等 待可能在其他处理器上运行的定时器处理程序都退出,这时就要使用del_timer_sync()函数执行删除工作:
del_timer_sync(&leds_report_timer);
和del_timer()函数不同,del_timer_sync()函数不能在中断上下文中使用。
二:LED驱动
因为我用的NUC950,而NUC950的linux2.6.35的内核中并没有包含LED的驱动程序,因此就自己写了一个基于sysfs的LED驱动,同时需要添加了reg-leds.h文件,具体代码如下:
/* * arch/arm/mach-nuc900/include/mach/regs-leds.h * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation;version 2 of the License. * */ #ifndef __ASM_ARCH_REGLEDS_H #define __ASM_ARCH_REGLEDS_H #define NUC900_LEDF_ACTLOW (1<<0) /* LED is on when GPIO low */ #define NUC900_LEDF_TRISTATE (1<<1) /* tristate to turn off */ struct nuc900_led_platdata { unsigned int gpio_group; unsigned int gpio_pin; unsigned int flags; char *name; char *def_trigger; }; #endif /* __ASM_ARCH_REGLEDS_H */
同时还需要添加平台设备platform_device,代码如下:
#if defined(CONFIG_LEDS_NUC900) /* LED */ static struct nuc900_led_platdata nuc900_pdata_led = { .gpio_group = GPIO_GROUP_D, .gpio_pin = 0, //GPIOD0 .flags = NUC900_LEDF_ACTLOW, .name = "led", .def_trigger = "timer", }; static struct platform_device nuc900_device_led = { .name = "nuc900-led", .id = 0, .dev = { .platform_data = &nuc900_pdata_led, }, }; #endif //It is enable support led if you have configured the CONFIG_LEDS_NUC900.将上面的平台设备加入到平台设备的初始化列表中,即可。
#if defined(CONFIG_LEDS_NUC900) &nuc900_device_led, #endif
最后是LED驱动代码的编写,整个代码参考了三星的LED驱动,针对NUC950修改了代码。具体的代码如下:
/* drivers/leds/leds-nuc900.c * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation;version 2 of the License. * */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/gpio.h> #include <linux/slab.h> #include <mach/hardware.h> #include <mach/regs-gpio.h> #include <mach/nuc900_gpio.h> #include <mach/regs-leds.h> /* our context */ #define DEF_LEDS_DELAY1 HZ/2 #define DEF_LEDS_DELAY2 HZ/2 struct nuc900_gpio_led { struct led_classdev cdev; struct nuc900_led_platdata *pdata; }; static struct nuc900_gpio_led *leds; static struct timer_list leds_report_timer; static int blink_cnt = 0; static void nuc900_led_timer_init(void); static inline struct nuc900_gpio_led *pdev_to_gpio(struct platform_device *dev) { return platform_get_drvdata(dev); } static inline struct nuc900_gpio_led *to_gpio(struct led_classdev *led_cdev) { return container_of(led_cdev, struct nuc900_gpio_led, cdev); } static void nuc900_led_set(struct led_classdev *led_cdev, enum led_brightness value) { struct nuc900_gpio_led *led = to_gpio(led_cdev); struct nuc900_led_platdata *pd = led->pdata; /* there will be a short delay between setting the output and * going from output to input when using tristate. */ if(value) // { nuc900_led_timer_init(); } else { del_timer(&leds_report_timer); } nuc900_gpio_set(pd->gpio_group, pd->gpio_pin, (value ? 1 : 0) ^ (pd->flags & NUC900_LEDF_ACTLOW)); } static int nuc900_led_remove(struct platform_device *dev) { leds = pdev_to_gpio(dev); led_classdev_unregister(&leds->cdev); del_timer(&leds_report_timer); kfree(&leds_report_timer); kfree(leds); return 0; } static void nuc900_blink_led(enum led_brightness value) { struct nuc900_led_platdata *pd = leds->pdata; /* there will be a short delay between setting the output and * going from output to input when using tristate. */ nuc900_gpio_set(pd->gpio_group, pd->gpio_pin, (value ? 1 : 0) ^ (pd->flags & NUC900_LEDF_ACTLOW)); } static void leds_report(unsigned long data) { leds_report_timer.expires = jiffies + data; add_timer(&leds_report_timer); // printk("leds blink test.\r\n"); if(blink_cnt == 0) blink_cnt = 1; else if(blink_cnt == 1) blink_cnt = 0; nuc900_blink_led(blink_cnt); } static void nuc900_led_timer_init(void) { init_timer(&leds_report_timer); leds_report_timer.function = leds_report; leds_report_timer.data = DEF_LEDS_DELAY2; leds_report_timer.expires = jiffies + DEF_LEDS_DELAY1; add_timer(&leds_report_timer); } static int nuc900_led_probe(struct platform_device *dev) { struct nuc900_led_platdata *pdata = dev->dev.platform_data; // struct nuc900_gpio_led *led; int ret; leds = kzalloc(sizeof(struct nuc900_gpio_led), GFP_KERNEL); if (leds == NULL) { dev_err(&dev->dev, "No memory for device\n"); return -ENOMEM; } ret = nuc900_gpio_configure(pdata->gpio_group, pdata->gpio_pin); if (!ret) { printk(" leds gpio group %s registration failed\n", pdata->name); return -ENODEV; } platform_set_drvdata(dev, leds); leds->cdev.brightness_set = nuc900_led_set; leds->cdev.default_trigger = pdata->def_trigger; leds->cdev.name = pdata->name; leds->cdev.flags |= LED_CORE_SUSPENDRESUME; leds->pdata = pdata; nuc900_led_timer_init(); /* no point in having a pull-up if we are always driving */ if (pdata->flags & NUC900_LEDF_ACTLOW) { nuc900_gpio_set_output(pdata->gpio_group, pdata->gpio_pin); nuc900_gpio_set(pdata->gpio_group, pdata->gpio_pin, 0); printk("Enter nuc900 gpio set LED on.\r\n"); } /* register our new led device */ ret = led_classdev_register(&dev->dev, &leds->cdev); if (ret < 0) { dev_err(&dev->dev, "led_classdev_register failed\n"); kfree(leds); return ret; } return 0; } static struct platform_driver nuc900_led_driver = { .probe = nuc900_led_probe, .remove = nuc900_led_remove, .driver = { .name = "nuc900-led", .owner = THIS_MODULE, }, }; static int __init nuc900_led_init(void) { pr_info("NUC900 SoC LEDS Driver.\r\n"); return platform_driver_register(&nuc900_led_driver); } static void __exit nuc900_led_exit(void) { platform_driver_unregister(&nuc900_led_driver); } module_init(nuc900_led_init); module_exit(nuc900_led_exit); MODULE_AUTHOR("EasyWave <wavemcu@163.com>"); MODULE_DESCRIPTION("NUC900 LED driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:nuc900_led");
三:应用程序如何控制LED
void write_int(char *path, int i) { char buf[1024]; int fd = open(path, O_RDWR); if (fd < 0) { perror("open"); exit(1); } sprintf(buf, "%d", i); if (write(fd, buf, strlen(buf)) != strlen(buf)) { perror("write"); exit(1); } close(fd); } void set_led(int on) { write_int("/sys/devices/platform/leds-gpio/leds/brightness", on); }
就是这么简单。就可以控制sysfs的LED驱动的关与开。
相关文章推荐
- 基于sysfs的LED驱动编写以及应用程序的编写(定时器操作)
- led子系统驱动,以及此类驱动(sysfs访问方式)应用程序编写
- 基于mini2440的led驱动编写的总结
- IMX6Q学习笔记———编写LED驱动和测试程序以及相关管脚配置
- 基于TX2440开发板在ADS1.2中编写LED的驱动(GPIO的使用)裸机程序
- 基于tiny6410开发板的LED驱动 编写与使用(第一个linux驱动)
- 5.LCD驱动调用方法以及按键函数编写—基于CT117E开发板的STM32库函数编程
- IMX6Q学习笔记——编写LED驱动和测试程序以及相关管脚配置
- 基于linux的mini2440 led驱动及应用程序
- Linux下fl2440之led驱动编写以及测试
- mini2440在linux-2.6.34.2中添加LED驱动,并编写应用程序验证
- 编写基于stm32f407-uclinux下的led灯驱动
- Led驱动程序及应用程序编写
- Linux系统自带spi驱动加载及应用程序编写方法详解
- 基于Linux的v4l2视频架构驱动编写
- mini2440 led驱动代码(原创)基于iomap<测试初始化没有问题,其它功能待开发>
- 基于Linux的v4l2视频架构驱动编写
- C++基于TCP的网络应用程序的编写
- AT91SAM9263 WINCE 6.0 R2驱动开发-GPIO流驱动的实现以及应用程序的调用
- linux led驱动 应用程序