在Linux驅動中使用LED子系統
阿新 • • 發佈:2021-06-15
在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);