请选择 进入手机版 | 继续访问电脑版
    查看: 206|回复: 0

    Linux下的LED子系统

    [复制链接]

    990

    主题

    1057

    帖子

    3640

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    3640
    基情
    1807
    发表于 2016-9-18 15:32:11 | 显示全部楼层 |阅读模式
    版权所有,转载必须说明转自 http://my.csdn.net/weiqing1981127



    最简单的led驱动就是从端口输出0或1来关闭或点亮灯。而我们这里讲的led子系统,主要是对led事件进行了分装和优化,这里我们主要讲的是可以实现跨平台的led驱动。不管你是使用三星的平台,还是Atmel的平台,你只要知道如何在你的BSP中添加平台数据,并且知道如何在应用程序中使用这个驱动,那么你就不用因为新的平台而再次编写led驱动。
    按键驱动属于input子系统,源码路径在/driver/leds下,我们的跨平台按键驱动文件是/driver/leds/leds-gpio.c,关于led子系统的核心文件是Led-class.c和Led-core.c
    查看/driver/leds/Makefile
    obj-$(CONFIG_LEDS_GPIO)                    += leds-gpio.o
    查看/driver/leds/Konfig
    config LEDS_GPIO
           tristate"LED Support for GPIO connected LEDs"
           dependson LEDS_CLASS && GENERIC_GPIO
    其中
    config LEDS_CLASS
           tristate"LED Class Support"
    所以配置内核makemenuconfig 时,需要选中这两项。

    现在先来看如何移植,比如我们现在要给mini2440开发板上的led1到key4编写led驱动,根据资料知道,led1到led4用的是GPB5-GPB8端口。下面就看移植代码了,在mach-mini2440.c这个mini2440开发板的BSP中添加如下代码
    static struct gpio_led s3c_gpio_leds[] = {
           {
                  .name                    = "led1",
                  .gpio                     = S3C2410_GPB(5),
                  .active_low= 1,
           },
           {
                  .name                    = "led2",
                  .gpio                     = S3C2410_GPB(6),
                  .active_low= 1,
           },
           {
                  .name                    = "led3",
                  .gpio                     = S3C2410_GPB(7),
                  .active_low= 1,
           },
           {
                  .name                    = "led4",
                  .gpio                     = S3C2410_GPB(8),
                  .active_low= 1,
           },
    };

    static struct gpio_led_platform_datas3c_gpio_led_data = {
           .leds        = s3c_gpio_leds,
           .num_leds       = ARRAY_SIZE(s3c_gpio_leds),
    };

    static struct platform_device s3c_leds_gpio= {
           .name      = "leds-gpio",
           .id    = -1,
           .dev = {
                  .platform_data = &s3c_gpio_led_data,
           },
    };
    然后把这个s3c_leds_gpio加入到mini2440_devices数组
    static struct platform_device*mini2440_devices[] __initdata = {
           ……
           &s3c_leds_gpio, //添加
    };
    最后添加头文件
    #include <linux/leds.h>
    这样配置完后,进行makezImage生成zImage内核镜像。

    下面大致说说/driver/leds/leds-gpio.c
    直接看平台驱动定义
    static struct platform_drivergpio_led_driver = {
           .probe            = gpio_led_probe,  //探测
           .remove          = __devexit_p(gpio_led_remove),
           .driver            = {
                  .name      = "leds-gpio",  //驱动名
                  .owner    = THIS_MODULE,
           },
    };
    下面看probe探测函数
    static int __devinit gpio_led_probe(structplatform_device *pdev)
    {
           structgpio_led_platform_data *pdata = pdev->dev.platform_data;
           structgpio_led_data *leds_data;
           inti, ret = 0;
           if(!pdata)
                  return-EBUSY;
           leds_data= kzalloc(sizeof(struct gpio_led_data) * pdata->num_leds,  //分配空间
                                GFP_KERNEL);
           if(!leds_data)
                  return-ENOMEM;
           for(i = 0; i < pdata->num_leds; i&#43;&#43;) {
                  ret= create_gpio_led(&pdata->leds, &leds_data,
                                      &pdev->dev,pdata->gpio_blink_set);  //创建led设备
                  if(ret < 0)
                         gotoerr;
           }
           platform_set_drvdata(pdev,leds_data);
           return0;
    err:
           for(i = i - 1; i >= 0; i--)
                  delete_gpio_led(&leds_data);
           kfree(leds_data);
           returnret;
    }
    继续跟踪probe中的create_gpio_led函数
    static int __devinit create_gpio_led(conststruct gpio_led *template,
           structgpio_led_data *led_dat, struct device *parent,
           int(*blink_set)(unsigned, unsigned long *, unsigned long *))
    {
           intret, state;
           led_dat->gpio= -1;
           if(!gpio_is_valid(template->gpio)) {
                  printk(KERN_INFO"Skipping unavailable LED gpio %d (%s)\n",
                                template->gpio,template->name);
                  return0;
           }
           ret= gpio_request(template->gpio, template->name);
           if(ret < 0)
                  returnret;
           led_dat->cdev.name= template->name;
           led_dat->cdev.default_trigger= template->default_trigger;
           led_dat->gpio= template->gpio;
           led_dat->can_sleep= gpio_cansleep(template->gpio);
           led_dat->active_low= template->active_low;
           if(blink_set) {
                  led_dat->platform_gpio_blink_set= blink_set;
                  led_dat->cdev.blink_set= gpio_blink_set;   //定义函数
           }
           led_dat->cdev.brightness_set= gpio_led_set;  //定义函数
           if(template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
                  state= !!gpio_get_value(led_dat->gpio) ^ led_dat->active_low;
           else
                  state= (template->default_state == LEDS_GPIO_DEFSTATE_ON);
           led_dat->cdev.brightness= state ? LED_FULL : LED_OFF;
           if(!template->retain_state_suspended)
                  led_dat->cdev.flags|= LED_CORE_SUSPENDRESUME;
           ret= gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
           if(ret < 0)
                  gotoerr;
           INIT_WORK(&led_dat->work,gpio_led_work);  //初始化工作队列
           ret= led_classdev_register(parent, &led_dat->cdev);  //向leds类中注册设备
           if(ret < 0)
                  gotoerr;
           return0;
    err:
           gpio_free(led_dat->gpio);
           returnret;
    }

    讲到这,让我们恍然明白,这个leds子系统跟之前分析过的backlight背光子系统是非常类似的。backlight背光子系统在核心层定义了一套device_attribute机制,并且定义了操作backlight背光的操作函数集的接口,同时在device_attribute的show和store属性中会调用backlight背光的操作函数集。然后我们使用这个backlight背光子系统核心层,编写自己的驱动时,只需要向backlight背光子系统注册设备,并填充backlight背光的操作函数集即可。用户层只需要通过echo向brightness中写数字触发store属性,或者通过cat向brightness中读数字触发show属性。
    再回过头来看看我们这个leds子系统,在create_gpio_led函数中有这样一行代码led_dat->cdev.brightness_set= gpio_led_set;其中的函数gpio_led_set是设置灯亮灭的函数,这就是相当于我们给led子系统的操作函数赋值,跟踪led_classdev_register函数,你会发现在那里会看到定义了leds类属性,并创建了device_attribute机制,同时同时在device_attribute的store属性中会调用我们这里定义的的操作函数gpio_led_set。
    所以,leds子系统的源码我就分析到这里了,我在学习backlight子系统的时候,就细细分析过这样一个类里包含设备属性,以及设备操作函数,这跟leds-gpio.c代码原理类似,如果需要可以参考。

    LED驱动测试
    用户可以先通过cd/sys/class/leds打开led子系统下的设备
    然后应用层通过访问/sys/class/leds/led1来设置等的亮灭
    点亮led1:
    echo 1 > /sys/class/leds/led1
    关闭led1:
    echo 0 > /sys/class/leds/led1


    上一篇:Linux下的RTC子系统
    下一篇:Linux下的Keyboard子系统
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|小黑屋|网站地图|DZ商业模板|VR福利资源|嵌入式Linux论坛 ( 粤ICP备15085165号-2

    GMT+8, 2017-2-28 13:30 , Processed in 0.709041 second(s), 45 queries .

    Powered by 深嵌论坛 X3.2

    © 2001-2013 Comsenz Inc.

    快速回复 返回顶部 返回列表