设备树基础概念:DTS、DTC、DTB、DTBO 文件格式与关系

做嵌入式Linux开发,设备树是绕不开的一道坎。我记得刚入行那会儿,看到一堆.dts文件,完全摸不着头脑。后来被硬件改版坑过几次,才老老实实把这块啃透。今天咱们就把这几个文件格式彻底聊明白。

一、设备树到底是什么?

说白了,设备树就是一套描述硬件信息的「数据结构」。它告诉内核:你的板子上有哪些外设、它们挂在哪条总线上、寄存器地址是多少、中断号怎么配。

你想想看,以前Linux内核里塞满了各种板级的硬编码代码。换一个板子就要改内核,重新编译。有了设备树之后,硬件描述和内核代码分开了。换板子?改个.dts文件就行,内核不用动。

核心思想:设备树 = 硬件配置的「说明书」,内核是「读说明书的人」。

二、四种文件格式详解

1. DTS(Device Tree Source)

这是人类可读的源文件。用文本编辑器打开,里面是树形结构的硬件描述。我习惯把DTS比作「C语言的源文件」——写给人看的。

// 一个简单的DTS片段
/dts-v1/;

/ {
    model = "TI AM335x BeagleBone Black";
    compatible = "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx";

    cpus {
        cpu@0 {
            compatible = "arm,cortex-a8";
            device_type = "cpu";
            reg = <0>;
        };
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x20000000>;  /* 512 MB */
    };
};

注意第一行 /dts-v1/;,这是版本声明。我见过有人漏掉这行,编译直接报错。嗯,这里要注意。

2. DTC(Device Tree Compiler)

DTC就是那个把DTS编译成DTB的工具。它就像gcc之于C语言。在Linux源码的 scripts/dtc/ 目录下能找到它。

用法很简单:

dtc -I dts -O dtb -o output.dtb input.dts

我个人习惯加个 -@ 参数,生成带符号表的DTB,方便后面做overlay调试。

小技巧:dtc -I dtb -O dts -o reverse.dts input.dtb 可以把DTB反编译回DTS。调试时特别有用,我曾经靠这个救回过一次搞丢的源文件。

3. DTB(Device Tree Blob)

DTB是二进制格式,Bootloader(比如U-Boot)把它传给内核。内核解析DTB,就知道硬件长什么样了。

DTB的结构是扁平的,但内部用「节点+属性」的方式保留了树形关系。每个节点有一个偏移量,内核通过遍历这些节点来加载驱动。

格式 可读性 用途 类比
DTS 人类可读 编写、修改 .c源文件
DTB 机器可读 内核加载 .o目标文件
DTBO 机器可读 运行时叠加 .so动态库

4. DTBO(Device Tree Blob Overlay)

DTBO是设备树的「补丁文件」。它允许你在系统运行时,动态添加或修改硬件描述。这个功能在FPGA、可插拔扩展板上特别实用。

举个例子:你有个树莓派的扩展板,插上后通过DTBO告诉内核「我多了个I2C设备」,不用重启系统。

// 一个简单的overlay示例
/dts-v1/;
/plugin/;

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

注意 /plugin/; 这个声明,它告诉DTC这是个overlay文件。编译时用 -@ 参数保留符号表,否则没法叠加。

避坑指南:我曾经在项目里用DTBO给一块FPGA加载外设,结果忘了检查符号表是否匹配。内核直接panic,查了两天才发现是overlay里的节点名和基树对不上。记住:DTBO依赖基树的符号表,编译时一定要加 -@

三、四种文件的关系

它们的关系其实很简单,我画个图你就明白了:

DTS 设备树源文件 DTC 设备树编译器 DTB 设备树二进制 DTBO 设备树叠加 运行时叠加 编译流程:DTS → DTC → DTB(或 DTBO)

流程很清楚:你写DTS,用DTC编译成DTB。如果需要动态扩展,就额外编译一个DTBO。内核启动时加载DTB,运行时再叠加DTBO。

四、实际项目中的使用建议

  1. DTS一定要放在版本管理里。 我见过有人只保存DTB,源文件丢了,后面改板子只能反编译,反编译出来的东西可读性很差。
  2. 编译时加 -@ 参数。 哪怕你现在不用overlay,也加上。万一后面要用呢?不加的话,DTBO根本用不了。
  3. DTBO命名要有规范。 我习惯用 厂商-板子-功能.dtbo 的格式,比如 ti-beaglebone-i2c2.dtbo。不然时间长了你自己都分不清哪个是哪个。
  4. fdtdump 调试。 内核自带这个工具,可以打印DTB的结构。遇到驱动加载问题,先dump一下看看设备树有没有被正确解析。

个人经验:调试设备树时,我经常在DTS里加一个 status = "disabled"; 的节点,然后通过DTBO动态改成 "okay"。这样不用重新编译整个内核,调试效率高很多。

好了,四种文件格式和它们的关系就讲到这里。记住:DTS是源文件,DTC是编译器,DTB是内核加载的二进制,DTBO是运行时补丁。搞懂这个,设备树就算入门了。

专注资料整理