1. 设备树基础回顾:DTS 语法、节点、属性、reg、interrupts 等核心概念
设备树(Device Tree)是一种描述硬件资源的数据结构,它把原本固化在 Linux 内核源码中的硬件信息抽取为独立文本,使同一份内核镜像能适配不同硬件平台。在嵌入式 Linux 开发中,设备树是驱动工程师必须掌握的基础设施。
1.1 设备树文件体系
| 文件类型 | 扩展名 | 说明 |
|---|---|---|
| 设备树源文件 | .dts |
人类可读的硬件描述文本,由开发者编写 |
| 设备树头文件 | .dtsi |
可被 .dts 通过 #include 包含,用于 SoC 公共定义 |
| 设备树二进制 | .dtb |
由 DTC 编译器将 .dts 编译生成的二进制 blob |
| 设备树 Overlay 源文件 | .dtso |
描述增量修改的源文件,编译为 .dtbo |
| 设备树 Overlay 二进制 | .dtbo |
运行时动态加载的 overlay 片段 |
工程提示: 在 Linux 5.x 之后,Overlay 源文件推荐使用
.dtso 后缀,与普通 .dts 区分。DTC 编译器通过 -@ 选项生成带符号表的 .dtbo,这是动态加载的前提。
1.2 DTS 语法基础
设备树采用树形结构,根节点是 /,每个节点代表一个硬件设备或总线,节点内包含若干属性描述设备特征。
/dts-v1/;
/ {
compatible = "acme,coyote-rev1";
#address-cells = <1>;
#size-cells = <1>;
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "arm,cortex-a9";
reg = <0x0>;
device_type = "cpu";
};
};
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x20000000>;
};
serial@101f1000 {
compatible = "ns16550";
reg = <0x101f1000 0x1000>;
interrupts = <0x0 0x1c 0x4>;
clock-frequency = <3686400>;
};
};
1.3 节点命名与路径
每个节点由 节点名 和可选的 单元地址 组成,格式为 node-name@unit-address。单元地址通常与 reg 属性的首地址一致,用于区分同一层级下的多个同类型设备。
- 节点名:描述设备类型,如
serial、i2c、gpio。 - 单元地址:可选,当存在多个相同节点名时用于区分,如
cpu@0、cpu@1。 - 路径:从根节点到目标节点的全路径,如
/soc/i2c@7e804000/eeprom@50。 - 标签(Label):为节点起别名,方便引用,如
uart0: serial@101f1000。
关键规则: 同一父节点下,子节点的
node-name@unit-address 必须唯一。如果省略单元地址,则节点名本身必须唯一。
1.4 核心属性详解
1.4.1 compatible
设备驱动匹配的核心属性,格式为 "厂商,型号"。内核通过该属性查找对应的 platform_driver。推荐使用多个值,从最具体到最通用。
compatible = "ti,am335x-uart", "ns16550";
1.4.2 reg 与地址编码
reg 描述设备在总线上的地址空间,由 #address-cells 和 #size-cells 决定每个地址和长度的字长(32 位或 64 位)。
| 属性 | 含义 | 示例值 |
|---|---|---|
#address-cells |
地址字段占用的 32 位单元数 | <1> 表示 32 位地址 |
#size-cells |
长度字段占用的 32 位单元数 | <1> 表示 32 位长度 |
reg |
地址 + 长度对,可包含多组 | <0x101f1000 0x1000> |
// 64 位地址示例
soc {
#address-cells = <2>;
#size-cells = <2>;
dma@0x100000000 {
reg = <0x1 0x00000000 0x0 0x100000>;
// 地址: 0x1_00000000, 长度: 0x100000
};
};
1.4.3 interrupts 与中断映射
interrupts 属性描述设备的中断号、触发类型等信息,其解释依赖于父节点的 #interrupt-cells。常见的中断控制器类型:
| 中断控制器 | #interrupt-cells | 中断描述格式 |
|---|---|---|
| GIC(通用中断控制器) | 3 | <中断类型 中断号 触发类型> |
| 简单中断控制器 | 1 | <中断号> |
| TI 特定中断控制器 | 2 | <中断号 触发类型> |
// GIC 中断示例:SPI 中断 28,上升沿触发
interrupts = <0x0 0x1c 0x4>;
// 字段说明:
// 0x0 – 中断类型:0=SPI(共享外设中断),1=PPI(私有外设中断)
// 0x1c – 中断号(28)
// 0x4 – 触发类型:1=上升沿,2=下降沿,4=高电平,8=低电平
常见错误: 中断号在设备树中通常从 0 开始编号,而芯片手册可能从 1 开始。务必核对 GIC 规范或 SoC 参考手册,否则中断无法正确路由。
1.5 设备树核心结构图
1.6 特殊属性与节点
| 属性/节点 | 作用 | 典型用法 |
|---|---|---|
status |
控制设备启用状态 | "okay" 启用,"disabled" 禁用 |
phandle |
为节点分配唯一数字句柄 | 由 DTC 自动生成,用于跨节点引用 |
aliases |
为节点路径定义短别名 | serial0 = &uart0; |
chosen |
传递运行时参数(如启动参数) | bootargs = "console=ttyS0,115200"; |
gpios |
描述 GPIO 引脚绑定 | <&gpio1 15 0> |
1.7 设备树 Overlay 的入口:& 引用语法
Overlay 的核心机制是通过 &label 引用基树中的节点,然后添加或覆盖属性。这种语法使得 Overlay 文件只描述增量修改,无需重复整个树结构。
// 基树中定义 uart0 节点
uart0: serial@101f1000 {
compatible = "ns16550";
reg = <0x101f1000 0x1000>;
interrupts = <0x0 0x1c 0x4>;
status = "disabled";
};
// Overlay 文件:启用 uart0 并添加 pinmux
/dts-v1/;
/plugin/;
&uart0 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&uart0_pins>;
};
Overlay 工程要点: 基树编译时必须使用
-@ 选项生成符号表(__symbols__ 节点),Overlay 才能通过标签名找到目标节点。动态加载时,内核的 configfs 接口会校验符号解析是否成功。
1.8 本章小结
设备树是嵌入式 Linux 硬件描述的事实标准,掌握 DTS 语法、节点层次、reg 地址编码和 interrupts 中断映射是进行 Overlay 开发的前提。下一阶段我们将深入 Overlay 的编译、加载与运行时调试技术。