十三、(正点原子)Linux自带的LED灯驱动

        Linux 内核已经集成了像 LED 灯这样非常基础的设备驱动。Linux 内核的 LED 灯驱动采用platform 框架,因此我们只需要按照要求在设备树文件中添加相应的 LED 节点即可。

一、Linux内核自带LED驱动使能

        要使用 Linux 内核自带的 LED 灯驱动首先得先配置 Linux 内核,使能自带的 LED 灯驱动,输入如下命令打开 Linux 配置菜单:

make menuconfig

        按照如下路径打开 LED 驱动配置项:

-> Device Drivers-> LED Support (NEW_LEDS [=y])->LED Support for GPIO connected LEDs

        按照上述路径,选择“LED Support for GPIO connected LEDs”,将其编译进 Linux 内核,也即是在此选项上按下“Y”键,使此选项前面变为“<*>”,如图:

        在“LED Support for GPIO connected LEDs”上按下‘?’ 可以打开此选项的帮助信息:

         把 Linux 内 部 自 带 的 LED 灯 驱 动 编 译 进 内 核 以 后 ,CONFIG_LEDS_GPIO 就会等于‘y’, Linux 会根据 CONFIG_LEDS_GPIO 的值来选择如何编译LED 灯驱动,如果为‘y’就将其编译进 Linux 内核。
        配置好 Linux 内核以后退出配置界面,打开.config 文件,会找到“CONFIG_LEDS_GPIO=y”这一行,如图所示:

        重新编译 Linux 内核,然后使用新编译出来的 zImage 镜像启动开发板,开发板内就会LED灯驱动。

二、Linux内核自带LED驱动简介

        1、LED灯驱动框架分析

        LED 灯驱动文件为/drivers/leds/leds-gpio.c,可以打开/drivers/leds/Makefile 这个文件,找到如下所示内容:

# LED Core
obj-$(CONFIG_NEW_LEDS) += led-core.o
.....
obj-$(CONFIG_LEDS_GPIO_REGISTER) += leds-gpio-register.o
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
2obj-$(CONFIG_LEDS_LP3944) += leds-lp3944.o
......

        如果定义了 CONFIG_LEDS_GPIO 的话就会编译 leds-gpio.c 这个文件,选择将 LED 驱动编译进 Linux 内核,在.config 文件中就会有“CONFIG_LEDS_GPIO=y”这一行,因此 leds-gpio.c 驱动文件就会被编译。
        接下来我们看一下 leds-gpio.c 这个驱动文件,找到驱动注册函数:

static const struct of_device_id of_gpio_leds_match[] = {{ .compatible = "gpio-leds", },{},
};static struct platform_driver gpio_led_driver = {.probe		= gpio_led_probe,.remove		= gpio_led_remove,.driver		= {.name	= "leds-gpio",.of_match_table = of_gpio_leds_match,},
};/* 驱动注册函数 */
module_platform_driver(gpio_led_driver);

        of_gpio_leds_match:为LED驱动的匹配表,compatible属性就是匹配的对象,我们在设备中LED灯节点的compatible属性要与of_gpio_leds_match匹配表一致,gpio_led_probe函数才能执行,否者驱动没法工作。

        gpio_led_driver:Linux 内核自带的 LED 驱动采用了 platform 框架。

        driver->name:驱动名字,在/sys/bus/platform/drivers 目录下存在一个名为“leds-gpio”的文件。

         2、module_platform_driver 函数简析

        LED 驱动会采用 module_platform_driver 函数向 Linux 内核注册platform 驱动,其实在 Linux 内核中会大量采用 module_platform_driver 来完成向 Linux 内核注册 platform 驱动的操作。 module_platform_driver 定义在 include/linux/platform_device.h 文件中,为一个宏,定义如下:

#define module_platform_driver(__platform_driver) \module_driver(__platform_driver, platform_driver_register, \platform_driver_unregister)

        可以看出, module_platform_driver 依赖 module_driver, module_driver 也是一个宏,定义在include/linux/device.h 文件中,内容如下:

#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

        将函数展开后得到:

static int __init gpio_led_driver_init(void)
{return platform_driver_register (&(gpio_led_driver));
}
module_init(gpio_led_driver_init);static void __exit gpio_led_driver_exit(void)
{platform_driver_unregister (&(gpio_led_driver) );
}
module_exit(gpio_led_driver_exit);

         这就是我们前面使用的注册驱动模块的框架。

        3、gpio_led_probe 函数简析

        当驱动和设备匹配以后 gpio_led_probe 函数就会执行,此函数主要是从设备树中获取 LED灯的 GPIO 信息,函数内容如下所示:

static int gpio_led_probe(struct platform_device *pdev)
{struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);struct gpio_leds_priv *priv;int i, ret = 0;if (pdata && pdata->num_leds) {            /* 非设备树情况 */。。。。} else {                                /* 使用设备树情况 */priv = gpio_leds_create(pdev);if (IS_ERR(priv))return PTR_ERR(priv);}platform_set_drvdata(pdev, priv);return 0;
}

        如果使用设备树的话,使用 gpio_leds_create 函数从设备树中提取设备信息,获取到的 LED 灯 GPIO 信息保存在返回值中, gpio_leds_create 函数内容如下:

static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct fwnode_handle *child;struct gpio_leds_priv *priv;int count, ret;struct device_node *np;/* 统计子结点数量 */count = device_get_child_node_count(dev);if (!count)return ERR_PTR(-ENODEV);priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), GFP_KERNEL);if (!priv)return ERR_PTR(-ENOMEM);/* 遍历子结点,获取每个子结点信息 */device_for_each_child_node(dev, child) {struct gpio_led led = {};const char *state = NULL;/* 获取 LED 灯所使用的 GPIO 信息 */led.gpiod = devm_get_gpiod_from_child(dev, NULL, child);if (IS_ERR(led.gpiod)) {fwnode_handle_put(child);ret = PTR_ERR(led.gpiod);goto err;}np = of_node(child);/* 读取子节点 label 属性值,因为使用 label 属性作为 LED 的名字 */if (fwnode_property_present(child, "label")) {fwnode_property_read_string(child, "label", &led.name);} else {if (IS_ENABLED(CONFIG_OF) && !led.name && np)led.name = np->name;if (!led.name)return ERR_PTR(-EINVAL);}/* 获取“linux,default-trigger”属性值 */fwnode_property_read_string(child, "linux,default-trigger",&led.default_trigger);/* 获取“default-state”属性值 */if (!fwnode_property_read_string(child, "default-state",&state)) {if (!strcmp(state, "keep"))led.default_state = LEDS_GPIO_DEFSTATE_KEEP;else if (!strcmp(state, "on"))led.default_state = LEDS_GPIO_DEFSTATE_ON;elseled.default_state = LEDS_GPIO_DEFSTATE_OFF;}if (fwnode_property_present(child, "retain-state-suspended"))led.retain_state_suspended = 1;ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],dev, NULL);if (ret < 0) {fwnode_handle_put(child);goto err;}}return priv;err:for (count = priv->num_leds - 2; count >= 0; count--)delete_gpio_led(&priv->leds[count]);return ERR_PTR(ret);
}

        调用 device_get_child_node_count 函数统计子节点数量,一般在在设备树中创建一个节点表示 LED 灯,然后在这个节点下面为每个 LED 灯创建一个子节点。因此子节点数量也是 LED 灯的数量。遍历每个子节点,获取每个子节点的信息。读取子节点 label 属性值,因为使用 label 属性作为 LED 的名字,获取“linux,default-trigger”属性值,可以通过此属性设置某个 LED 灯在Linux 系统中的默认功能,比如作为系统心跳指示灯等等,获取“default-state”属性值,也就是 LED 灯的默认状态属性,调用 create_gpio_led 函数创建 LED 相关的 io,其实就是设置 LED 所使用的 io为输出之类的。 create_gpio_led 函数主要是初始化 led_dat 这个 gpio_led_data 结构体类型变量,led_dat 保存了 LED 的操作函数等内容。

        知道了驱动获取哪些节点信息后,我们就可以使用时候添加节点时,我们就可以知道如何添加节点信息,添加哪些节点信息。

二、设备树节点编写

        打开文档 Documentation/devicetree/bindings/leds/leds-gpio.txt,此文档详细的讲解了 Linux自带驱动对应的设备树节点该如何编写,我们在编写设备节点的时候要注意以下几点:

        ①、 创建一个节点表示 LED 灯设备,比如 dtsleds,如果板子上有多个 LED 灯的话每个LED灯都作为 dtsleds 的子节点。

        ②、dtsleds 节点的 compatible 属性值一定要为“gpio-leds”。
        ③、设置 label 属性,此属性为可选,每个子节点都有一个 label 属性, label 属性一般表示LED 灯的名字,比如以颜色区分的话就是 red、 green 等等。

        ④、每个子节点必须要设置 gpios 属性值,表示此 LED 所使用的 GPIO 引脚!

        ⑤、可以设置“linux,default-trigger”属性值,也就是设置 LED 灯的默认功能,可以查阅
Documentation/devicetree/bindings/leds/common.txt 这个文档来查看可选功能,比如:

        backlight:          LED 灯作为背光。
        default-on:        LED 灯打开
        heartbeat:         LED 灯作为心跳指示灯,可以作为系统运行提示灯。
        ide-disk:            LED 灯作为硬盘活动指示灯。
        timer:                LED 灯周期性闪烁,由定时器驱动,闪烁频率可以修改

        ⑥、可以设置“default-state”属性值,可以设置为 onoff keep,为 on 的时候 LED 灯默认打开,为 off 的话 LED 灯默认关闭,为 keep 的话 LED 灯保持当前模式。

        示例:

        在dtsled节点里面,添加了Linux内核LED驱动需要的节点信息,还包括了使用pinctrl子系统和gpio子系统的节点信息可以参考前面的 六、(正点原子)pinctrl子系统和gpio子系统_正点原子开发板gpio-CSDN博客

         编译设备树文件,开发板使用新的设备树文件启动。

 三、验证

        在LED驱动添加后在/sys/bus/platform/driver里面会有配置的leds-gpio驱动文件:

         在/sys/bus/platform/devices里面会有设备树添加的节点设备:

         在启动内核时,我们的LED灯设置为  heartbeat模式,所以LED灯会像心跳一样闪动。,执行如下指令改变当前触发模式,改成[none]模式就可以通过指令来控制 LED 的亮灭了

echo none > /sys/class/leds/sys-led/trigger     // 改变 LED 的触发模式
echo 1    > /sys/class/leds/sys-led/brightness  // 点亮 LED
echo 0    > /sys/class/leds/sys-led/brightness  // 熄灭 LED

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/377450.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络之广域网

广域网特点: 主要提供面向通信的服务&#xff0c;支持用户使用计算机进行远距离的信息交换。 覆盖范围广,通信的距离远&#xff0c;需要考虑的因素增多&#xff0c; 线路的冗余、媒体带宽的利用和差错处理问题。 由电信部门或公司负责组建、管理和维护&#xff0c;并向全社会…

[ruby on rails]部署时候产生ActiveRecord::PreparedStatementCacheExpired错误的原因及解决方法

一、问题&#xff1a; 有时在 Postgres 上部署 Rails 应用程序时&#xff0c;可能会看到 ActiveRecord::PreparedStatementCacheExpired 错误。仅当在部署中运行迁移时才会发生这种情况。发生这种情况是因为 Rails 利用 Postgres 的缓存准备语句(PreparedStatementCache)功能来…

【UNI-APP】阿里NLS一句话听写typescript模块

阿里提供的demo代码都是javascript&#xff0c;自己捏个轮子。参考着自己写了一个阿里巴巴一句话听写Nls的typescript模块。VUE3的组合式API形式 startClient&#xff1a;开始听写&#xff0c;注意下一步要尽快开启识别和传数据&#xff0c;否则6秒后会关闭 startRecognition…

《基于 LatentFactor + Redis + ES 实现动态药房分配方法》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; 近期刚转战 CSDN&#xff0c;会严格把控文章质量&#xff0c;绝不滥竽充数&#xff0c;欢迎多多交流。&am…

从Centos7升级到Rocky linux 9后,网卡连接显示‘Wired connection 1‘问题解决方法

问题描述 从Centos7升级到Rocky9后, 发现网卡eth0的IP不正确。通过nmcli查看网卡连接&#xff0c;找不到name为eth0的连接&#xff0c;只显示’Wired connection 1’ 查看/etc/NetworkManager/system-connections/&#xff0c;发现找不到网卡配置文件。 原因分析 centos7使…

5G RedCap调查报告

一、5G RedCap技术背景 5G RedCap(Reduced Capability缩写,轻量化5G),是3GPP标准化组织定义下的5G裁剪版本,是5G面向中高速率连接场景的物联网技术,它的能力介于5G NR(含eMBB和uRLLC)和LPWA(如LTE-M和NR-IoT)之间,如图1所示,是5G-A(5G Advanced)的关键技术之一。…

PHP中的函数与调用:深入解析与应用

目录 一、函数基础 1.1 函数的概念 1.2 函数的定义 1.3 函数的调用 二、PHP函数的分类 2.1 内置函数 2.2 用户自定义函数 2.3 匿名函数 2.4 递归函数 2.5 回调函数 2.6 魔术方法 三、函数的参数与返回值 3.1 参数传递 3.2 返回值 四、函数的高级特性 4.1 可变函…

在linux中查找 / 目录下的以.jar结尾的文件(find / -name *.jar)

文章目录 1、查找 / 目录下的以.jar结尾的文件 1、查找 / 目录下的以.jar结尾的文件 [rootiZuf6332h890vozldoxcprZ ~]# find / -name *.jar /etc/java/java-1.8.0-openjdk/java-1.8.0-openjdk-1.8.0.342.b07-1.el9_0.x86_64/lib/security/policy/limited/US_export_policy.ja…

【BUG】Python3|COPY 指令合并 ts 文件为 mp4 文件时长不对(含三种可执行源代码和解决方法)

文章目录 前言源代码FFmpeg的安装1 下载2 安装 前言 参考&#xff1a; python 合并 ts 视频&#xff08;三种方法&#xff09;使用 FFmpeg 合并多个 ts 视频文件转为 mp4 格式 Windows 平台下&#xff0c;用 Python 合并 ts 文件为 mp4 文件常见的有三种方法&#xff1a; 调用…

设计模式8-桥模式

设计模式8-Bridge 桥模式 由来与目的模式定义结构桥接模式的结构结构说明 代码推导1. 类和接口的定义2. 平台实现3. 业务抽象4. 使用示例总结1. 类数量过多&#xff0c;复杂度高2. 代码重复3. 不符合单一职责原则4. 缺乏扩展性改进后的设计1. 抽象和实现分离&#xff08;桥接模…

【云岚到家】-day05-6-项目迁移-门户-CMS

【云岚到家】-day05-6-项目迁移-门户-CMS 4 项目迁移-门户4.1 迁移目标4.2 能力基础4.2.1 缓存方案设计与应用能力4.2.2 静态化技术应用能力 4.3 需求分析4.3.1 界面原型 4.4 系统设计4.4.1 表设计4.4.2 接口与方案4.4.2.1 首页信息查询接口4.4.3.1 数据缓存方案4.4.3.2 页面静…

linux的学习(四):磁盘,进程,定时,软件包的相关命令

简介 关于磁盘管理&#xff0c;进程管理&#xff0c;定时任务&#xff0c;软件包管理的命令的使用 磁盘管理类命令 du du 目录名&#xff1a; 查看文件和目录占用的磁盘空间 参数&#xff1a; -h&#xff1a;可以看到大小的单位&#xff0c;g,mb-a&#xff1a;还可以看到文…

红色文化3D虚拟数字展馆搭建意义深远

在房地产与土地市场的浪潮中&#xff0c;无论是新城规划、乡村振兴&#xff0c;还是商圈建设&#xff0c;借助VR全景制作、虚拟现实和web3d开发技术打造的全链条无缝VR看房新体验。不仅极大提升了带看与成交的转化率&#xff0c;更让购房者足不出户&#xff0c;即可享受身临其境…

毕设项目springboot+vue实现的在线求职平台

一、前言 随着信息技术的飞速发展和互联网的普及&#xff0c;线上求职已成为众多求职者和企业招聘的重要渠道。为满足市场需求&#xff0c;我们利用Spring Boot和Vue技术栈&#xff0c;开发了一款功能全面、用户友好的在线求职平台。本文将对该平台的设计、实现及关键技术进行详…

Python与自动化脚本编写

Python与自动化脚本编写 Python因其简洁的语法和强大的库支持&#xff0c;成为了自动化脚本编写的首选语言之一。在这篇文章中&#xff0c;我们将探索如何使用Python来编写自动化脚本&#xff0c;以简化日常任务。 一、Python自动化脚本的基础 1. Python在自动化中的优势 Pyth…

昇思25天学习打卡营第15天|基于MobileNetv2的垃圾分类

一、关于MobileNetv2 MobileNet网络专注于移动端、嵌入式或IoT设备的轻量级CNN网络。MobileNet网络使用深度可分离卷积&#xff08;Depthwise Separable Convolution&#xff09;的思想在准确率小幅度降低的前提下&#xff0c;大大减小了模型参数与运算量。并引入宽度系数 α和…

LLM 构建Data Multi-Agents 赋能数据分析平台的实践之④:数据分析之三(数据展示)

概述 在先前探讨的文章中&#xff0c;我们构建了一个全面的数据测试体系&#xff0c;该体系遵循“数据获取—数据治理—数据分析”的流程。如何高效地构建数据可视化看板&#xff0c;以直观展现分析结果&#xff0c;正逐渐成为利用新兴技术提升效能的关键领域。伴随业务拓展、数…

SQl server 日期函数查询相关练习

练习1.按月份分析销售数据。 create database date_db; use date_db; CREATE TABLE SalesData ( SaleID INT PRIMARY KEY IDENTITY(1,1), ProductName NVARCHAR(100) NOT NULL, SaleAmount DECIMAL(10, 2) NOT NULL, SaleDate DATE NOT NULL ); INSERT INTO Sa…

华为USG6000V防火墙v1

目录 一、实验拓扑图 二、要求 三、IP地址规划 四、实验配置 1&#x1f923;防火墙FW1web服务配置 2.网络配置 要求1&#xff1a;DMZ区内的服务器&#xff0c;办公区仅能在办公时间内(9:00-18:00)可以访问&#xff0c;生产区的设备全天可以访问 要求2&#xff1a;生产区不…

使用Python和MediaPipe实现手势控制音量(Win/Mac)

1. 依赖库介绍 OpenCV OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和机器学习软件库。它包含了数百个计算机视觉算法。 MediaPipe MediaPipe是一个跨平台的机器学习解决方案库&#xff0c;可以用于实时人类姿势估计、手势识…