在Linux驱动中使用LED子系统

在Linux驱动中使用LED子系统

原文:https://blog.csdn.net/hanp_linux/article/details/79037684

前提配置device driver下面的LED Support和它下面的LED class support及相应的trigger打开。

步骤

编写设备树(可选)

类似高通平台的方案。

    qcom,gpio-leds {
        compatible = "gpio-leds";
        led-blue{
            label = "red";
            default-state = "off";
            linux,default-trigger = "none";//没有默认的触发源,也可以写为timer 
            gpios = <&msm_gpio 17 0x00>;
        };
        led-green{
            label = "green";
            default-state = "on";
            gpios = <&msm_gpio 34 0x00>;
        };
    };

分配led_classdev实例以及初始化

一般在init或者probe中实现这个。

     static struct led_classdev *led_devs;
     led_devs = kzalloc(sizeof(struct led_classdev), GFP_KERNEL);
     if (led_devs == NULL)
     {                    
         printk("alex.han %s:%d led_devs alloc error
", __func__, __LINE__);
         return -1;       
     }

     //设置led的最大亮度  LED_FULL在leds.h中定义,为255(有些led是可以通过控制电流来控制亮度的,)
     led_devs->max_brightness = LED_FULL;
     //设置led的默认亮度,LED_HALF在leds.h中定义,为127,如果不设置默认为0
     led_devs->brightness = LED_HALF;
     led_devs->flags = LED_CORE_SUSPENDRESUME;
     //这个led设备的名字,注册后将会在/sys/class/leds/目录下创建xxx设备目录
     led_devs->name = "xxx";
     //设置默认的trigger,如果不设置则默认trigger为0, 如果不需要trigger,这个地方可以不设置
     led_devs->default_trigger = "timer"; //默认trigger为timer
     //设置亮度的函数,当我们通过sys文件系统来调节led亮度的时候,会调用这个函数,当我们设置了trigger,对应的trigger也会调用这个函数
     led_devs->brightness_set = my_brightness_set;
     //delay_on和delay_off表示默认led闪烁的频率,只有在使用timer这个trigger的时候才有效,表示led亮的时间和灭的时间,从而来控制闪烁频率,单位是ms
     led_devs->blink_delay_on = 1000;
     led_devs->blink_delay_off = 2000;
     //设置闪烁时led的亮度
     led_devs->blink_brightness = 100;

实现亮度调节函数

static void my_brightness_set(struct led_classdev * led_cdev, enum led_brightness brightness)
{                                          
  struct led_device * dev = (struct led_device *)led_cdev;
  led_cdev->brightness = brightness;     

  printk("alex.han %s %d brightness = %d gpio = %d
", __func__, __LINE__, brightness, dev->gpio);
  /*
     这个地方要实现你自己的的led设备的亮和灭或者是设置亮度操作
     比如:
      如果你的led设备是用一个gpio进行简单控制,那么这个地方对你来说brightness就是亮和灭的开个,brightness=0就设置灯亮,否则就设置led灭
      如果你的led设备使用一个中间芯片来控制的(比如lp5523,可以通过iic控制lp5523芯片从而来控制led的亮度),同时又是通过控制电流来控制亮度,那么就需要调用i2c_write将需要设置的内容写到对应的芯片中,
  */
}

注册这个结构体

//调用led_class.c中的注册函数,将初始化的led_classdev结构体注册到led子系统中,创建对应的设备节点
led_classdev_register(NULL, led_devs);

测试

将上述框架添加到一个模块中,编译到kernel中,并make menuconfig打开相应的宏,重新烧写image。

进入/sys/class/目录会发现有leds目录,进入leds目录会发现我们注册的xxx设备,进入xxx目录会发现有brightness max_brightness trigger等属性

cat brightness #会打印出我们设置的默认的brightness值, 
echo 100 > brightness #根据log会发现我们驱动的my_brightness_set函数被调用, 

关于 trigger,如果你在make menuconfig去将相应的trigger添加的话,cat trigger 会发现打印出很多的触发器。此时,对应触发器前面如果有[]代表当前使用的trigger。

如果在none的这个触发器上加了[],表示我们当前没有添加触发器,

这时如果你echo timer > trigger然后cat trigger会发现[]加在了timer上面,表示当前的触发器是timer,并且在当前目录下生成了delay_on和delay_off两个文件。

分别cat会发现打印的值和我们设置的值一样,同时看log会发现我们的my_brightness_set函数被不断的调用。

最后附上我自己的实例代码,虚拟了4个led:

/*************************************************************************
    > File Name: led-test.c
    > Author: 
    > Mail: 
    > Created Time: 2018年01月02日 星期二 18时37分17秒
 ************************************************************************/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/leds.h>

struct led_desc {
    int gpio;
    char * name;
};

/* 虚拟了4个led */
static struct led_desc led_gpios[] = {
    {1, "led1"},
    {2, "led2"},
    {3, "led3"},
};

struct led_device {
    struct led_classdev cdev;
    int gpio;
};

static struct led_device * led_devs = NULL;

static void my_brightness_set(struct led_classdev * led_cdev, enum led_brightness brightness)
{
    struct led_device * dev = (struct led_device *)led_cdev;
    led_cdev->brightness = brightness;

    printk("alex.han %s %d brightness = %d gpio = %d
", __func__, __LINE__, brightness, dev->gpio);
}

static int myled_init(void)
{
    int i;
    int ret;
    printk("alex.han %s %d
", __func__, __LINE__);

    led_devs = kzalloc(sizeof(struct led_device) * sizeof(led_gpios) / sizeof(led_gpios[0]), GFP_KERNEL);
    if (led_devs == NULL)
    {
        printk("alex.han %s:%d led_devs alloc error
", __func__, __LINE__);
        return -1;
    }

    for (i = 0; i < (sizeof(led_gpios) / sizeof(led_gpios[0])); i++)
    {
        led_devs[i].cdev.max_brightness = LED_FULL;
        led_devs[i].cdev.brightness = LED_HALF;
        led_devs[i].cdev.flags = LED_CORE_SUSPENDRESUME;
        led_devs[i].cdev.name = led_gpios[i].name;
        led_devs[i].cdev.default_trigger = "timer"; //默认trigger为timer
        led_devs[i].gpio = led_gpios[i].gpio; // gpio端口号
        led_devs[i].cdev.brightness_set = my_brightness_set;
        led_devs[i].cdev.blink_delay_on = 1000;
        led_devs[i].cdev.blink_delay_off = 2000;
        led_devs[i].cdev.blink_brightness = 100;

        ret = led_classdev_register(NULL, &led_devs[i].cdev);
        if (ret < 0)
        {
            i--;
            while (i >= 0)
            {
                i--;

                printk("alex.han %s %d register err
", __func__, __LINE__);
                led_classdev_unregister(&led_devs[i].cdev);
            }
            kfree(led_devs);

            return -1;
        }
    }

    return 0;
}

static void myled_exit(void)
{
    int i;
    for (i = 0; i < (sizeof(led_gpios) / sizeof(led_gpios[0])); i++)
    {
        led_classdev_unregister(&led_devs[i].cdev);
    }

    kfree(led_devs);
}

module_init(myled_init);
module_exit(myled_exit);
如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
博客地址:https://www.cnblogs.com/schips/
原文地址:https://www.cnblogs.com/schips/p/using_linux_kernel_led_sub_system.html