4. 设备树基础:GPIO 控制器与使用者节点
设备树(Device Tree)是嵌入式 Linux 中描述硬件拓扑的标准机制。在 GPIO 子系统开发中,设备树承担着“硬件连线图”的角色:它告诉内核哪个引脚属于哪个控制器、引脚默认电平、中断触发方式等关键信息。本章聚焦设备树语法基础,并深入解析 GPIO 控制器节点与使用者节点的绑定规则。
4.1 设备树语法速览
设备树源文件(.dts)采用树形结构,每个硬件设备对应一个节点(node),节点内包含若干属性(property)。以下是最常用的语法元素:
| 语法元素 | 示例 | 说明 |
|---|---|---|
| 根节点 | / { ... }; |
设备树的顶层容器,所有设备节点挂载其下 |
| 节点定义 | gpio0: gpio@ff310000 { ... }; |
gpio0 为标签(label),gpio@ff310000 为节点名+寄存器地址 |
| 属性赋值 | compatible = "vendor,device"; |
字符串属性,用于驱动匹配 |
| 32 位整数 | reg = <0xff310000 0x1000>; |
地址和长度对,通常描述寄存器范围 |
| 数组 | gpios = <&gpio0 15 0>; |
引用其他节点并传递参数 |
| 包含文件 | #include "imx6ul.dtsi" |
引入 SoC 级 .dtsi 头文件,复用公共定义 |
<&gpio0 15 0> 这种写法称为 phandle + 参数 模式。&gpio0 是对标签 gpio0 节点的引用,后面的 15 和 0 是传递给该节点的参数,具体含义由 #gpio-cells 决定。
4.2 GPIO 控制器节点
GPIO 控制器节点描述 SoC 内部的一个 GPIO 模块。它必须声明两个关键属性:
gpio-controller—— 空属性,标记该节点是一个 GPIO 控制器。#gpio-cells—— 整型属性,指定使用者在引用该控制器时需要提供几个参数(cell)。
以下是一个典型的 GPIO 控制器节点(以 i.MX6UL 为例):
gpio1: gpio@0209c000 {
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
reg = <0x0209c000 0x4000>;
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
该节点中 #gpio-cells = <2> 表示:任何其他节点通过 gpios 引用 gpio1 时,必须提供 2 个参数——引脚号(0~31)和标志位(如 GPIO_ACTIVE_HIGH/LOW)。
#gpio-cells 与 #interrupt-cells 是两个独立的属性。前者用于 GPIO 输出/输入控制,后者用于中断触发。一个控制器可以同时支持两种功能,但参数个数可能不同。
4.3 GPIO 使用者节点
使用者节点(consumer node)代表一个需要占用 GPIO 引脚的设备,例如 LED、按键、传感器等。它通过 gpios 属性(或 cs-gpios、reset-gpios 等命名后缀)来声明引脚连接。
基本语法格式:
gpios = <&gpio_controller pin_number flags>;
其中 pin_number 和 flags 的个数由控制器节点的 #gpio-cells 决定。以下是一个 LED 子节点的完整示例:
/ {
leds {
compatible = "gpio-leds";
led-red {
label = "red-led";
gpios = <&gpio1 15 GPIO_ACTIVE_LOW>;
linux,default-trigger = "heartbeat";
};
};
};
这里 gpios 提供了 3 个值:
&gpio1—— 引用 gpio1 控制器节点。15—— 引脚编号(GPIO1_IO15)。GPIO_ACTIVE_LOW—— 标志位,表示低电平有效(内核中定义为 1)。
#gpio-cells = <2>,但使用者只写了 gpios = <&gpio1 15>(缺少 flags),内核在解析时会报错并忽略该引脚。务必检查 #gpio-cells 与 gpios 参数个数是否匹配。
4.4 GPIO 控制器与使用者关系图
下图展示了设备树中 GPIO 控制器节点与使用者节点之间的引用关系,以及 #gpio-cells 如何决定参数传递格式:
4.5 多控制器与命名后缀
一个设备可能占用多个 GPIO 引脚,且这些引脚可能来自不同的控制器。设备树允许使用 命名后缀 来区分不同用途的引脚:
| 属性名 | 典型用途 | 示例 |
|---|---|---|
gpios |
通用 GPIO 引脚 | gpios = <&gpio1 10 0>; |
cs-gpios |
SPI 片选引脚 | cs-gpios = <&gpio2 5 0>; |
reset-gpios |
复位引脚 | reset-gpios = <&gpio3 12 GPIO_ACTIVE_LOW>; |
irq-gpios |
中断请求引脚 | irq-gpios = <&gpio4 7 0>; |
内核通过 of_get_named_gpio_flags() 等 API 根据属性名解析对应的引脚。命名后缀使设备树更具可读性,也便于驱动代码按功能查找引脚。
4.6 本章小结
- 设备树使用 节点 + 属性 描述硬件,
compatible是驱动匹配的关键。 - GPIO 控制器节点必须声明
gpio-controller和#gpio-cells,后者决定使用者提供参数的个数。 - 使用者节点通过
gpios属性引用控制器,参数包括 phandle、引脚号、标志位。 - 多引脚设备可使用
cs-gpios、reset-gpios等命名后缀区分功能。 - 参数个数不匹配是设备树最常见的错误之一,务必在编译时使用
dtc工具检查。