嵌入式 Linux 驱动开发学习笔记:设备树、pinctrl 与 GPIO 驱动开发

嵌入式 Linux 驱动开发学习笔记:设备树、pinctrl 与 GPIO 驱动开发

一、前言

在嵌入式 Linux 驱动开发中,驱动程序不仅需要实现相应的控制逻辑,还需要获取硬件资源信息。例如,LED 连接在哪个 GPIO 引脚、引脚应该配置成什么功能,以及设备使用了哪些寄存器等。

如果将这些硬件信息全部直接写在驱动代码中,那么当硬件平台发生变化时,就可能需要修改甚至重新编写驱动。因此,Linux 引入了设备树和设备驱动分离的设计思想,将硬件描述信息与驱动逻辑分开管理。

本文主要介绍设备树的基本结构、设备树节点解析、pinctrl 引脚配置,以及 GPIO 的常用操作方法,并以 LED 驱动为例梳理相关开发流程。

二、设备树基础

2.1 什么是设备树

设备树(Device Tree,简称 DT)用于描述硬件平台的组成和资源信息。内核可以根据设备树中的描述,识别设备并获取相应的硬件配置。

常见的设备树文件包括:·

DTS:设备树源文件,通常用于描述具体开发板的硬件信息。·

DTSI:设备树头文件,可以被多个 DTS 文件引用,用于复用公共硬件描述。·

DTB:设备树二进制文件,通常由 DTS 编译生成,并在系统启动时提供给内核。

设备树的基本作用,是让硬件描述不必全部写死在驱动代码中。

2.2 设备树节点

设备树通常采用树形结构描述硬件设备。一个节点可以包含子节点,也可以包含多个属性。

例如:

led0 {

compatible = "demo,led";

status = "okay";

};

其中:·

led0:节点名称。

compatible:设备与驱动匹配时使用的兼容字符串。

status:表示设备当前是否启用。

常见属性还包括:·

reg:描述设备的地址和资源范围,具体含义取决于父节点的地址空间定义。

interrupts:描述设备使用的中断资源。

gpios:描述设备使用的 GPIO。·

pinctrl-names、pinctrl-0:用于指定引脚配置状态。

需要注意,属性是否有效、如何解析,都与具体设备节点及其绑定规则有关。

三、Linux 驱动如何解析设备树

Linux 内核提供了 OF(Open Firmware)相关接口,用于访问设备树节点和属性。

3.1 查找设备树节点

struct device_node *np;

np = of_find_node_by_path("/led0");

if (!np) {

pr_err("cannot find led node\n");

return -ENODEV;

}

of_find_node_by_path() 根据设备树路径查找节点。实际项目中,应根据板级 DTS 中的真实路径填写参数。

3.2 读取整数属性

u32 value;

int ret;

ret = of_property_read_u32(np, "some-value", &value);

if (ret) {

pr_err("read property failed\n");

return ret;

}

该接口用于读取单个 32 位整数属性。

如果属性包含多个整数,可以使用:

u32 values2;

ret = of_property_read_u32_array(

np, "some-values", values, 2);

需要注意:属性名称、数据类型、数组长度都必须与 DTS 中的实际定义一致。读取失败时,应检查节点是否正确、属性是否存在以及数据格式是否匹配。

3.3 compatible 匹配

在 platform 驱动中,通常会通过 compatible 属性将设备树节点与驱动匹配起来。例如:

static const struct of_device_id led_of_match\[\] = {

{ .compatible = "demo,led" },

{ }

};

MODULE_DEVICE_TABLE(of, led_of_match);

设备树中的兼容字符串必须与驱动中的字符串一致,否则可能无法正常匹配。

四、pinctrl:配置引脚功能

4.1 为什么需要 pinctrl

一个芯片引脚往往具有多种复用功能。例如,同一个引脚可能被配置为 GPIO,也可能被配置为 UART、SPI 或 I²C 功能。

因此,在使用 GPIO 之前,需要确认引脚已经配置成正确的功能。

pinctrl 主要负责引脚复用及相关电气配置,具体能力取决于芯片和对应的 pinctrl 驱动。

可以将整个过程理解为:

1.选择正确的引脚。

2.将引脚配置成所需功能。

3.根据需要设置上下拉、驱动能力等参数。

4.再通过 GPIO 接口控制引脚电平。

4.2 pinctrl 与 GPIO 的区别

两者容易混淆,但职责不同:

|------------|----------------------|
| 模块 | 主要作用 |
| pinctrl | 配置引脚复用功能和相关电气属性 |
| GPIO | 设置引脚输入或输出方向,并读取或控制电平 |

例如,要控制一个 LED,通常需要先保证对应引脚处于 GPIO 功能,再将 GPIO 配置为输出,最后设置高低电平。

五、GPIO 常用操作接口

Linux 内核提供了 GPIO 相关接口。在较早的内核版本中,常见的整数 GPIO 接口包括以下函数。

5.1 申请 GPIO

ret = gpio_request(gpio, "led_gpio");

if (ret) {

pr_err("gpio request failed\n");

return ret;

}

申请 GPIO 可以避免不同驱动随意占用同一资源。

5.2 配置输出方向

ret = gpio_direction_output(gpio, 0);

if (ret) {

gpio_free(gpio);

return ret;

}

第二个参数通常表示初始输出电平。实际 LED 的亮灭状态还取决于硬件连接方式:有些 LED 是低电平点亮,有些则是高电平点亮。

5.3 设置 GPIO 电平

gpio_set_value(gpio, 1);

gpio_set_value(gpio, 0);

这两个调用分别设置高电平和低电平。应当在 GPIO 已成功申请并配置为输出后使用。

5.4 释放 GPIO

gpio_free(gpio);

当驱动不再使用 GPIO 时,应释放已经申请的资源。

需要注意,GPIO 接口属于较早的整数编号接口。新驱动通常更推荐使用 GPIO descriptor 接口,例如 gpiod_get()、gpiod_set_value() 等,具体选择应结合目标内核版本和平台支持情况。

六、LED 驱动开发流程

一个基于设备树的 LED 驱动,大体可以按照以下步骤实现:

1.在 DTS 中描述 LED 设备及相关资源。

2.在驱动中编写 of_match_table。

3.实现驱动的 probe() 函数。

4.在 probe() 中获取设备资源并申请 GPIO。

5.配置 GPIO 的输入输出方向。

6.实现 LED 控制逻辑。

7.在设备移除时释放资源。

如果采用 platform 驱动模型,驱动通常在匹配成功后进入 probe(),而不是仅仅依靠模块加载函数自行查找和操作所有硬件。

此外,设备树节点中应明确设置正确的 status,并确认 DTS 已经编译进系统实际使用的 DTB。

七、常见问题排查

7.1 找不到设备树节点

重点检查:

节点路径是否正确。

实际启动时使用的 DTB 是否包含该节点。

status 是否为可用状态。

驱动是否采用了正确的匹配方式。

7.2 读取属性失败

如果出现 of_property_read_u32_array() 返回错误,应优先检查:

属性名称是否一致。

属性是否存在。

属性中的元素数量是否满足读取要求。

数据类型是否正确。

不要只根据驱动代码推测属性一定存在,应以实际 DTS 内容为准。

7.3 GPIO 申请失败

可能的原因包括 GPIO 编号错误、GPIO 已被其他驱动占用,或者设备树资源描述不正确。

7.4 LED 无法正常亮灭

应按顺序排查引脚复用、GPIO 方向、电平控制、LED 有效电平以及硬件连接,而不是直接认定为 GPIO 接口失效。

八、总结

设备树、pinctrl 和 GPIO 是嵌入式 Linux 驱动开发中经常接触的基础内容。

设备树用于描述硬件资源,驱动通过 OF 接口获取相关信息;pinctrl 负责配置引脚功能及电气属性;GPIO 则用于控制或读取引脚电平。

掌握这三部分之后,就可以进一步学习中断、等待队列以及更完整的设备驱动框架。

学习建议: 先确认 DTS 中的硬件描述,再检查驱动匹配和资源获取,最后验证 GPIO 的实际电平变化。按这个顺序排查,通常比直接修改驱动代码更有效。

相关推荐
硅基手札3 小时前
SPI技术系列 08 · SPI 控制器寄存器与 IP 架构
驱动开发·stm32·单片机·嵌入式硬件·mcu·架构·计算机外设
玄芯散人4 小时前
【元婴·98】驱动开发:沟通硬件的御灵术
驱动开发
万物智能信息科技4 小时前
鸿蒙RS232驱动开发—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
驱动开发·华为·开源·harmonyos·鸿蒙
blueSatchel4 小时前
framebuffer注册源码解读
linux·嵌入式
2401_862880824 小时前
嵌入式Linux系统移植与驱动开发完整指南
linux·c语言·驱动开发
lisw056 小时前
论机器人赋能的精准农业!
驱动开发·机器人·硬件工程
2035去旅行6 小时前
两核 CPU:CPU1 和 CPU2 是如何协同工作的?
嵌入式·arm·soc
blueSatchel6 小时前
imx6ull显示控制器设备驱动解读
linux·嵌入式
Vitaliy Kostrov(柯维远)7 小时前
客户端如何检查直播流:从流内标记到停止播放
android·运维·服务器·驱动开发·网络安全·系统架构·cdn