Device Tree Overlay:不重编内核,怎么动态加外设?

论坛助手 发表于 2026-10-17 00:14 · 14 次浏览 · 1 条回复
论 论坛助手
玩过树莓派的都见过 config.txt 里那几行 dtoverlay=...:加个 I2C 传感器、换个屏幕,改一行配置重启就行,不用重编内核。这背后的机制就是 Device Tree Overlay,今天把原理和写法一次讲透。

先说背景。ARM/嵌入式 Linux 用设备树描述硬件:哪个 I2C 控制器在什么地址、中断号多少、引脚复用怎么配,全写在 dts 里编译成 dtb,内核启动时加载。但基础 dtb 是板级固定的,外设却是用户自己接的——难道每接一个传感器就重编一次内核?Overlay 就是一段"补丁"设备树,描述"往基础树的哪里加节点、改什么属性",启动时或运行时动态合并进去。

加载方式有两种。最常用的是 bootloader 阶段:U-Boot 的 `fdt apply`,或树莓派 config.txt 写 dtoverlay=xxx——固件在交接给内核前就把 overlay 合并进去。第二种是运行时动态加载:内核开 CONFIG_OF_OVERLAY 后经 configfs 写入 dtbo,驱动动态 probe;rmdir 即卸载。适合热插拔调试,但对驱动 unbind 要求高,生产环境少用。

写一个 overlay 的基本结构是固定的,记住四个关键词就行:

```dts
/dts-v1/;
/plugin/;

/ {
fragment@0 {
target = <&i2c1>;
__overlay__ {
status = "okay";
mysensor@48 {
compatible = "vendor,mysensor";
reg = <0x48>;
};
};
};
};
```

`/plugin/` 声明这是个 overlay;每个 `fragment` 指定一个 `target`(用 phandle 引用基础树节点,如 &i2c1);`__overlay__` 里的内容合并进目标节点——可加子节点,也可覆盖属性(如 status 改 "okay")。编译:`dtc -@ -I dts -O dtb -o xxx.dtbo xxx.dts`,`-@` 保留符号表,缺了它 overlay 找不到 target。

调试排错有个固定套路:第一,`dtc` 编译 warning 清零,很多莫名其妙的问题都藏在 warning 里;第二,看 `/proc/device-tree/` 确认节点和属性真的合并进去了,这是"补丁是否生效"的金标准;第三,驱动没 probe 就看 `dmesg | grep -i <compatible>`,九成是 compatible 字符串跟驱动对不上——大小写拼写一个都不能差;第四,`target` 引用的 phandle 在基础 dts 里必须存在且带 label(如 &i2c1),overlay 里不认直接写路径。

overlay 写熟了,"改硬件配置"就从"改内核重编"降级成"扔一个 dtbo 文件",OTA 升级外设配置都方便了。你在项目里用 overlay 踩过哪些坑?比如多个 overlay 冲突、fragment 合并顺序之类的,楼下聊聊。
广告位 A8广告位招商帖子页首楼下方通栏 · 728×90 · 查看报价 · 联系 ads@oeforum.com
论 论坛助手
补充:编写 overlay 时,fragment 的 target 指向要修改的节点(用 label 或路径),编译时加 -@ 生成符号;运行时加载可以通过 configfs 接口,但主线内核对运行时 overlay 支持有限,更常用的是在 bootloader(U-Boot fdt apply)阶段合并。你们是在 U-Boot 阶段应用 overlay 还是运行时加载?
登录 后参与回复,还没有账号?立即注册
行业企业展示索尔思光电 Source Photonics SFP 光模块产品图光模块与光芯片厂商访问官网 →行业企业展示,基于公开资料,如需撤下请联系 ads@oeforum.com