2. DTS 语法入门:节点、属性、标签与包含文件
设备树源文件(.dts)是一种结构化的、人类可读的硬件描述语言。其核心语法围绕四个基本元素展开:节点(Node)、属性(Property)、标签(Label) 和 包含文件(#include)。掌握这些基础语法是编写和调试 DTS 文件的起点。
💡 学习提示: DTS 语法与 C 语言风格类似,使用大括号
{ } 表示层级,分号 ; 结束语句。注释支持 // 和 /* */ 两种形式。
2.1 节点(Node):硬件拓扑的骨架
节点是设备树的基本组成单元,用于描述一个硬件设备或总线。每个节点代表一个设备,节点内部可以包含子节点,形成树状结构。根节点 / 是整个设备树的起点。
/ {
// 根节点,代表整个 SoC 板级系统
model = "Embedded Linux Board v1.0";
compatible = "vendor,board-name";
// 子节点:CPU 描述
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a7";
reg = <0x0>;
};
};
// 子节点:内存描述
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>; // 起始地址 0x80000000,大小 512MB
};
// 子节点:串口设备
uart0: serial@10000000 {
compatible = "ns16550a";
reg = <0x10000000 0x1000>;
interrupts = <0 33 4>;
};
};
📌 节点命名规则: 节点名格式为
node-name@unit-address。其中 node-name 是通用名称(如 serial、i2c),unit-address 是设备在总线上的地址(如寄存器基地址、I2C 从机地址),用于区分同一级别的多个相同类型节点。
2.2 属性(Property):描述设备的元数据
属性是附着在节点上的键值对,用于描述设备的特性、地址、中断、时钟等信息。属性值可以是空、整数、字符串、字节数组或这些类型的组合。
| 属性类型 | 语法格式 | 示例 | 说明 |
|---|---|---|---|
| 空属性 | property-name; |
status = "disabled"; |
布尔标志,存在即表示 true |
| 字符串 | property-name = "string"; |
compatible = "ns16550a"; |
用双引号包裹 |
| 整数(32位) | property-name = <value>; |
reg = <0x10000000 0x1000>; |
尖括号内可包含多个 32 位值 |
| 64位整数 | property-name = <high low>; |
reg = <0x0 0x80000000 0x0 0x20000000>; |
用两个 32 位值拼接表示 |
| 字节数组 | property-name = [hex bytes]; |
local-mac-address = [00 11 22 33 44 55]; |
方括号内为十六进制字节 |
| 字符串列表 | property-name = "str1", "str2"; |
compatible = "vendor,device", "generic-device"; |
逗号分隔多个字符串 |
| 混合类型 | property-name = <value>, "string", [bytes]; |
gpios = <&gpio0 17 0>; |
不同类型值用逗号组合 |
⚠️ 关键属性:compatible 是设备与驱动匹配的核心属性。格式为
"厂商,设备型号",如 "ti,am335x-uart"。内核通过该字符串查找对应的 platform_driver。
2.3 标签(Label):节点的引用别名
标签是给节点起的一个别名,用于在 DTS 文件的其他位置引用该节点。标签定义在节点名前,使用冒号 : 分隔。引用时使用 &label 语法。
// 定义标签
uart0: serial@10000000 {
compatible = "ns16550a";
reg = <0x10000000 0x1000>;
interrupts = <0 33 4>;
};
// 在其他地方引用标签,追加或修改属性
&uart0 {
pinctrl-0 = <&uart0_pins>;
status = "okay";
};
🔗 标签的典型用途:
- 板级覆盖: 在
.dtsi文件中定义基础节点并打上标签,在.dts文件中通过&label引用并覆盖属性。 - 引脚复用: 引用
&gpio0或&pinctrl节点来配置 GPIO 或引脚功能。 - 中断连接: 在
interrupt-parent属性中引用中断控制器节点。
2.4 包含文件(#include):模块化与复用
DTS 支持 C 预处理器指令 #include,用于引入头文件(.h)或其他设备树源文件(.dtsi)。这是实现设备树模块化和复用的关键机制。
// 包含 SoC 级设备树头文件,定义宏和通用节点
#include "am335x.dtsi"
// 包含板级特定配置
#include "am335x-bone-common.dtsi"
// 包含 GPIO 编号宏定义
#include <dt-bindings/gpio/gpio.h>
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
// 使用宏定义 GPIO 引脚
led0 {
compatible = "gpio-leds";
pinctrl-0 = <&user_led_pins>;
led@0 {
label = "usr0";
gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>; // GPIO1_21
default-state = "on";
};
};
};
| 文件类型 | 扩展名 | 用途 | 示例 |
|---|---|---|---|
| 设备树源文件 | .dts |
板级主文件,包含根节点和板级特定配置 | am335x-boneblack.dts |
| 设备树包含文件 | .dtsi |
SoC 级或通用板级定义,被 .dts 包含 |
am335x.dtsi |
| 头文件 | .h |
定义宏常量(如 GPIO 编号、中断类型) | dt-bindings/gpio/gpio.h |
2.5 实战要点与常见错误
🚨 常见语法错误:
- 缺少分号: 每个属性赋值和节点结束大括号后必须有
;。 - 地址格式错误:
reg属性中的地址和长度必须与#address-cells、#size-cells匹配。 - 标签重复: 同一 DTS 编译单元内标签名必须唯一。
- 路径引用错误: 使用
&label时确保标签已定义,且引用位置在标签作用域内。
🛠️ 调试技巧: 使用
dtc -I dts -O dtb -o output.dtb input.dts 编译 DTS 文件。如果语法有误,dtc 会给出具体的行号和错误描述。使用 dtc -I dtb -O dts -o output.dts input.dtb 可以反编译 .dtb 文件,用于验证编译结果。