在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);

在Linux驱动中使用LED子系统的更多相关文章

  1. Linux内核中SPI/I2c子系统剖析

    Linux内核中,SPI和I2C两个子系统的软件架构是一致的,且Linux内核的驱动模型都以bus,driver,device三种抽象对象为基本元素构建起来.下文的分析将主要用这三种抽象对象的创建过程 ...

  2. 【Linux驱动】TQ2440 LED驱动程序

    ★整体介绍 LED驱动程序主要实现了TQ2440开发板上的4个LED灯的硬件驱动,实现了对引脚GPIOB5.GPIOB6.GPIOB7.GPIOB8的高低电平设置(common-smdk.c中已经实现 ...

  3. 树莓派linux驱动学习之LED控制

    前面我们编写了hello world的程序,接下来继续研究GPIO功能,通过GPIO来控制LED的亮灭,这在单片机中应该算是十分简单的一个程序了,但是在Linux系统中控制GPIO没有那么简单,难点就 ...

  4. Linux 驱动——Button8(输入子系统)

    输入子系统由驱动层.输入子系统核心.事件处理层三部分组成.一个输入事件,如鼠标移动.键盘按下等通过Driver->Inputcore->Event handler->userspac ...

  5. 超简单易用的 “在 pcduino 开发板上写 Linux 驱动控制板载 LED 的闪烁”

    版权声明:本文为博主原创文章,未经博主同意不得转载.转载联系 QQ 30952589,加好友请注明来意. https://blog.csdn.net/sleks/article/details/251 ...

  6. Linux驱动架构之pinctrl子系统分析(一)

    1.前言在嵌入式系统中,许多SoC的内部都包含了pin控制器,通过芯片内部的pin控制器,我们可以配置一个或者一组引脚的状态和功能特性,Linux内核为了统一各SoC厂商的引脚管理,提供了pinctr ...

  7. Linux驱动开发之LED驱动

    首先讲下字符设备控制技术 : 大部分驱动程序除了需要提供读写设备的能力外,还需要具备控制设备的能力.比如: 改变波特率. 在用户空间,使用ioctl系统调用来控制设备,原型如下:int ioctl(i ...

  8. Linux驱动中的EPROBE_DEFER是个啥

    ​Linux kernel 驱动中,有不少驱动会引用到 EPROBE_DEFER 这个错误号.比如下面这个例子,对 devm_gpiod_get 的返回值进行判断,如果有错误且错误号不是 -EPRBO ...

  9. linux驱动中printk的使用注意事项

    今天在按键驱动中增加printk(KERN_INFO "gpio_keys_gpio_isr()\n");在驱动加载阶段可以输出调试信息,但驱动加载起来后的信息,在串口端看不到输出 ...

  10. Linux驱动中completion接口浅析(wait_for_complete例子,很好)【转】

    转自:http://blog.csdn.net/batoom/article/details/6298267 completion是一种轻量级的机制,它允许一个线程告诉另一个线程工作已经完成.可以利用 ...

随机推荐

  1. go语言package使用

    近期接触go感觉package包之间引用很麻烦,很绕圈子.下面一起理一理这个package咋用 关于package: 1.不限于一个文件,可以多个文件组成一个package 2.不要求package的 ...

  2. Java根据URL截图的4种方式

    方案选择 XHTMLRenderer(不要用) PhantomJs(三方库,已停更) Puppeteer(Chrome团队开发和维护) Selenium(支持多浏览器.多语言,服务器需要安谷歌浏览器) ...

  3. 2023 Stack Overflow 调研

    一.Programming, scripting, and markup languages 二.Databases 三.Web frameworks and technologies 四.Other ...

  4. 前端JavaScript开发风格规范

    开发者需要建立和遵守的规范 大致可以划分成这几个方向: 开发流程规范 代码规范 git commit规范 项目文件结构规范 UI设计规范 1. 开发流程规范 这里可能有小伙伴有疑问了,开发流程规范不是 ...

  5. vue2下拉框组件使用技巧

    1.ant design 下拉框组件--单选 <span style="font-size: 14px;">污水厂</span> <a-select ...

  6. 5GC 关键技术之 CUPS(控制与用户面分离)

    目录 文章目录 目录 前文列表 CUPS(控制与用户面分离) 前文列表 <简述移动通信网络的演进之路> <5G 第五代移动通信网络> <5GC 关键技术之 SBA(基于服 ...

  7. Vue cli传递数据

    Nav组件和Forecast组件都是Home组件的子组件. (1)子组件传递数据给父组件 在子组件中,通过this.$emit('自定义事件名', 参数1,参数2,...)来调用父组件中定义的事件. ...

  8. springboot~封装依赖引用包jar还是pom,哪种更规范

    将多个第三方包封装成一个项目后,如果你的目的是让其他开发人员可以直接引用这些依赖,一般来说有两种常见的方式: 打成JAR包:将封装好的项目编译打包成JAR文件,其他开发人员可以将这个JAR文件添加到他 ...

  9. 薄书的pytorch项目实战lesson49-情感分类+蹭免费GPU

    项目来源 B站视频pytorch项目实战-情感分类问题 github lesson49-情感分类实战 1 实验环境 在这里和大家推荐一个学习ML和DL的一个实验运行平台,就是google的Colabo ...

  10. Advanced .Net Debugging 9:平台互用性

    一.介绍 这是我的<Advanced .Net Debugging>这个系列的第九篇文章.这篇文章的内容是原书的第二部分的[调试实战]的第七章[互用性].互用性包含两个方面,第一个方面就是 ...