Linux 自带的LED 灯驱动实验

目录

  • Linux 内核自带LED 驱动使能
  • Linux 内核自带LED 驱动简介
    • LED 灯驱动框架分析
    • module_platform_driver 函数简析
    • gpio_led_probe 函数简析
  • 设备树节点编写
  • 运行测试

前面我们都是自己编写LED 灯驱动,其实像LED 灯这样非常基础的设备驱动,Linux 内核已经集成了。Linux 内核的LED 灯驱动采用platform 框架,因此我们只需要按照要求在设备树文件中添加相应的LED 节点即可,本章我们就来学习如何使用Linux 内核自带的LED 驱动来驱动I.MX6U-ALPHA 开发板上的LED0。

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”键,使此选项前面变为“<*>”,如图56.1.1 所示:
在这里插入图片描述
在“LED Support for GPIO connected LEDs”上按下‘?’可以打开此选项的帮助信息,如图56.1.2 所示:
在这里插入图片描述
从图56.1.2 可以看出,把Linux 内部自带的LED 灯驱动编译进内核以后,
CONFIG_LEDS_GPIO 就会等于‘y’,Linux 会根据CONFIG_LEDS_GPIO 的值来选择如何编译LED 灯驱动,如果为‘y’就将其编译进Linux 内核。

配置好Linux 内核以后退出配置界面,打开.config 文件,会找到“CONFIG_LEDS_GPIO=y”这一行,如图56.1.3 所示:
在这里插入图片描述

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

Linux 内核自带LED 驱动简介

LED 灯驱动框架分析

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

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

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

236 static const struct of_device_id of_gpio_leds_match[] = {
237 { .compatible = "gpio-leds", },
238 {},
239 };
......
290 static struct platform_driver gpio_led_driver = {
291 .probe = gpio_led_probe,
292 .remove = gpio_led_remove,
293 .driver = {
294 .name = "leds-gpio",
295 .of_match_table = of_gpio_leds_match,
296 },
297 };
298
299 module_platform_driver(gpio_led_driver);

第236~239 行,LED 驱动的匹配表,此表只有一个匹配项,compatible 内容为“gpio-leds”,因此设备树中的LED 灯设备节点的compatible 属性值也要为“gpio-leds”,否则设备和驱动匹配不成功,驱动就没法工作。

第290~296 行,platform_driver 驱动结构体变量,可以看出,Linux 内核自带的LED 驱动采用了platform 框架。第291 行可以看出probe 函数为gpio_led_probe,因此当驱动和设备匹配成功以后gpio_led_probe 函数就会执行。从294 行可以看出,驱动名字为“leds-gpio”,因此会在/sys/bus/platform/drivers 目录下存在一个名为“leds-gpio”的文件,如图56.2.1.1 所示:
在这里插入图片描述
第299 行通过module_platform_driver 函数向Linux 内核注册gpio_led_driver 这个platform驱动。

module_platform_driver 函数简析

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

221 #define module_platform_driver(__platform_driver) \
222 module_driver(__platform_driver, platform_driver_register, \
223 platform_driver_unregister)

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

1260 #define module_driver(__driver, __register, __unregister, ...) \
1261 static int __init __driver##_init(void) \
1262 { \
1263 return __register(&(__driver) , ##__VA_ARGS__); \
1264 } \
1265 module_init(__driver##_init); \
1266 static void __exit __driver##_exit(void) \
1267 { \
1268 __unregister(&(__driver) , ##__VA_ARGS__); \
1269 } \
1270 module_exit(__driver##_exit);

借助示例代码56.2.2.1 和示例代码56.2.2.2,将:

module_platform_driver(gpio_led_driver)

展开以后就是:

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

上面的代码不就是标准的注册和删除platform 驱动吗?因此module_platform_driver 函数的功能就是完成platform 驱动的注册和删除。

gpio_led_probe 函数简析

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

243 static int gpio_led_probe(struct platform_device *pdev)
244 {
245 struct gpio_led_platform_data *pdata =
dev_get_platdata(&pdev->dev);
246 struct gpio_leds_priv *priv;
247 int i, ret = 0;
248
249 if (pdata && pdata->num_leds) { /* 非设备树方式*/
/* 获取platform_device信息*/
......
268 } else { /* 采用设备树*/
269 priv = gpio_leds_create(pdev);
270 if (IS_ERR(priv))
271 return PTR_ERR(priv);
272 }
273
274 platform_set_drvdata(pdev, priv);
275
276 return 0;
277 }

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

167 static struct gpio_leds_priv *gpio_leds_create(struct
platform_device *pdev)
168 {
169 struct device *dev = &pdev->dev;
170 struct fwnode_handle *child;
171 struct gpio_leds_priv *priv;
172 int count, ret;
173 struct device_node *np;
174
175 count = device_get_child_node_count(dev);
176 if (!count)
177 return ERR_PTR(-ENODEV);
178
179 priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count),
GFP_KERNEL);
180 if (!priv)
181 return ERR_PTR(-ENOMEM);
182
183 device_for_each_child_node(dev, child) {
184 struct gpio_led led = {};
185 const char *state = NULL;
186
187 led.gpiod = devm_get_gpiod_from_child(dev, NULL, child);
188 if (IS_ERR(led.gpiod)) {
189 fwnode_handle_put(child);
190 ret = PTR_ERR(led.gpiod);
191 goto err;
192 }
193
194 np = of_node(child);
195
196 if (fwnode_property_present(child, "label")) {
197 fwnode_property_read_string(child, "label", &led.name);
198 } else {
199 if (IS_ENABLED(CONFIG_OF) && !led.name && np)
200 led.name = np->name;
201 if (!led.name)
202 return ERR_PTR(-EINVAL);
203 }

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

关于gpio_led_probe 函数就分析到这里,gpio_led_probe 函数主要功能就是获取LED 灯的设备信息,然后根据这些信息来初始化对应的IO,设置为输出等。

设备树节点编写

打开文档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”属性值,可以设置为on、off 或keep,为on 的时候LED 灯默认打开,为off 的话LED 灯默认关闭,为keep 的话LED 灯保持当前模式。
根据上述几条要求在imx6ull-alientek-emmc.dts 中添加如下所示LED 灯设备节点:

1 dtsleds {
2 compatible = "gpio-leds";
3
4 led0 {
5 label = "red";
6 gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
7 default-state = "off";
8 };
9 };

因为I.MX6U-ALPHA 开发板只有一个LED0,因此在dtsleds 这个节点下只有一个子节点led0,LED0 名字为red,默认关闭。修改完成以后保存并重新编译设备树,然后用新的设备树启动开发板。

运行测试

用新的zImage 和imx6ull-alientek-emmc.dtb 启动开发板,启动以后查看
/sys/bus/platform/devices/dtsleds 这个目录是否存在,如果存在的话就如到此目录中,如图56.4.1所示:
在这里插入图片描述
进入到leds 目录中,此目录中的内容如图56.4.2 所示:
在这里插入图片描述

从图56.4.2 可以看出,在leds 目录下有一个名为“red”子目录,这个子目录的名字就是我们在设备树中第5 行设置的label 属性值。
我们的设置究竟有没有用,最终是要通过测试才能知道的,首先查看一下系统中有没有“sys/class/leds/red/brightness”这个文件,如果有的话就输入如下命令打开RED 这个LED 灯:

echo 1 > /sys/class/leds/red/brightness //打开LED0

关闭RED 这个LED 灯的命令如下:

echo 0 > /sys/class/leds/red/brightness //关闭LED0

如果能正常的打开和关闭LED 灯话就说明我们Linux 内核自带的LED 灯驱动工作正常。
我们一般会使用一个LED 灯作为系统指示灯,系统运行正常的话这个LED 指示灯就会一闪一闪的。里我们设置LED0 作为系统指示灯,在dtsleds 这个设备节点中加入“linux,default-trigger”属性信息即可,属性值为“heartbeat”,修改完以后的dtsleds 节点内容如下:

1 dtsleds {
2 compatible = "gpio-leds";
3
4 led0 {
5 label = "red";
6 gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
7 linux,default-trigger = "heartbeat";
8 default-state = "on";
9 };
10 };

第7 行,设置LED0 为heartbeat。
第8 行,默认打开LED0。
重新编译设备树并且使用新的设备树启动Linux 系统,启动以后LED0 就会闪烁,作为系统心跳指示灯,表示系统正在运行。

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

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

相关文章

虚拟硬盘VHD的程式化挂载方式

2019独角兽企业重金招聘Python工程师标准>>> 微软推出的虚拟硬盘vhd同vmdk,qcow2等一样&#xff0c;是一种磁盘镜像格式。磁盘镜像一般多用于虚拟化计算&#xff0c;但微软支持将Win7/Win8/Win2012等直接安装进vhd文件中&#xff0c;并且不通过任何虚拟机&#xff0…

Cs231n课堂内容记录-Lecture 5 卷积神经网络介绍

Lecture 5 CNN 课堂笔记参见&#xff1a;https://zhuanlan.zhihu.com/p/22038289?referintelligentunit 不错的总结笔记&#xff1a;https://blog.csdn.net/sugar_girl/article/details/79108709 1.卷积核步长公式&#xff1a;&#xff08;N-F2*padding&#xff09;/stride1ne…

Linux MISC 驱动实验

目录MISC 设备驱动简介硬件原理图分析实验程序编写修改设备树beep 驱动程序编写编写测试APP运行测试编译驱动程序和测试APP运行测试misc 的意思是混合、杂项的&#xff0c;因此MISC 驱动也叫做杂项驱动&#xff0c;也就是当我们板子上的某些外设无法进行分类的时候就可以使用MI…

PHP的session阻塞问题

2019独角兽企业重金招聘Python工程师标准>>> 通过phpinfo()可以看到php支持的session存储方式有&#xff1a; 可以看出session的存储方式有文件、用户自定义、memcache存储方式 在文件的存储方式用存在session阻塞的问题&#xff0c;该种阻塞存在于同一个浏览器执行…

Linux INPUT 子系统实验

目录input 子系统input 子系统简input 驱动编写流程input_event 结构体硬件原理图分析实验程序编写修改设备树文件按键input 驱动程序编写编写测试APP运行测试编译驱动程序和测试APP运行测试Linux 自带按键驱动程序的使用自带按键驱动程序源码简析自带按键驱动程序的使用按键、…

django项目中settings.py文件中路径设置问题

2019独角兽企业重金招聘Python工程师标准>>> 首先&#xff0c;__file__表示当前模块的名字。 将下列代码写入os.path.py文件中&#xff0c;存放在桌面上 """ import os print __file__ """ win R 开启一个 CMD&#xff0c;使用pytho…

Linux LCD 驱动实验

目录Linux 下LCD 驱动简析1 Framebuffer 设备LCD 驱动简析硬件原理图分析LCD 驱动程序编写运行测试LCD 屏幕基本测试设置LCD 作为终端控制台LCD 背光调节从LCD自动关闭解决方法LCD 是很常用的一个外设&#xff0c;在裸机篇中我们讲解了如何编写LCD 裸机驱动&#xff0c;在Linux…

一个老兵的linux学习和面试经验分享

特别说明&#xff1a;本文为约9个月前老男孩linux培训内部师兄给师弟的经验分享&#xff0c;经过该同学同意&#xff0c;特此分享给所有博友。学习和面试经验分享大家好&#xff0c;非常高兴能在这里给大家分享学习和面试的经验&#xff0c;同时也非常感谢老男孩老师给我这次机…

Linux RTC 驱动实验

目录Linux 内核RTC 驱动简介I.MX6U 内部RTC 驱动分析RTC 时间查看与设置RTC 也就是实时时钟&#xff0c;用于记录当前系统时间&#xff0c;对于Linux 系统而言时间是非常重要的&#xff0c;就和我们使用Windows 电脑或手机查看时间一样&#xff0c;我们在使用Linux 设备的时候也…

opencv及相机相关6

下午调试了下&#xff0c;现在可以实现在arm板上打开相机并实时显示&#xff0c;但是人脸检测暂时还没有实现&#xff0c;猜想是因为opencv的原因。明天接着调试。今天整明白一件事&#xff0c;首先在linux系统下&#xff0c;g编译器当cpp文件较少时&#xff0c;编译程序时需要…

Linux I2C 驱动实验

目录Linux I2C 驱动框架简介I2C 总线驱动I2C 设备驱动I2C 设备和驱动匹配过程I.MX6U 的I2C 适配器驱动分析I2C 设备驱动编写流程I2C 设备信息描述I2C 设备数据收发处理流程硬件原理图分析实验程序编写修改设备树AP3216C 驱动编写编写测试APP运行测试编译驱动程序和测试APP运行测…

NXP(I.MX6uLL)DDR3实验——DDR3重要时间参数、时钟配置与原理图简析

目录DDR3 内存时间参数传输速率tRCD 参数CL 参数AL 参数tRC 参数tRAS 参数I.MX6U MMDC内存控制器简介MMDC内存控制器简介MMDC内存控制器信号引脚MMDC内存控制器时钟源ALPHA开发板核心板DDR3L原理图DDR3 内存时间参数 大家在购买DDR3 内存的时候通常会重点观察几个常用的时间参…

[elk]elasticsearch dsl语句

例子1 统计1&#xff0c;有唱歌兴趣的 2&#xff0c;按年龄分组 3&#xff0c;求每组平均年龄 4,按平均年龄降序排序 sql转为dsl例子 # 每种型号车的颜色数 > 1的 SELECT model,COUNT(DISTINCT color) color_count FROM cars GROUP BY model HAVING color_count > 1 ORDE…

NXP(I.MX6uLL)DDR3实验——DDR3初始化、校准、超频测试

DDR3L 初始化与测试ddr_stress_tester简介(DDR压力测试工具)DDR3L驱动配置DDR3L校准DDR3L超频测试DDR3L驱动总结ddr_stress_tester简介(DDR压力测试工具) NXP 提供了一个非常好用的DDR 初始化工具&#xff0c;叫做ddr_stress_tester。此工具已经放到了开发板光盘中&#xff0c…

RGBLCD显示实验————复习到这

目录LCD 和eLCDIF 简介LCD 简介eLCDIF 接口硬件原理分析实验程序编写编译下载验证编写Makefile 和链接脚本编译下载LCD 液晶屏是常用到的外设&#xff0c;通过LCD 可以显示绚丽的图形、界面等&#xff0c;提高人机交互的效率。I.MX6U 提供了一个eLCDIF 接口用于连接RGB 接口的液…

unity 里调试native code

因项目需要&#xff0c;需要调试dll工程代码。 把生成的debug dll和pdb拷贝进unity的plugins工程&#xff0c;遇到 断点无法进入&#xff0c;修改下调试信息格式&#xff0c;OK。 转载于:https://www.cnblogs.com/marcher/p/10191521.html

将自己写的windows服务加入到windows集群中

最近发现windows集群能进行很多自定义&#xff0c;比如在集群中加入自己编写的服务。 能自定义的可不少&#xff0c;截个图&#xff1a; 本次演示中&#xff0c;只想用“通用服务”这个类型。 先列下步骤编写一个记录时间的Windows服务&#xff0c;这个服务会每隔1秒钟写入本地…

RTC实时时钟实验(低功耗、纽扣电池供电)

目录I.MX6U RTC 简介硬件原理分析实验程序编写修改文件MCIMX6Y2.h编写实验程序编译下载验证编写Makefile 和链接脚本编译下载实时时钟是很常用的一个外设&#xff0c;通过实时时钟我们就可以知道年、月、日和时间等信息。因此在需要记录时间的场合就需要实时时钟&#xff0c;可…

Python(21)_input用户交互

name input("请输入你的名字&#xff1a;") age input("请输入你的年龄&#xff1a;") print("我的名字是&#xff1a;"name" 年龄是&#xff1a;"age) #print(name) #print(age) 这是在本地调试的结果。因为程序是在远程服务器上执…

多点电容触摸屏实验

目录多点电容触摸简介硬件原理分析实验程序编写编译下载验证编写Makefile 和链接脚本编译下载随着智能手机的发展&#xff0c;电容触摸屏也得到了飞速的发展。相比电阻触摸屏&#xff0c;电容触摸屏有很多的优势&#xff0c;比如支持多点触控、不需要按压&#xff0c;只需要轻轻…