Linux学习第30天:Linux 自带的 LED 灯驱动实验:驱动开发思维方式的转变势在必行

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长


学习嵌入式Linux驱动开发整整30天了。今天简单做一个小结。因为之前的主要工作是做ARM的裸机开发,所以接触Linux以后感觉很多东西都变了。不仅仅包括相关知识点的归纳,更重要的是思维方式的转变。做裸机开发的时候,在很多时候是要弄清楚最底层的逻辑才能进行代码的开发。而Linux则更像是站在一个更高的维度去思考,很细枝末叶的东西是不需要考虑的。相对裸机开发的维度来说,裸机开发后做Linux驱动开发更有点降维打击的感觉。但并不是说Linux驱动开发简单,而是说裸机开发对底层的要求可能会更高一些。以上仅仅是个人的一点自我感受。

本节笔记主要学习Linux自带的LED驱动试验。主要内容包括驱动使能、简介、设备树节点编写及运行测试。其中驱动简介又包括LED灯驱动框架分析、module_platform_driver函数简介和gpio_led_probe函数简析。

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

需要配置Linux内核,使能自带的LED驱动。使用make menuconfig打开Linux配置菜单。

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

Device Drivers

LED Support(NEW_LEDS=[y])

LED Support for GPIO connected LEDs. 在此选项上按下" Y"键,使此选项前面变为" <*>",

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

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

1、LED灯驱动框架分析

obj - $ ( CONFIG_LEDS_GPIO ) += leds - gpio . o

cpp 复制代码
236 static const struct of_device_id of_gpio_leds_match[] = {
237 { .compatible = "gpio-leds", },
238 {},
239 };

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

cpp 复制代码
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 },

platform_driver 驱动结构体变量,可以看出, Linux 内核自带的 LED 驱动采用了 platform 框架。

cpp 复制代码
module_platform_driver(gpio_led_driver);

通过 module_platform_driver 函数向 Linux 内核注册 gpio_led_driver 这个 platform

驱动。

2、module_platform_driver函数简介

module_platform_driver 定义在 include/linux/platform_device.h 文件中为一个,

module_platform_driver 依赖module_driver, module_driver 也是一个宏。

module_platform_driver 函数的功能就是完成 platform 驱动的注册和删除

3、gpio_led_probe函数简析

cpp 复制代码
269 priv = gpio_leds_create(pdev);
270 if (IS_ERR(priv))
271 return PTR_ERR(priv);

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

在gpio_leds_create 函数中,调用 device_get_child_node_count 函数统计子节点数量,一般在在设备树中创建一个节点表示 LED 灯,然后在这个节点下面为每个 LED 灯创建一个子节点。因此子节点数量也是 LED 灯的数量。

cpp 复制代码
device_for_each_child_node(dev, child) {

遍历每个子节点,获取每个子节点的信息。

cpp 复制代码
led.gpiod = devm_get_gpiod_from_child(dev, NULL, child);

获取 LED 灯所使用的 GPIO 信息。

cpp 复制代码
196 if (fwnode_property_present(child, "label")) {
197 fwnode_property_read_string(child, "label", &led.name);

读取子节点 label 属性值,因为使用 label 属性作为 LED 的名字。

cpp 复制代码
204 fwnode_property_read_string(child, "linux,default-trigger",
205 &led.default_trigger);

获取" linux,default-trigger"属性值,可以通过此属性设置某个 LED 灯在Linux 系统中的默认功能,比如作为系统心跳指示灯等等。

cpp 复制代码
207 if (!fwnode_property_read_string(child, "default-state",
208 &state)) {
209 if (!strcmp(state, "keep"))
210 led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
211 else if (!strcmp(state, "on"))
212 led.default_state = LEDS_GPIO_DEFSTATE_ON;
213 else
214 led.default_state = LEDS_GPIO_DEFSTATE_OFF;
215 }

获取" default-state"属性值,也就是 LED 灯的默认状态属性。

cpp 复制代码
220 ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
221 dev, NULL);

调用 create_gpio_led 函数创建 LED 相关的 io,其实就是设置 LED 所使用的 io为输出之类的。 create_gpio_led 函数主要是初始化 led_dat这个 gpio_led_data 结构体类型变量,led_dat 保存了 LED 的操作函数等内容。

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

三、设备树节点编写

在编写设备节点的时候要注意以下几点:

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

灯都作为 dtsleds 的子节点。

②、 dtsleds 节点的 compatible 属性值一定要为" gpio-leds"。

③、设置 label 属性,此属性为可选,每个子节点都有一个 label 属性, label 属性一般表示

LED 灯的名字,比如以颜色区分的话就是 red、 green 等等。

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

⑤、可以设置" linux,default-trigger "属性值,也就是设置 LED 灯的默认功能,比如:
backlight : LED 灯作为背光。
default-on : LED 灯打开
heartbeat : LED 灯作为心跳指示灯,可以作为系统运行提示灯。
ide-disk : LED 灯作为硬盘活动指示灯。
timer : LED 灯周期性闪烁,由定时器驱动,闪烁频率可以修改

⑥、可以设置" default-state "属性值,可以设置为 on、 off 或 keep,为 on 的时候 LED 灯默

认打开,为 off 的话 LED 灯默认关闭,为 keep 的话 LED 灯保持当前模式。

cpp 复制代码
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 };

四、运行测试

启 动 开 发 板 , 启 动 以 后 查 看/sys/bus/platform/devices/dtsleds 这个目录是否存在。

进入到 leds 目录中,此目录中的内容如图所示:

在 leds 目录下有一个名为" red"子目录,这个子目录的名字就是我们在设备树中第 5 行设置的 label属性值。

输入如下命令打开 RED 这个 LED 灯:
echo 1 > /sys/class/leds/red/brightness //打开 LED0

关闭 RED 这个 LED 灯的命令如下:
echo 0 > /sys/class/leds/red/brightness //关闭 LED0

系统运行指示灯:

cpp 复制代码
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 };

设置 LED0 作为系统指示灯,在 dtsleds 这个设备节点中加入" linux,default-trigge r"属性信息即可,属性值为" heartbeat"。

五、总结

本节笔记主要内容包括驱动使能、简介、设备树节点编写及运行测试。其中驱动简介又包括LED灯驱动框架分析、module_platform_driver函数简介和gpio_led_probe函数简析。


本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。

相关推荐
Mephisto.java21 分钟前
【大数据学习 | Spark-Core】Spark提交及运行流程
大数据·学习·spark
blessing。。42 分钟前
I2C学习
linux·单片机·嵌入式硬件·嵌入式
丁总学Java1 小时前
ARM 架构(Advanced RISC Machine)精简指令集计算机(Reduced Instruction Set Computer)
arm开发·架构
PandaCave1 小时前
vue工程运行、构建、引用环境参数学习记录
javascript·vue.js·学习
yuwinter1 小时前
鸿蒙HarmonyOS学习笔记(2)
笔记·学习·harmonyos
2202_754421541 小时前
生成MPSOC以及ZYNQ的启动文件BOOT.BIN的小软件
java·linux·开发语言
red_redemption1 小时前
自由学习记录(23)
学习·unity·lua·ab包
幽兰的天空2 小时前
默语博主的推荐:探索技术世界的旅程
学习·程序人生·生活·美食·交友·美女·帅哥
运维&陈同学2 小时前
【zookeeper03】消息队列与微服务之zookeeper集群部署
linux·微服务·zookeeper·云原生·消息队列·云计算·java-zookeeper
嵌新程2 小时前
day03(单片机高级)RTOS
stm32·单片机·嵌入式硬件·freertos·rtos·u575