2. 设备树Overlay概念
2.1 什么是设备树 Overlay
设备树 Overlay(Device Tree Overlay)是一种用于在运行时动态修改或扩展系统设备树描述的机制。它允许开发者在不重新编译整个设备树源文件(DTS)的情况下,向系统添加新的硬件节点、修改现有节点的属性或禁用某些设备。
Overlay 本质上是一个“增量”的设备树片段,它描述了相对于基础设备树(Base Device Tree)的变更集合。当 Overlay 被加载时,内核中的 Overlay 子系统会将其与基础设备树进行合并,生成一个完整的、更新后的设备树视图。
核心思想: 基础设备树定义系统启动时的默认硬件拓扑,Overlay 则提供“热插拔”式的硬件描述更新能力。
2.2 Overlay 与基础设备树的区别
基础设备树(Base DTB)和 Overlay(DTBO)在编译方式、加载时机、生命周期和修改粒度上存在本质区别。下表从多个维度进行了详细对比:
| 对比维度 | 基础设备树(Base DTB) | 设备树 Overlay(DTBO) |
|---|---|---|
| 编译产物 | 由 DTS 编译为 .dtb 文件 | 由 DTSO 编译为 .dtbo 文件 |
| 加载时机 | Bootloader 在引导阶段加载,系统启动后固定 | 内核启动后,通过 configfs 或 API 动态加载/卸载 |
| 生命周期 | 从系统启动到关机,不可变更 | 可多次加载、卸载,与硬件模块生命周期绑定 |
| 修改粒度 | 定义完整的硬件拓扑 | 仅描述增量变更(新增节点、修改属性、覆盖状态) |
| 依赖关系 | 无依赖,系统启动的硬件基础 | 依赖基础设备树中的目标节点(通过 target 或 target-path 指定) |
| 错误影响 | 错误可能导致系统无法启动 | 错误仅影响 Overlay 相关设备,可卸载恢复 |
2.3 Overlay 的作用与核心价值
Overlay 机制在嵌入式 Linux 系统中扮演着“硬件描述热更新”的关键角色,其核心作用体现在以下三个方面:
- 硬件模块化支持: 允许将 FPGA 逻辑、扩展板、传感器模块等可插拔硬件的设备树描述独立为 Overlay,实现“插入即识别”。
- 减少系统耦合: 基础设备树保持通用性,特定硬件功能通过 Overlay 按需加载,避免为每种硬件组合维护独立的完整 DTS 文件。
- 运行时灵活性: 无需重启系统即可启用或禁用硬件功能,适用于需要动态调整硬件配置的工业控制、自动驾驶和边缘计算场景。
工程提示: 在复杂嵌入式系统中,一个基础设备树通常搭配 5~20 个不同的 Overlay 文件,通过用户空间脚本或 udev 规则实现自动化加载。
2.4 动态加载的应用场景
Overlay 动态加载机制在真实工程中覆盖了从原型验证到量产部署的多个阶段。以下列出四个典型应用场景:
- FPGA 动态重配置: 当 FPGA 加载不同的比特流时,其内部外设(如 SPI 控制器、DMA 通道)的地址和中断号会变化。Overlay 可以在 FPGA 重配置后动态更新对应的设备节点,使内核驱动正确识别新硬件。
- 扩展板/子卡支持: 基于 Raspberry Pi、BeagleBone 等平台的扩展板(HAT、Cape)通过 EEPROM 存储 Overlay 名称,系统启动后自动加载对应的 .dtbo 文件,实现即插即用。
- 硬件调试与原型验证: 开发阶段频繁修改 GPIO 复用、I2C 地址或时钟频率时,使用 Overlay 可以避免反复烧写内核和设备树,将调试周期从分钟级缩短到秒级。
- 多模式硬件切换: 同一硬件平台在不同工作模式下(如低功耗模式、高性能模式)需要启用不同的外设集合。通过加载不同的 Overlay 组合,可以在运行时切换硬件配置,无需重启。
2.5 Overlay 动态加载的软件架构
下图展示了 Overlay 从编译到加载、合并、生效的完整软件架构流程:
2.6 Overlay 与基础设备树的合并规则
当 Overlay 被加载时,内核按照以下优先级和规则将其与基础设备树合并:
- 定位目标节点: Overlay 通过
target(引用 phandle)或target-path(字符串路径)指定要修改的基础节点。 - 新增子节点: Overlay 中定义的、在基础设备树中不存在的子节点会被直接添加。
- 属性覆盖: Overlay 中定义的属性会覆盖基础设备树中同名属性的值。
- 状态修改: 通过
status = "disabled"或status = "okay"可以启用或禁用基础设备树中的设备。 - 删除操作: 使用
/delete-node/或/delete-property/可以移除基础设备树中的节点或属性。
注意事项: Overlay 的合并是“一次性”的,加载后无法部分回滚。如果需要撤销 Overlay 的修改,必须卸载整个 Overlay,这将导致该 Overlay 添加的所有设备被移除。
2.7 一个典型的 Overlay 示例
以下是一个真实的 Overlay 源文件片段,它向基础设备树中添加一个 I2C 温度传感器节点:
// temperature-sensor.dts
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = &i2c2;
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
temp_sensor: tmp117@48 {
compatible = "ti,tmp117";
reg = <0x48>;
status = "okay";
};
};
};
};
编译命令:
dtc -@ -I dts -O dtb -o temperature-sensor.dtbo temperature-sensor.dts
加载命令(通过 configfs):
mkdir /sys/kernel/config/device-tree/overlays/temp
cat temperature-sensor.dtbo > /sys/kernel/config/device-tree/overlays/temp/dtbo
2.8 动态加载 vs 静态编译:工程选型对比
| 选型维度 | 静态编译(单一 DTB) | 动态加载(Overlay) |
|---|---|---|
| 开发效率 | 每次修改需重新编译 DTB 并烧写/重启 | 修改 DTSO 后直接加载,秒级验证 |
| 部署复杂度 | 单一文件,管理简单 | 需要管理多个 .dtbo 文件及加载脚本 |
| 运行时灵活性 | 固定配置,无法运行时变更 | 支持热插拔、多模式切换 |
| 系统稳定性 | 启动时一次性验证,错误影响全局 | Overlay 加载失败不影响系统核心功能 |
| 适用场景 | 量产产品、硬件配置固定 | 原型验证、FPGA 动态重配、扩展板支持 |
在实际工程中,两种方式并非互斥。一个成熟的嵌入式系统通常采用“基础 DTB + 按需 Overlay”的混合策略,在保证系统稳定性的同时获得最大的硬件配置灵活性。