在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. 如何拥有自己的专属GPT-本地部署目前最强大模型llama3

    你是不是苦于没法使用ChatGPT?或者访问了ChatGPT却没法使用GPT4?现在一切问题都可以解决了! 4月18日,Meta发布两款开源Llama 3 8B与Llama 3 70B模型,供外部开发 ...

  2. SQL语法之:连表查询:union all

    1.准备 两条sql查询出来的字段数必须一致 表1 字段: 数据: 表2 字段: 数据: 2.使用 1.两张表结构完全一样,查询字段顺序也一样 select ID,NAME,SEX,AGE,NAME2 ...

  3. Hive中的FileFormat、RowFormat和SerDe总结

    Hive如何读写数据? 我们知道,hive表的数据是存储在hdfs文件系统中的.那么Hive是如何将hdfs上的数据文件,映射成一张张表呢,今天就来理清楚这个问题. 官方文档中对于Hive读数据的流程 ...

  4. ansible(14)--ansible的get_url模块

    1. get_url模块 功能:通过互联网下载软件至被控端本地: 主要参数如下: 参数 说明 url 资源文件在互联网上的具体url地址 dest 文件下载位置的绝对路径 mode 文件下载位置的绝对 ...

  5. AIRIOT物联网平台助力油库自动化升级 实现业务场景全覆盖

      随着我国石油工业的飞速发展,油库规模迅速扩大,油库系统逐渐完善起来.石油行业属于高风险行业,所以石油化工产品在储存.运输和生产各个环节,均有极高的安监.环保.应急的管理要求.通常情况下,油库容量. ...

  6. AIRIOT答疑第6期|如何使用二次开发引擎?

    ​​灵活扩展,满足客户定制化需求   AIRIOT物联网低代码平台提供丰富的前端.后台服务二次开发接口,具备灵活的组件服务部署与管理能力,对任何功能模块进行二次开发,满足客户各类二次开发需求.支持多种 ...

  7. 【PB案例学习笔记】-02 目录浏览器

    写在前面 这是PB案例学习笔记系列文章的第二篇,该系列文章适合具有一定PB基础的读者, 通过一个个由浅入深的编程实战案例学习,提高编程技巧,以保证小伙伴们能应付公司的各种开发需求. 文章中设计到的源码 ...

  8. WPF自定义控件,如何使得xaml涉及器中的修改能立即反应到预览

    这是我无意中发现的,xaml中设置的是依赖属性而不是包装器,所以我们可以直接在注册依赖属性那里设置回调,触发某个控件重绘,比如本身或父控件重绘. xaml设计器就会实时更新 1 // !!!由于xam ...

  9. 一文了解npm install -g和npm install --save-dev的关系

    本文分享自华为云社区<npm install -g 和 npm install --save-dev 的关系>,作者: SHQ5785. 一.npm install 本地安装 将安装包放在 ...

  10. 安装node-sass失败原因及解决办法汇总

    node-sass 安装过程 npm 拉下 node-sass包: 根据node版本和node-sass版本拉取对应的binding.node编译器,原因是sass的编译语言比较特殊,需要下载对应版本 ...