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

设备树节点层级结构 / (根节点) cpu@0 memory@80000000 soc uart0: serial@1c090000 节点层级说明 • 根节点 / 是设备树的入口,所有设备节点都是其子节点或后代节点。 • cpu@0 和 memory@80000000 是根节点的直接子节点,描述 CPU 和内存。 • soc 节点作为内部总线,包含 uart0 等外设节点,形成二级层级。 • 节点路径示例:/soc/uart0 或 /soc/serial@1c090000。

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

设备树节点与 Platform 驱动匹配流程 .dts / .dtsi 源文件 dtc 编译 .dtb 二进制文件 内核解析 device_node 创建 platform_device of_match_table 匹配 probe() 执行 匹配关键:驱动中的 .of_match_table 指向的 compatible 字符串必须与设备树节点中的 compatible 属性完全一致。 匹配成功后,内核调用驱动的 probe 函数,驱动通过 of_* 系列 API 获取 reg、interrupts 等资源。

2.8 本章小结

  • 节点是设备树的骨架,从根节点 / 开始形成树状层级,每个节点代表一个硬件设备或总线。
  • 属性是节点的数据载体,compatible 用于驱动匹配,reg 描述地址空间,interrupts 描述中断资源。
  • 标签提供节点引用机制,通过 &标签 实现属性追加和 phandle 引用,是模块化设计的基础。
  • 包含文件通过 #include 实现 .dtsi 与 .dts 的分层设计,SoC 厂商维护 .dtsi,板级开发者维护 .dts。
  • 掌握 dtc 编译与反编译工具,能够有效调试设备树配置问题。