第3章:设备树编译器(DTC):dtc工具的使用、反编译与编译、设备树覆盖层(overlay)
设备树写好了,怎么变成二进制文件给内核用?
答案就是 DTC——设备树编译器。
说实话,我刚入行那会儿,觉得这工具没啥好学的,不就是 dtc -I dts -O dtb 嘛。直到有一次,我在一个项目里因为编译选项没加对,导致整块板子起不来,排查了整整两天……嗯,从那以后,我对 DTC 的态度就认真多了。
3.1 dtc 工具的基本用法
DTC 的全称是 Device Tree Compiler。它负责把 .dts 文本文件编译成 .dtb 二进制文件,也能反向操作。
最基本的编译命令长这样:
dtc -I dts -O dtb -o my_board.dtb my_board.dts
参数说明:
-I:指定输入格式,dts 表示设备树源文件-O:指定输出格式,dtb 是二进制格式-o:输出文件名
反编译呢?就是把 .dtb 变回 .dts:
dtc -I dtb -O dts -o my_board.dts my_board.dtb
我个人习惯在调试时加一个 -V 17 参数,指定设备树版本。版本 17 是目前最通用的,兼容性最好。
uart@fe010000。这是正常的,不是 bug。
3.2 编译过程中的那些坑
我在项目中遇到过最典型的问题,是 头文件路径找不到。
很多 .dts 文件会包含 .dtsi 文件,比如:
#include "soc.dtsi"
#include "board-common.dtsi"
如果这些文件不在当前目录,编译就会报错。解决办法是加 -i 参数指定搜索路径:
dtc -I dts -O dtb -o my_board.dtb my_board.dts -i /path/to/include/dir
另一个常见问题是 语法错误。比如少了个分号,或者标签名重复。DTC 的错误信息有时候不太友好,只会告诉你「语法错误在第 123 行附近」。这时候我一般会先用 dtc -I dts -O dts 做一次「格式化输出」,看看 DTC 自己能不能解析通过。
3.3 设备树覆盖层(Overlay)
覆盖层是什么?说白了,就是「运行时打补丁」。
你想想看,一个硬件平台可能有多种外设扩展板。如果每种组合都写一个完整的 .dts,那得累死人。覆盖层允许你写一个基础 .dtb,再动态加载额外的片段。
覆盖层的语法和普通 .dts 几乎一样,但有一个关键区别:必须用 /plugin/; 声明。
举个例子:
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = &i2c1;
__overlay__ {
status = "okay";
temp_sensor: tmp102@48 {
compatible = "ti,tmp102";
reg = <0x48>;
};
};
};
};
编译覆盖层时,要加 -@ 参数:
dtc -I dts -O dtb -@ -o i2c_sensor.dtbo i2c_sensor.dts
注意输出后缀是 .dtbo,这是覆盖层二进制文件的约定命名。
fragment 和 target 指定要修改的节点。每个 fragment 就是一个独立的补丁块。
3.4 覆盖层的加载方式
覆盖层怎么用?有两种主流方式:
| 方式 | 说明 | 适用场景 |
|---|---|---|
| configfs 接口 | 通过 /sys/kernel/config/device-tree/overlays/ 加载 |
运行时动态加载,适合热插拔外设 |
| U-Boot 加载 | 在 bootloader 阶段合并覆盖层 | 启动时固定配置,适合扩展板 |
configfs 的方式我比较常用。命令大致是:
mkdir /sys/kernel/config/device-tree/overlays/my_overlay
cat my_overlay.dtbo > /sys/kernel/config/device-tree/overlays/my_overlay/dtbo
加载成功后,对应的设备节点就会出现在 /sys/firmware/devicetree/base/ 下。
3.5 反编译覆盖层
有时候拿到一个 .dtbo 文件,想看看里面到底改了啥。反编译命令和普通 .dtb 一样:
dtc -I dtb -O dts -o dump.dts my_overlay.dtbo
但要注意,反编译出来的内容会包含 __symbols__ 和 __fixups__ 这些元数据节点。它们是覆盖层机制内部用的,不用太在意。
我个人习惯在反编译时加 -f 参数,强制输出,即使遇到一些格式问题也能继续:
dtc -I dtb -O dts -f -o dump.dts my_overlay.dtbo
3.6 实战建议
最后,分享几个我踩过的坑:
- 覆盖层的 target 必须存在:如果基础 .dtb 里没有
i2c1节点,覆盖层加载会失败。不会自动创建。 - 标签名不能冲突:覆盖层里的标签(比如
temp_sensor)不能和基础 .dtb 里的标签重名。 - 编译选项要一致:基础 .dtb 和覆盖层 .dtbo 最好用同一版本的 DTC 编译。版本差异可能导致兼容性问题。
嗯,设备树编译器这块,说白了就是「写对语法、选对参数、注意路径」。但真正用起来,细节还是挺多的。尤其是覆盖层,用好了能省不少事,用不好就是给自己挖坑。
下一章,我们会深入设备树的绑定文档和 GPIO 控制器的具体实现。到时候再聊。