2. 设备树语法入门:节点与属性、标签与引用、包含文件与dtsi组织

设备树(Device Tree)是嵌入式Linux中用于描述硬件拓扑结构的扁平化数据格式。它使用节点(node)表示硬件设备,使用属性(property)描述设备特性,通过标签(label)与phandle实现跨节点引用,并借助#include与.dtsi文件实现模块化组织。掌握这些基础语法是编写正确设备树的前提。

2.1 节点(node)与属性(property)的定义

设备树的基本单元是节点。每个节点代表一个硬件设备或总线,节点内可包含子节点和属性。属性是键值对,用于描述设备的地址、中断、时钟、兼容性等信息。

2.1.1 节点语法

/dts-v1/;

/ {
    model = "TI AM335x BeagleBone Black";
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am335x";

    cpus {
        #address-cells = <1>;
        #size-cells = <0>;

        cpu@0 {
            compatible = "arm,cortex-a8";
            device_type = "cpu";
            reg = <0x00>;
        };
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x20000000>;  /* 512 MB */
    };
};
语法元素说明示例
节点名节点名称,可包含单元地址(@后跟地址)cpu@0、memory@80000000
属性键值对,值可为字符串、32位整数数组、字节数组等compatible = "arm,cortex-a8";
子节点节点内可嵌套子节点,形成树形结构cpus 内含 cpu@0
根节点用 / 表示,是设备树的顶层节点/ { ... };
提示:节点名中的 @ 后跟的地址仅用于区分同名节点,不一定是物理地址。例如 i2c@44e0b000 表示该I2C控制器的寄存器基地址为0x44E0B000。

2.1.2 常用属性类型

属性类型格式示例
字符串"string"model = "TI AM335x BeagleBone Black";
32位整数<value>reg = <0x80000000 0x20000000>;
64位整数<high low>reg = <0x00000000 0x80000000>;
字节数组[byte1 byte2 ...]local-mac-address = [00 11 22 33 44 55];
字符串列表"str1", "str2", ...compatible = "ti,am335x-bone", "ti,am335x";
布尔值存在即真status = "disabled"; 或 status = "okay";

2.2 标签(label)与引用(phandle)

设备树中,节点可以通过标签定义别名,并通过phandle(指针句柄)实现跨节点引用。这是设备树实现“覆盖”和“复用”的核心机制。

2.2.1 标签定义

标签是节点前的标识符,使用冒号分隔:

uart0: serial@44e09000 {
    compatible = "ti,am3352-uart", "ti,omap3-uart";
    reg = <0x44e09000 0x2000>;
    interrupts = <72>;
};

此处 uart0 即为标签,可在其他位置通过 &uart0 引用该节点。

2.2.2 phandle 引用

phandle 是设备树内部自动生成的唯一整数标识符,用于在属性中引用其他节点。开发者通常使用 &label 语法,编译器会自动转换为 phandle:

/* 引用 uart0 节点 */
chosen {
    stdout-path = &uart0;
};

/* 在属性中引用 */
clocks = <&clk_uart 0>;
关键区别:标签(label)是给开发者使用的符号名;phandle 是编译器生成的数字标识符。在 .dts 源文件中,始终使用 &label 语法,不要直接写 phandle 数字。

2.2.3 标签与引用的典型应用场景

  • 引脚复用配置:在 pinctrl 节点中引用 GPIO 控制器
  • 时钟/中断连接:引用时钟源或中断控制器
  • 设备覆盖(overlay):通过标签向已有节点追加属性或子节点
  • chosen 节点:设置内核启动参数和控制台路径
节点、标签与 phandle 引用关系 根节点 / uart0: serial@44e09000 标签: uart0 clk_uart: clock@44e00000 标签: clk_uart chosen stdout-path = &uart0; 引用机制说明 标签(label)是节点别名,通过 &label 语法引用 编译器自动将 &label 转换为 phandle 数字标识符

2.3 包含文件(#include)与 dtsi 文件组织

设备树源文件使用 #include 预处理指令引入公共定义,实现代码复用。扩展名为 .dtsi 的文件是设备树包含文件,通常存放 SoC 公共定义、外设模板和头文件宏。

2.3.1 文件类型与作用

扩展名类型作用
.dts设备树源文件板级设备树,描述具体硬件板卡
.dtsi设备树包含文件SoC 公共定义、IP 核模板、头文件
.dtb编译后的二进制内核引导时加载的设备树二进制
.dtbo设备树覆盖文件运行时动态加载的设备树片段

2.3.2 典型文件组织层次

/* 1. SoC 级公共定义:am33xx.dtsi */
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>

/ {
    compatible = "ti,am33xx";
    cpus { ... };
    ocp {
        uart0: serial@44e09000 { ... };
        i2c0: i2c@44e0b000 { ... };
        gpio0: gpio@44e07000 { ... };
    };
};

/* 2. 板级文件:am335x-boneblack.dts */
#include "am33xx.dtsi"
#include "am335x-bone-common.dtsi"

/ {
    model = "TI AM335x BeagleBone Black";
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am335x";

    &uart0 {
        status = "okay";
    };

    &i2c0 {
        status = "okay";
        clock-frequency = <400000>;
        tps65217: tps@24 { ... };
    };
};
注意:在 .dts 文件中使用 &label 语法引用 .dtsi 中定义的节点时,不能重复定义根节点 / 内的相同节点名,否则会导致编译冲突。正确的做法是使用 &label { ... }; 追加属性或子节点。

2.3.3 头文件与宏定义

设备树支持通过 #include 引入 C 语言头文件,使用宏定义提高可读性:

/* dt-bindings/gpio/gpio.h */
#define GPIO_ACTIVE_HIGH 0
#define GPIO_ACTIVE_LOW  1

/* 在 dts 中使用 */
reset-gpios = <&gpio0 7 GPIO_ACTIVE_LOW>;
enable-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
dtsi 文件组织层次 am335x-boneblack.dts am335x-bone-common.dtsi am33xx.dtsi dt-bindings/gpio/gpio.h dt-bindings/interrupt/irq.h dt-bindings/pinctrl/am33xx.h 文件组织原则 .dts 文件 #include .dtsi 文件,.dtsi 文件 #include 头文件 SoC 级定义放在 .dtsi,板级差异放在 .dts,宏定义放在 dt-bindings 头文件

2.4 语法避坑与调试技巧

常见错误:
  • 忘记分号:每个属性值后必须加分号 ;,节点结束用 };
  • 标签重复:同一 .dts/.dtsi 中标签名必须唯一
  • 引用未定义节点:使用 &label 前必须确保标签已定义
  • 地址/大小单元不匹配:#address-cells 和 #size-cells 必须与 reg 属性中的数值个数一致
调试技巧:
  • 使用 dtc -I dts -O dtb -o output.dtb input.dts 编译设备树
  • 使用 dtc -I dtb -O dts -o output.dts input.dtb 反编译查看二进制内容
  • 使用 fdtdump input.dtb 快速查看设备树结构
  • 内核启动时添加 earlycon 和 dtoc 参数可输出设备树解析日志

2.5 本章小结

知识点核心要点
节点与属性节点表示硬件设备,属性描述设备特性;根节点用 / 表示
标签与 phandle标签是节点别名,通过 &label 引用;编译器自动生成 phandle
包含文件#include 引入 .dtsi 和头文件,实现 SoC 级复用
文件组织.dts 板级文件 → .dtsi SoC 公共定义 → dt-bindings 头文件宏