2、设备树(DTS)基础:设备树的作用、基本语法(节点、属性、标签)、dts/dtsi/dtb文件关系
2.1 设备树的作用与设计初衷
设备树(Device Tree)是一种描述硬件资源的数据结构,用于将硬件配置信息从内核代码中分离出来。在嵌入式Linux系统中,同一款SoC可能被用于成百上千种不同外设组合的板卡,传统方式需要为每种板卡编写大量板级文件(如 arch/arm/mach-xxx),导致内核代码臃肿且难以维护。
设备树的核心作用包括:
- 硬件描述标准化:以统一格式描述CPU、内存、总线、外设、中断、GPIO、时钟等资源。
- 驱动与硬件解耦:驱动通过设备树节点名称和兼容属性匹配硬件,无需硬编码地址或资源。
- 多板卡支持:同一内核镜像通过更换设备树二进制文件(DTB)即可适配不同硬件。
- 减少内核补丁:硬件变更只需修改DTS文件,无需修改内核C代码。
工程提示:在驱动移植过程中,超过70%的适配问题源于设备树节点描述错误或遗漏。掌握设备树语法是排查硬件资源冲突、中断号错误、时钟配置失败等问题的前提。
2.2 设备树基本语法
设备树源文件(DTS)采用树形层级结构,由节点(node)、属性(property)和标签(label)三要素构成。每个节点代表一个硬件设备或总线,属性描述该设备的配置参数。
2.2.1 节点(Node)
节点是设备树的基本单元,格式为 节点名@地址 { ... };。根节点为 /,所有设备节点均挂载在根节点下。
/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>; /* 256MB RAM */
};
leds {
compatible = "gpio-leds";
led0 {
label = "beaglebone:green:usr0";
gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>;
linux,default-trigger = "heartbeat";
};
};
};
2.2.2 属性(Property)
属性以键值对形式存在,常见属性类型如下表:
| 属性类型 | 格式示例 | 说明 |
|---|---|---|
| 字符串 | compatible = "ti,am335x-bone"; |
驱动匹配标识,优先使用"厂商,型号"格式 |
| 32位整数 | reg = <0x48000000 0x1000>; |
地址和长度,可包含多个地址段 |
| 64位整数 | reg = <0x00000000 0x80000000 0x00000000 0x10000000>; |
高32位在前,低32位在后 |
| 布尔值 | status = "disabled"; |
存在即表示true,常用"okay"/"disabled" |
| 数组 | interrupts = <0 31 4>; |
中断控制器、类型、触发方式 |
| 字节数组 | local-mac-address = [00 11 22 33 44 55]; |
MAC地址等二进制数据 |
| 引用 | clocks = <&clkc 12>; |
通过标签引用其他节点 |
2.2.3 标签(Label)
标签为节点提供别名,方便在其他位置引用。标签定义在节点名之前,使用 &标签名 引用。
/* 定义标签 */
uart0: serial@44e09000 {
compatible = "ti,am3352-uart", "ti,omap3-uart";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
};
/* 在其他位置引用标签 */
chosen {
stdout-path = &uart0; /* 引用uart0节点 */
};
/* 通过标签追加属性 */
&uart0 {
pinctrl-0 = <&uart0_pins>;
status = "okay";
};
常见错误:标签引用时使用
&标签名,而非 &节点名。节点名可能重复(如不同总线下的同名设备),但标签在整个设备树中必须唯一。
2.3 dts/dtsi/dtb 文件关系
设备树涉及三种文件类型,它们之间的关系构成了从源码到二进制、从SoC通用描述到板级定制的完整链路。
| 文件类型 | 扩展名 | 作用 | 是否可编译 |
|---|---|---|---|
| 设备树源文件 | .dts | 板级硬件描述,包含完整设备树 | 是(编译为.dtb) |
| 设备树头文件 | .dtsi | SoC通用描述或共用外设定义,可被.dts包含 | 否(被.dts包含后编译) |
| 设备树二进制 | .dtb | 编译后的二进制文件,由Bootloader加载传递给内核 | 是(由.dts编译生成) |
典型包含关系示例:
/* am335x-boneblack.dts */
#include "am33xx.dtsi" /* SoC通用定义 */
#include "am335x-bone-common.dtsi" /* 板级共用定义 */
/ {
model = "TI AM335x BeagleBone Black";
compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";
/* 板级特有设备 */
&lcdc {
status = "okay";
};
};
编译命令:
反编译查看二进制内容:
dtc -I dts -O dtb -o am335x-boneblack.dtb am335x-boneblack.dts反编译查看二进制内容:
dtc -I dtb -O dts -o am335x-boneblack.dts am335x-boneblack.dtb
2.4 设备树节点层级与地址编码
设备树通过 reg 属性和 #address-cells / #size-cells 描述设备地址空间。父节点定义地址编码规则,子节点使用 reg 描述具体地址和长度。
/* 地址编码示例 */
soc {
compatible = "simple-bus";
#address-cells = <1>; /* 地址用1个32位单元表示 */
#size-cells = <1>; /* 长度用1个32位单元表示 */
gpio0: gpio@44e07000 {
compatible = "ti,omap4-gpio";
reg = <0x44e07000 0x1000>; /* 基地址0x44e07000,长度0x1000 */
interrupts = <96>;
gpio-controller;
#gpio-cells = <2>;
};
uart0: serial@44e09000 {
compatible = "ti,am3352-uart";
reg = <0x44e09000 0x2000>;
interrupts = <72>;
};
};
2.5 驱动匹配机制:compatible 属性
设备树驱动的核心匹配依据是 compatible 属性。内核通过设备树节点的 compatible 字符串与驱动中 of_match_table 进行匹配,匹配成功后调用驱动的 probe 函数。
/* 设备树节点 */
i2c0: i2c@44e0b000 {
compatible = "ti,am3352-i2c", "ti,omap4-i2c";
reg = <0x44e0b000 0x1000>;
interrupts = <70>;
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
};
/* 驱动匹配表 */
static const struct of_device_id omap_i2c_of_match[] = {
{ .compatible = "ti,omap4-i2c", },
{ .compatible = "ti,am3352-i2c", },
{ },
};
MODULE_DEVICE_TABLE(of, omap_i2c_of_match);
匹配优先级:内核遍历
compatible 属性中的字符串列表,按顺序与驱动匹配表中的条目比较。通常将最具体的型号放在最前面(如 "ti,am3352-i2c"),通用型号放在后面(如 "ti,omap4-i2c")。
2.6 设备树排查常用命令
| 命令 | 用途 | 示例 |
|---|---|---|
dtc |
编译/反编译设备树 | dtc -I dtb -O dts -o output.dts input.dtb |
ls /sys/firmware/devicetree/base/ |
查看内核当前使用的设备树 | 遍历所有节点和属性 |
cat /proc/device-tree/model |
查看板卡型号 | TI AM335x BeagleBone Black |
of_* API |
驱动中解析设备树属性 | of_property_read_u32() |
fdtdump |
以可读格式转储DTB | fdtdump input.dtb |
排查要点:当驱动无法加载时,首先检查
/sys/firmware/devicetree/base/ 下是否存在对应节点,其次确认 compatible 属性字符串与驱动匹配表完全一致(包括大小写和标点符号)。