2. DTS语法入门:DTS文件结构、节点定义、属性定义、标签与引用
设备树源文件(.dts)是描述硬件拓扑的文本文件,经过设备树编译器(dtc)编译为二进制的设备树二进制(.dtb)供内核使用。本章从最基础的语法单元开始,逐步构建完整的DTS文件认知。
学习提示:DTS语法与C语言结构体定义有相似之处,但更强调层级关系和键值对描述。掌握节点、属性、标签与引用这四大要素,是后续编写和修改设备树的基础。
2.1 DTS文件整体结构
一个典型的DTS文件由以下部分组成:
- 根节点“/”:设备树的起点,所有设备节点都挂载在根节点下。
- 节点(node):表示一个硬件设备或总线,可嵌套形成树状结构。
- 属性(property):描述节点的特征,如地址、中断号、时钟频率等。
- 标签(label):为节点或属性起别名,便于引用。
- 引用(phandle):通过标签或路径连接不同节点,实现跨节点关联。
- 包含文件(#include):引入.dtsi头文件,复用通用定义。
2.2 节点(node)定义
节点是设备树的基本组成单元,每个节点代表一个硬件设备、总线或控制器。节点定义格式如下:
[label:] node-name[@unit-address] {
[property-definitions]
[child-nodes]
};
节点名称由三部分组成:
| 部分 | 说明 | 示例 |
|---|---|---|
| label | 可选标签,用于在其他位置引用该节点 | uart0 |
| node-name | 通用名称,描述设备类型,如 serial、gpio |
serial |
| unit-address | 设备基地址或寄存器偏移,用于区分同类型设备 | fe201000 |
节点命名规范:
- 节点名称应使用通用名称,如
ethernet、i2c、spi,而非厂商特定名称。 - unit-address 通常为十六进制,不带
0x前缀。 - 根节点固定为
/,且不能有标签。
节点嵌套示例:
/ {
model = "Embedded Development Board";
compatible = "vendor,board-v1", "vendor,board";
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
uart0: serial@fe201000 {
compatible = "vendor,uart";
reg = <0xfe201000 0x1000>;
interrupts = <0 33 4>;
};
gpio0: gpio@fe200000 {
compatible = "vendor,gpio";
reg = <0xfe200000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
};
};
};
2.3 属性(property)定义
属性是节点的键值对,用于描述设备的硬件特性。属性值支持多种数据类型:
| 数据类型 | 语法 | 说明 | 示例 |
|---|---|---|---|
| 字符串 | "string" |
双引号包裹的文本 | compatible = "vendor,uart"; |
| 32位整数 | <value> |
尖括号包裹,支持十进制或十六进制 | reg = <0xfe201000 0x1000>; |
| 64位整数 | <0x00000000 0xvalue> |
两个32位整数拼接表示64位 | reg = <0x00000000 0xfe201000>; |
| 字节数组 | [hex bytes] |
方括号包裹的十六进制字节序列 | local-mac-address = [00 11 22 33 44 55]; |
| 布尔值 | 仅属性名,无值 | 存在即表示 true | gpio-controller; |
| 字符串列表 | "str1", "str2" |
逗号分隔的多个字符串 | compatible = "vendor,board", "simple-bus"; |
| 混合类型 | <value> "string" |
不同类型组合,用空格分隔 | foo = <0x1234> "bar"; |
常用标准属性:
compatible:驱动匹配的关键字,格式为"厂商,设备"。reg:描述设备地址空间,格式为<基地址 长度>。interrupts:描述中断号、触发类型等。clocks:引用时钟控制器和时钟 ID。pinctrl-0、pinctrl-names:引脚控制配置(后续章节详解)。status:设备状态,可选"okay"、"disabled"、"fail"。
常见错误:
- 忘记分号结尾:每个属性定义必须以
;结束。 - 整数格式错误:十六进制值必须使用
0x前缀,如0xfe201000。 - 字符串未加引号:字符串必须用双引号包裹。
2.4 标签(label)与引用(phandle)
标签和引用是设备树实现“跨节点连接”的核心机制,广泛应用于中断连接、时钟引用、pinctrl配置等场景。
2.4.1 标签定义
标签是节点或属性的别名,定义在节点名称之前,使用冒号分隔:
uart0: serial@fe201000 {
compatible = "vendor,uart";
reg = <0xfe201000 0x1000>;
interrupts = <0 33 4>;
};
标签命名规则:
- 以字母或下划线开头,后跟字母、数字、下划线。
- 通常使用设备功能缩写,如
uart0、i2c1、gpio_a。 - 同一文件内标签必须唯一。
2.4.2 引用(phandle)
引用通过 &label 语法,在其它节点中引用已定义的标签节点,实现属性追加或覆盖:
// 在 board.dts 中引用 uart0 并追加属性
&uart0 {
pinctrl-0 = <&uart0_pins>;
pinctrl-names = "default";
status = "okay";
};
引用机制的核心作用:
| 场景 | 说明 | 示例 |
|---|---|---|
| 追加属性 | 在 board.dts 中为 soc.dtsi 定义的节点添加板级特定属性 | &uart0 { pinctrl-0 = ... }; |
| 覆盖属性 | 重新定义已有属性值 | &clk { clock-frequency = <50000000>; }; |
| 跨节点连接 | 中断控制器、时钟、GPIO 等引用 | interrupt-parent = &gic; |
| pinctrl 配置 | 引用 pinmux 节点配置引脚功能 | pinctrl-0 = <&uart0_pins>; |
2.5 包含文件与设备树分层
实际项目中,设备树通常分为多个文件,通过 #include 指令组合:
| 文件类型 | 扩展名 | 用途 |
|---|---|---|
| SoC 通用定义 | .dtsi |
定义 SoC 内部所有外设节点、中断控制器、时钟等 |
| 板级定义 | .dts |
引用 .dtsi,添加板级特定属性(引脚、外设使能等) |
| 头文件 | .h |
定义宏、常量、中断号等,通过 #include 引入 |
典型文件组织示例:
// soc.dtsi - SoC 通用定义
/ {
compatible = "vendor,soc";
#address-cells = <1>;
#size-cells = <1>;
uart0: serial@fe201000 {
compatible = "vendor,uart";
reg = <0xfe201000 0x1000>;
interrupts = <0 33 4>;
status = "disabled"; // 默认关闭
};
};
// board.dts - 板级定义
#include "soc.dtsi"
/ {
model = "My Board";
compatible = "vendor,myboard";
&uart0 {
pinctrl-0 = <&uart0_pins>;
pinctrl-names = "default";
status = "okay"; // 板级使能
};
};
最佳实践:
- SoC 通用外设在 .dtsi 中定义为
status = "disabled",板级文件按需使能。 - 使用
#include而非/include/旧语法,保持与 C 预处理器一致。 - 标签命名统一风格,如
uart0、i2c1、spi2,便于维护。
2.6 本章小结
本章覆盖了DTS语法的四大核心要素:
- 节点:硬件设备的树状表示,支持嵌套和地址区分。
- 属性:描述设备特征的键值对,支持多种数据类型。
- 标签:为节点起别名,便于跨文件引用。
- 引用:通过
&label实现属性追加、覆盖和跨节点连接。