1. 程式人生 > 其它 >在Linux驅動中使用LED子系統

在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\n", __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\n", __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\n", __func__, __LINE__, brightness, dev->gpio);
}

static int myled_init(void)
{
    int i;
    int ret;
    printk("alex.han %s %d\n", __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\n", __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\n", __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);