2、设备树基础语法:节点、属性、标签、引用、包含文件

设备树这东西,说白了就是一块板子的「硬件说明书」。你想想看,Linux 内核要跑起来,总得知道 CPU 接了什么外设、中断连到哪个引脚、时钟频率是多少吧?

我刚开始接触设备树时,觉得它就是个配置文件。后来踩了几个坑才明白——它其实是操作系统和硬件之间的「翻译官」。今天咱们就把设备树最核心的五个语法要素讲透。

2.1 节点——设备树的骨架

设备树里的每个硬件设备,都对应一个节点。节点用花括号 {} 包裹,格式长这样:

/dts-v1/;

/ {
    model = "MyBoard";
    compatible = "vendor,myboard";

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

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x40000000>;
    };
};

根节点 / 是整个设备树的起点。每个子节点代表一个设备,比如 cpu@0 就是第一个 CPU 核心。

我的习惯:节点名尽量用「设备类型@地址」的格式。比如 uart@1c20000,一看就知道是 UART,地址是 0x1c20000。这样后期维护时,不用翻手册就能定位。

2.2 属性——描述设备的「形容词」

属性就是节点的特征。常见的属性类型有:

属性类型 示例 说明
字符串 compatible = "vendor,device"; 用于匹配驱动
32位整数 reg = <0x10000000 0x1000>; 地址和大小
64位整数 reg = <0x00000000 0x80000000 0x00000000 0x40000000>; 高32位在前
布尔值 status = "disabled"; 控制设备启用/禁用
字节数组 local-mac-address = [00 11 22 33 44 55]; MAC 地址等

我在项目中遇到过最坑的事:有人把 reg 属性的地址写反了,结果驱动死活读不到寄存器。后来查了两天才发现——地址和大小必须成对出现,顺序不能乱。

2.3 标签——给节点起个「外号」

标签就是节点的别名。用 标签名: 放在节点名前:

uart1: serial@1c20000 {
    compatible = "snps,dw-apb-uart";
    reg = <0x1c20000 0x1000>;
    interrupts = <0 10 4>;
};

这里 uart1 就是标签。后面引用时,直接用 &uart1 就行。

为什么要用标签? 因为设备树里节点名可能很长,比如 serial@1c20000。你每次引用都写全名,手不酸吗?标签就是给节点起个短名,方便引用。

2.4 引用——把散落的节点「串起来」

引用用 &标签名 表示。最常见的场景是:

  • 在板级 dts 文件中,引用 SoC 级 dtsi 里的节点
  • 给已有节点添加属性或修改属性

举个例子:

// soc.dtsi 中定义了 uart1
uart1: serial@1c20000 {
    compatible = "snps,dw-apb-uart";
    status = "disabled";
};

// board.dts 中引用并启用
&uart1 {
    status = "okay";
    clock-frequency = <24000000>;
};

你看,引用之后可以追加属性,也可以覆盖已有属性。这招在项目里特别实用——SoC 厂商提供基础 dtsi,板级厂商只需要引用并修改少量属性就行。

我曾经踩过的坑:引用时如果写错了标签名,编译器不会报错,但节点根本不会被修改。我调试过一整天,最后发现是标签名拼写错了。所以,引用前一定要确认标签名存在。

2.5 包含文件——代码复用的「法宝」

设备树支持 #include 预处理指令,用法和 C 语言一样:

#include "soc.dtsi"
#include "myboard-pinctrl.dtsi"

/ {
    model = "MyBoard v2";
    compatible = "vendor,myboard-v2";
};

包含文件的好处很明显:

  • SoC 级定义(如 CPU、中断控制器、时钟)放在 .dtsi 文件中
  • 板级差异(如外设引脚、内存大小)放在 .dts 文件中
  • 多个板子可以共用同一个 .dtsi,减少重复代码

我建议你按这个层次组织文件:

  1. soc.dtsi:定义 SoC 内部所有外设,默认 status = "disabled"
  2. myboard.dtsi:定义板级通用配置(如内存、时钟)
  3. myboard-v1.dts / myboard-v2.dts:具体板型的差异化配置

这样分层后,维护起来特别清爽。我参与的一个项目有 8 款板子,全靠这套分层结构,每次改版只需要改一个 .dts 文件。

2.6 知识体系总览

下面这张图,把设备树语法的核心逻辑串起来了:

设备树语法核心要素 根节点 / 子节点(设备) 属性 compatible / reg / status 标签 uart1: serial@... 引用 &uart1 { ... } 包含文件 #include "soc.dtsi" 编译生成 .dtb 文件

嗯,这张图把今天讲的内容都串起来了。从根节点出发,到子节点,再到属性、标签、引用,最后通过包含文件组织成完整的设备树,编译成 .dtb 供内核使用。

给你个小建议:刚开始写设备树时,别急着写复杂结构。先画个草图,把节点层次、属性、引用关系理清楚。我每次写新板子的设备树,都会先在纸上画一遍,确认无误再动手。这习惯帮我省了不少调试时间。

设备树语法其实不复杂,核心就是这五个要素。你只要把节点、属性、标签、引用、包含文件这五样东西搞明白,剩下的就是多写多练了。

可可精品资料库