2. 设备树语法入门:DTS文件结构、节点、属性、标签与包含文件
设备树(Device Tree)是一种描述硬件资源的数据结构,它使用树形节点来记录CPU、内存、外设、中断、GPIO等硬件信息。在嵌入式Linux中,设备树源文件(.dts)经过编译生成设备树二进制文件(.dtb),由Bootloader传递给内核,内核据此完成设备驱动与硬件资源的绑定。
2.1 DTS文件整体结构
一个典型的.dts文件由以下部分组成:
- 根节点:
/,整个设备树的起点。 - 节点(node):描述一个硬件设备或总线,可嵌套子节点。
- 属性(property):键值对,描述节点的特性(地址、中断、时钟等)。
- 标签(label):为节点或属性起别名,便于引用。
- 包含文件(dtsi):通过
#include引入公共硬件描述,实现复用。
💡 核心原则:设备树只描述硬件“有什么、在哪里”,不描述驱动“怎么做”。驱动通过匹配
compatible属性来绑定节点。
2.2 节点(node)
节点是设备树的基本单元,每个节点代表一个硬件设备、总线或功能块。节点可以嵌套,形成树状层级。
/dts-v1/;
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am335x";
cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a8";
reg = <0x0>;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>; /* 512 MB */
};
&i2c0 {
status = "okay";
eeprom@50 {
compatible = "atmel,24c32";
reg = <0x50>;
};
};
};
| 节点语法要素 | 说明 | 示例 |
|---|---|---|
| 节点名称 | 格式:name@unit-address,unit-address通常为寄存器基址 |
cpu@0、i2c@4802a000 |
| 子节点 | 在父节点花括号内定义,缩进表示层级 | eeprom@50 是 i2c0 的子节点 |
| 节点引用 | 使用 &label 在根节点外追加或修改节点 |
&i2c0 { status = "okay"; }; |
| 特殊节点 | / 根节点、aliases、chosen、memory |
chosen { bootargs = "..."; }; |
2.3 属性(property)
属性是节点的数据载体,驱动通过读取属性获取硬件参数。属性值支持多种数据类型。
| 属性类型 | 语法 | 示例 | 用途 |
|---|---|---|---|
| 字符串 | "string" |
compatible = "ti,am335x-bone"; |
设备型号、驱动匹配 |
| 32位整数 | <value> |
reg = <0x4802a000 0x1000>; |
地址、大小、中断号 |
| 64位整数 | <0x00000000 0x80000000> |
reg = <0x00000000 0x80000000 0x00000000 0x20000000>; |
64位地址空间 |
| 字节数组 | [hex bytes] |
local-mac-address = [00 11 22 33 44 55]; |
MAC地址、UUID |
| 布尔值 | 仅属性名,无值 | status = "disabled"; 或 status = "okay"; |
设备启用/禁用 |
| 字符串列表 | "str1", "str2" |
compatible = "ti,am335x-bone-black", "ti,am335x-bone"; |
多级兼容匹配 |
🔑 关键属性:
compatible:驱动匹配的核心,格式为 "厂商,型号"。reg:描述设备地址空间,格式为 <基址 长度>。interrupts:描述中断号与触发类型。status:"okay" 启用,"disabled" 禁用。
2.4 标签(label)
标签为节点或属性提供别名,便于在多个.dts/.dtsi文件中引用和修改节点。
// 定义标签
i2c0: i2c@4802a000 {
compatible = "ti,omap4-i2c";
reg = <0x4802a000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
};
// 在其他文件中通过 & 引用标签,追加或修改属性
&i2c0 {
status = "okay";
clock-frequency = <400000>;
};
| 标签用法 | 语法 | 说明 |
|---|---|---|
| 定义标签 | label: node-name@addr { ... }; |
标签放在节点名称前,用冒号分隔 |
| 引用标签 | &label { ... }; |
在根节点外使用 & 引用,追加或覆盖属性 |
| 标签作用域 | 全局可见 | 跨文件引用,常用于 .dtsi 定义,.dts 定制 |
2.5 包含文件(dtsi)
.dtsi 文件是设备树的“头文件”,用于存放SoC公共硬件描述。一个SoC平台通常有一个主.dtsi,多个板级.dts通过#include引入并定制。
// am335x.dtsi (SoC公共定义)
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
/ {
compatible = "ti,am335x";
ocp {
uart0: serial@44e09000 {
compatible = "ti,am335x-uart";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
status = "disabled";
};
};
};
// am335x-boneblack.dts (板级定制)
/dts-v1/;
#include "am335x.dtsi"
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am335x";
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>;
};
};
&uart0 {
status = "okay";
};
⚠️ 常见错误:
1. 忘记写
2.
3. 在
4. 标签名冲突,不同
1. 忘记写
/dts-v1/; 版本声明,导致编译失败。2.
#include 路径错误,找不到 .dtsi 文件。3. 在
.dtsi 中直接设置 status = "okay",导致所有板级都启用该设备,失去定制灵活性。4. 标签名冲突,不同
.dtsi 中定义了同名标签。
2.6 设备树语法结构总览
2.7 设备树编译与反编译
| 操作 | 命令 | 说明 |
|---|---|---|
| 编译 | dtc -I dts -O dtb -o output.dtb input.dts |
将 .dts 编译为 .dtb |
| 反编译 | dtc -I dtb -O dts -o output.dts input.dtb |
将 .dtb 反编译为 .dts,用于调试 |
| 检查 | dtc -I dtb -O dts -o /dev/null input.dtb |
仅检查语法,不输出文件 |
| 生成符号 | dtc -I dts -O dtb -@ -o output.dtb input.dts |
-@ 生成符号节点,支持插件 |
📌 工程实践要点:
- SoC公共定义放在
.dtsi,板级定制放在.dts,通过#include组合。 - 使用标签 +
&引用修改节点,避免重复定义整个节点。 - 所有设备默认
status = "disabled",板级文件按需启用。 - 编译时使用
-@选项生成符号,便于后续设备树插件(overlay)加载。