2. 设备树基础:设备树(DTS)语法入门、节点、属性、标签、包含文件
设备树(Device Tree)是嵌入式 Linux 系统中用于描述硬件资源的数据结构,它替代了传统架构中硬编码的板级文件(board-*.c)。在 Platform 驱动开发中,设备树是驱动与硬件匹配的桥梁。本章将系统讲解设备树的语法核心:节点、属性、标签与包含文件,为后续驱动匹配与资源获取打下坚实基础。
2.1 设备树概述与文件结构
设备树源文件(.dts)经过设备树编译器(dtc)编译为二进制设备树(.dtb),由 bootloader 传递给内核。一个典型的设备树文件包含以下部分:
| 文件类型 | 扩展名 | 作用 |
|---|---|---|
| 设备树源文件 | .dts | 描述单板级硬件,包含根节点和所有外设节点 |
| 设备树头文件 | .dtsi | SoC 级通用定义,通过 #include 被 .dts 引用 |
| 设备树二进制 | .dtb | 编译后的二进制文件,供内核解析 |
| 设备树覆盖 | .dtbo | 运行时动态加载的叠加片段 |
实践要点:在编写驱动时,通常只需要关注 .dts 和 .dtsi 文件。.dtsi 由 SoC 厂商提供,开发者应在 .dts 中引用并扩展,避免直接修改 .dtsi。
2.2 节点(Node)—— 设备树的骨架
节点是设备树的基本组成单元,每个节点代表一个硬件设备或总线。节点从根节点 / 开始,形成树状层级结构。
2.2.1 节点语法
/dts-v1/;
/ {
model = "Embedded Device Board v1.0";
compatible = "vendor,board-name";
cpu@0 {
device_type = "cpu";
reg = <0x00>;
compatible = "arm,cortex-a7";
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x40000000>; /* 1GB RAM */
};
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
uart0: serial@1c090000 {
compatible = "vendor,uart";
reg = <0x1c090000 0x1000>;
interrupts = <0 32 4>;
status = "okay";
};
};
};
节点命名规则:
- 节点名:格式为
node-name@unit-address,其中@unit-address是设备在总线上的起始地址,可省略(如根节点)。 - 节点路径:从根节点到目标节点的全路径,如
/soc/uart0。 - 节点标签:在节点名前加
标签名:,用于在其他位置引用该节点(详见 2.4 节)。
关键概念:节点名中的
@unit-address 不是必需的,但强烈建议为有地址空间的设备添加,以区分同类型设备。例如 uart0: serial@1c090000 和 uart1: serial@1c0a0000。
2.2.2 节点层级关系 SVG
2.3 属性(Property)—— 节点的数据载体
属性是节点中存储具体硬件信息的键值对。内核通过解析属性来获取设备地址、中断号、时钟频率等资源。常用属性分类如下:
| 属性类别 | 属性名 | 值格式 | 说明 |
|---|---|---|---|
| 兼容性 | compatible |
字符串列表 | 驱动匹配的关键,格式为 "厂商,设备型号" |
| 地址 | reg |
32/64 位整数数组 | 描述设备地址空间,格式为 <地址 长度> |
| 中断 | interrupts |
整数数组 | 描述中断号、触发类型等 |
| 状态 | status |
字符串 | 常用值 "okay"(启用)或 "disabled"(禁用) |
| 地址单元 | #address-cells |
整数 | 子节点 reg 地址字段占用的 32 位单元数 |
| 大小单元 | #size-cells |
整数 | 子节点 reg 长度字段占用的 32 位单元数 |
| 标签 | label |
字符串 | 设备别名,用于用户空间识别 |
2.3.1 属性值格式详解
/* 字符串 */
compatible = "vendor,device";
/* 32 位整数(单个) */
#address-cells = <1>;
/* 32 位整数数组(地址 + 长度) */
reg = <0x1c090000 0x1000>;
/* 64 位整数(使用两个 32 位单元) */
reg = <0x00 0x80000000 0x00 0x40000000>;
/* 字符串列表 */
compatible = "vendor,device", "generic,device";
/* 布尔属性(存在即为 true) */
dma-coherent;
/* 字节数组 */
local-mac-address = [00 11 22 33 44 55];
常见错误:忘记设置
#address-cells 和 #size-cells 会导致内核解析 reg 属性时地址长度错误。对于 32 位系统,通常设置为 <1> 和 <1>;对于 64 位系统,设置为 <2> 和 <2>。
2.4 标签(Label)与引用
标签是设备树中用于引用节点的别名机制。通过标签,可以在设备树的其他位置(如 .dtsi 文件或覆盖层)方便地访问或修改节点。
2.4.1 标签定义与引用语法
// 定义标签(在节点名前加标签名和冒号)
uart0: serial@1c090000 {
compatible = "vendor,uart";
reg = <0x1c090000 0x1000>;
};
// 引用标签并追加属性(使用 & 符号)
&uart0 {
pinctrl-0 = &uart0_pins;
status = "okay";
};
// 引用标签作为 phandle(指针句柄)
clocks = &clk_uart0;
2.4.2 标签的三种使用场景
| 场景 | 语法 | 说明 |
|---|---|---|
| 追加属性 | &标签 { ... }; |
在 .dts 文件中扩展 .dtsi 中定义的节点 |
| 引用 phandle | &标签 |
作为属性值,指向另一个节点(如时钟、中断控制器) |
| 覆盖层引用 | &标签 { ... }; |
在 .dtso 文件中修改运行时节点 |
最佳实践:SoC 厂商在 .dtsi 中为所有外设节点定义标签(如 uart0、i2c1、spi2),板级 .dts 文件通过
&标签 引用并启用或配置这些节点。这种方式避免了重复定义,提高了代码复用性。
2.5 包含文件(Include)—— 模块化设计
设备树支持 C 语言风格的 #include 预处理指令,用于引入 .dtsi 头文件、宏定义文件(.h)或 GPIO/时钟绑定定义。
2.5.1 包含文件的使用
// 包含 SoC 级设备树头文件
#include "soc.dtsi"
// 包含 GPIO 编号宏定义
#include "gpio.h"
// 包含时钟宏定义
#include "clk.h"
/ {
model = "My Board";
compatible = "vendor,my-board";
&uart0 {
pinctrl-0 = <&uart0_pins>;
status = "okay";
};
&i2c1 {
status = "okay";
clock-frequency = <100000>;
};
};
2.5.2 .dtsi 与 .dts 的分工
| 文件 | 内容范围 | 维护者 | 修改频率 |
|---|---|---|---|
| .dtsi | SoC 通用外设定义、时钟、中断控制器、总线节点 | SoC 厂商 | 低(芯片版本更新时) |
| .dts | 板级外设配置、GPIO 复用、设备启用/禁用 | 板级开发者 | 高(硬件改板时) |
2.6 设备树编译与反编译
掌握设备树的编译与反编译工具,是调试设备树问题的必备技能。
2.6.1 常用命令
# 编译 .dts 为 .dtb
dtc -I dts -O dtb -o board.dtb board.dts
# 反编译 .dtb 为 .dts(调试用)
dtc -I dtb -O dts -o board_decompiled.dts board.dtb
# 检查设备树语法错误
dtc -I dts -O dtb -o /dev/null board.dts
# 查看内核中当前使用的设备树
dtc -I fs -O dts /sys/firmware/devicetree/base
调试技巧:使用
dtc -I fs -O dts /sys/firmware/devicetree/base 可以查看内核实际解析的设备树内容,与源文件对比可发现编译或覆盖层应用中的问题。
2.7 设备树匹配驱动流程 SVG
2.8 本章小结
- 节点是设备树的骨架,从根节点
/开始形成树状层级,每个节点代表一个硬件设备或总线。 - 属性是节点的数据载体,
compatible用于驱动匹配,reg描述地址空间,interrupts描述中断资源。 - 标签提供节点引用机制,通过
&标签实现属性追加和 phandle 引用,是模块化设计的基础。 - 包含文件通过
#include实现 .dtsi 与 .dts 的分层设计,SoC 厂商维护 .dtsi,板级开发者维护 .dts。 - 掌握
dtc编译与反编译工具,能够有效调试设备树配置问题。