1. 设备树基础:设备树(DTS)的起源、作用与基本语法结构
1.1 设备树的起源与背景
在早期的 Linux 内核中,板级硬件信息(如内存基址、中断号、GPIO 映射)被硬编码在 arch/arm/mach-xxx 目录下的 C 语言板文件中。这种“代码即配置”的方式导致内核源码充斥着大量重复的、与平台相关的硬件描述代码,每增加一款新硬件就需要修改内核源码并重新编译。
为解决上述问题,PowerPC 社区率先提出了 设备树(Device Tree) 概念,随后被 ARM、RISC-V、MIPS 等架构广泛采纳。设备树是一种描述硬件资源的、与操作系统无关的数据结构,它将硬件配置从内核代码中剥离出来,以 .dts(Device Tree Source)文本文件的形式独立存在,编译后生成 .dtb(Device Tree Blob)二进制文件,由 bootloader 传递给内核。
| 对比维度 | 传统板文件(Board File) | 设备树(Device Tree) |
|---|---|---|
| 硬件描述位置 | 内核源码 arch/arm/mach-xxx |
独立 .dts 文件 |
| 修改硬件配置 | 修改 C 代码,重新编译内核 | 修改 .dts,重新编译 DTB |
| 内核与硬件耦合度 | 高耦合,每板一份内核镜像 | 低耦合,同一内核镜像支持多板 |
| 厂商参与门槛 | 需提交内核补丁 | 只需提供 DTS 文件 |
| 可维护性 | 差,冗余代码多 | 好,结构清晰 |
1.2 设备树的核心作用
设备树在 Linux 内核启动过程中扮演着“硬件翻译官”的角色,其核心作用可归纳为以下四点:
- 硬件资源描述: 向内核传递 CPU 数量、内存布局、外设寄存器地址、中断号、DMA 通道、时钟频率等硬件参数。
- 驱动匹配: 通过
compatible属性将设备节点与对应的驱动程序绑定,实现“设备-驱动”的自动匹配。 - 配置灵活性: 允许在不修改内核源码的前提下,通过修改 DTS 文件来调整引脚复用、中断触发方式、电源管理等板级参数。
- 多平台统一: 同一份内核镜像可以加载不同的 DTB 文件,从而支持多种硬件平台,实现“一次编译,到处运行”。
1.3 设备树的基本语法结构
设备树源文件(.dts)采用树形层次结构,以节点(node)和属性(property)为核心元素。以下是一个典型的设备树片段:
/dts-v1/;
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
chosen {
stdout-path = &uart0;
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x10000000>; /* 512 MB */
};
leds {
compatible = "gpio-leds";
led0 {
label = "beaglebone:green:usr0";
gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
};
};
uart0: serial@44e09000 {
compatible = "ti,am3352-uart", "ti,omap3-uart";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
clocks = <&uart0_clk>;
};
};
1.3.1 节点(Node)
节点是设备树的基本组成单元,每个节点代表一个硬件设备或一个逻辑功能块。节点名称格式为 node-name@unit-address,其中 unit-address 通常为设备寄存器基址,用于区分同一层级下的同名节点。
- 根节点: 用
/表示,是整个设备树的起点,包含所有其他节点。 - 子节点: 在根节点内部通过缩进定义,可以嵌套多层。
- 标签(Label): 如
uart0:,用于在 DTS 文件中引用节点,编译时会被替换为节点的路径。
1.3.2 属性(Property)
属性用于描述节点的特征,采用 key = value; 的形式。常见的属性类型包括:
| 属性类型 | 语法格式 | 示例 | 说明 |
|---|---|---|---|
| 字符串 | "string" |
model = "TI AM335x BeagleBone Black"; |
描述设备型号 |
| 字符串列表 | "str1", "str2" |
compatible = "ti,am3352-uart", "ti,omap3-uart"; |
驱动兼容性匹配 |
| 32 位整数 | <value> |
interrupts = <72>; |
中断号、时钟频率等 |
| 64 位整数 | <0x00000000 0xvalue> |
reg = <0x00000000 0x80000000>; |
大地址空间(如 64 位内存基址) |
| 字节数组 | [hex bytes] |
local-mac-address = [00 11 22 33 44 55]; |
MAC 地址、UUID 等 |
| 布尔值 | 仅属性名,无值 | status = "disabled"; |
控制设备启用/禁用 |
| 引用(phandle) | &label |
clocks = <&uart0_clk>; |
引用其他节点 |
1.3.3 标准属性详解
设备树规范定义了一系列标准属性,用于统一硬件描述方式:
compatible: 最重要的属性,格式为"厂商,设备型号",内核通过该属性查找匹配的驱动程序。匹配顺序为从最具体到最通用。reg: 描述设备地址空间,格式为<基址 长度>,可包含多组地址。interrupts: 描述设备使用的中断号,具体含义由父节点的#interrupt-cells决定。status: 设备状态,常用值包括"okay"(启用)、"disabled"(禁用)、"fail"(故障)。device_type: 早期规范遗留属性,主要用于内存节点(device_type = "memory"),新设备建议避免使用。
1.4 设备树层次结构与寻址
设备树的树形结构通过父子节点关系来体现硬件的总线拓扑。每个节点可以包含 #address-cells 和 #size-cells 属性,用于定义子节点 reg 属性中地址和长度的编码位数。
soc {
#address-cells = <1>;
#size-cells = <1>;
gpio0: gpio@44e07000 {
compatible = "ti,omap4-gpio";
reg = <0x44e07000 0x1000>;
interrupts = <96>;
#gpio-cells = <2>;
gpio-controller;
};
i2c0: i2c@44e0b000 {
compatible = "ti,omap4-i2c";
reg = <0x44e0b000 0x1000>;
interrupts = <70>;
#address-cells = <1>;
#size-cells = <0>;
eeprom@50 {
compatible = "atmel,24c32";
reg = <0x50>;
};
};
};
在上例中:
soc节点定义#address-cells = <1>和#size-cells = <1>,因此其子节点(如gpio0)的reg包含两个 32 位整数:基址和长度。i2c0节点定义#size-cells = <0>,因此其子节点(如eeprom@50)的reg只包含一个 32 位整数:I2C 从设备地址。
#address-cells 和 #size-cells,或者子节点 reg 的整数个数与父节点定义不匹配,会导致内核解析地址失败,设备无法被正确识别。
1.5 设备树编译与使用流程
设备树的完整工作流程包括编写、编译、加载和解析四个阶段:
- 编写 DTS: 开发者根据硬件原理图编写
.dts源文件,或通过#include包含.dtsi(设备树包含文件)来复用 SoC 公共定义。 - 编译 DTB: 使用
dtc(Device Tree Compiler)工具将.dts编译为二进制.dtb文件。 - 加载 DTB: Bootloader(如 U-Boot)从存储介质(eMMC、SD 卡、网络)读取
.dtb文件,并将其加载到内存中,将地址传递给内核。 - 内核解析: 内核启动早期调用
unflatten_device_tree()函数,将 DTB 展开为device_node树结构,供驱动子系统使用。
# 编译设备树
dtc -I dts -O dtb -o am335x-boneblack.dtb am335x-boneblack.dts
# 反编译设备树(调试用)
dtc -I dtb -O dts -o am335x-boneblack.dts am335x-boneblack.dtb
1.6 设备树结构总览图
下图展示了设备树从源文件到内核解析的完整结构关系:
1.7 本章小结
设备树是嵌入式 Linux 开发中不可或缺的硬件描述语言,它将硬件配置从内核代码中解耦出来,极大地提高了代码的可移植性和可维护性。掌握 DTS 的基本语法——节点、属性、标准属性以及地址编码规则——是后续学习中断控制器、GPIO 配置、时钟管理等高级主题的基础。