一、设备树基础概念

什么是设备树?

设备树,英文叫 Device Tree,是一套描述硬件资源的数据结构。说白了,它就是一份「硬件配置清单」。

我刚开始接触嵌入式 Linux 时,总觉得这玩意儿有点玄乎。后来做项目多了才明白——设备树其实就是告诉内核:你的板子上有哪些外设、它们挂在哪里、用哪个中断、GPIO 怎么配。

设备树的文件后缀通常是 .dts(Device Tree Source),编译后会生成 .dtb(Device Tree Blob),内核启动时直接读取这个二进制文件。

核心要点:设备树 = 硬件描述语言,专门给 Linux 内核用的。

为什么需要设备树?

在早期 Linux 内核中,硬件信息是直接写在 C 代码里的。比如 arch/arm/mach-xxx 目录下,每个板子都有对应的 board 文件。你想想看,换一个 GPIO 就要改源码、重新编译内核,多麻烦。

我遇到过最头疼的一次:客户临时换了颗传感器,中断引脚从 GPIO1_3 改到了 GPIO2_7。当时我改完 board 文件,编译内核花了 20 分钟,烧录又花了 10 分钟。后来换成设备树,改两行 dts,重新编译 dtb 几秒钟搞定。

设备树解决了三个核心问题:

  • 硬件与驱动解耦——同一份内核镜像可以跑在不同板子上
  • 减少内核编译次数——改硬件配置不用重编内核
  • 统一描述格式——ARM、RISC-V、x86 都用同一套规则

我的经验:做产品开发时,设备树文件比内核源码改得更频繁。建议把 dts 文件纳入版本管理,跟硬件原理图一一对应。

设备树在 Linux 内核中的角色

内核启动时,设备树扮演了「硬件导游」的角色。它告诉内核:

  • CPU 是什么架构、几个核、频率多少
  • 内存起始地址和大小
  • 每个外设的寄存器基地址
  • 中断控制器是 GIC 还是老式的
  • GPIO 控制器挂在哪个总线下面

我画了一张图,帮你理解设备树在内核中的位置:

设备树在 Linux 内核中的角色 硬件层(CPU、内存、UART、I2C、GPIO、中断控制器...) 设备树(.dts / .dtb) Linux 内核(OF API 解析设备树,匹配驱动) 设备驱动(读取设备树节点,获取资源) 数据流向:硬件 → 设备树 → 内核 → 驱动

内核启动时,Bootloader(比如 U-Boot)会把 dtb 文件加载到内存,然后传给内核。内核的 OF(Open Firmware)子系统负责解析这个二进制文件,把硬件信息提取出来,供各个驱动使用。

设备树的基本语法

来看一个最简单的设备树节点:

/dts-v1/;

/ {
    model = "MyBoard v1.0";
    compatible = "vendor,myboard";

    uart0: serial@10000000 {
        compatible = "ns16550";
        reg = <0x10000000 0x1000>;
        interrupts = <0 33 4>;
        clock-frequency = <1843200>;
    };
};

这里我解释几个关键字段:

字段 含义 我的理解
compatible 兼容性字符串,驱动匹配的依据 相当于硬件的「身份证号」
reg 寄存器基地址和范围 告诉驱动去哪里操作硬件
interrupts 中断号、触发类型 这个我踩过坑,后面细说
#address-cells 地址字段占几个 32 位 32位系统通常是1,64位是2

注意:我曾经遇到一个坑——设备树里 reg 的地址写错了,导致驱动 probe 失败。排查了半天才发现是地址多了一个 0。所以写设备树时,一定要对照原理图和数据手册,一个字都不能错。

设备树与中断、GPIO 的关系

中断和 GPIO 是设备树里最常用的两个资源。为什么把它们放在一起讲?因为在实际项目中,它们经常是绑定的。

比如一个按键,它既是一个 GPIO 输入,又会产生中断。设备树里是这样描述的:

gpio-keys {
    compatible = "gpio-keys";
    
    button@0 {
        label = "Power";
        gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
        interrupts = <&gpio1 3 IRQ_TYPE_EDGE_FALLING>;
    };
};

这里 gpios 指定了 GPIO 控制器、引脚号和有效电平。interrupts 指定了中断控制器、中断号和触发方式。两者配合,才能实现按键检测。

我个人的习惯是:先确认硬件原理图上的 GPIO 编号,再查芯片手册找到对应的中断号,最后写到设备树里。顺序不能乱,否则驱动拿到的资源就是错的。

设备树的编译与使用

写好的 .dts 文件需要编译成 .dtb:

# 编译设备树
dtc -I dts -O dtb -o myboard.dtb myboard.dts

# 反编译(调试用)
dtc -I dtb -O dts -o myboard.dts myboard.dtb

我建议你在开发阶段,每次改完设备树都反编译验证一下。有时候语法错误不会报错,但生成的 dtb 可能不完整。

小技巧:用 fdtdump 工具可以查看 dtb 的原始内容,适合快速确认设备树是否被正确加载。

总结一下

设备树不是什么高深的技术,它就是一份硬件配置清单。把硬件信息从内核源码里抽出来,放到一个独立文件里,方便修改和维护。

做嵌入式 Linux 开发,设备树是绕不开的一关。尤其是中断和 GPIO 的配置,稍不注意就会出问题。后面的章节,我会结合具体项目案例,带你一步步搞定这些配置。

记住一句话:设备树写对了,驱动开发就成功了一半。

可可精品资料库