1. 设备树基础概念

1.1 什么是设备树 (Device Tree)

设备树(Device Tree,DT)是一种描述硬件资源的数据结构,它使用节点(node)和属性(property)的树形层次来向操作系统(尤其是Linux内核)传递板级硬件信息。设备树源文件(.dts)经过设备树编译器(dtc)编译为二进制设备树(.dtb),由引导加载程序传递给内核。

核心定义:设备树是一个硬件描述文件,它将原本硬编码在Linux内核中的板级硬件细节(如内存地址、中断号、GPIO引脚、时钟频率等)分离出来,以结构化文本的形式提供给内核。

一个典型的设备树节点结构如下:

/dts-v1/;

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

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x10000000>;  /* 256MB RAM */
    };

    leds {
        compatible = "gpio-leds";
        led0: led@0 {
            label = "usr0";
            gpios = <&gpio1 21 GPIO_ACTIVE_HIGH>;
            linux,default-trigger = "heartbeat";
        };
    };
};
关键术语:节点(node)用 { } 表示,属性(property)用 key = value; 表示。每个节点可以有子节点,形成树形结构。

1.2 设备树的作用

设备树在嵌入式Linux系统中承担三大核心作用:

作用 说明 传统方式(无设备树) 设备树方式
硬件描述 描述CPU、内存、外设、中断、GPIO等硬件资源 硬编码在 arch/arm/mach-xxx 板级文件中 统一在 .dts 文件中描述
硬件抽象 同一内核镜像支持多个不同板卡 每个板卡需重新编译内核 只需更换 .dtb 文件
资源传递 将中断号、寄存器地址、GPIO引脚等传递给驱动 通过 platform_data 结构体传递 通过设备树节点属性自动解析

设备树解决了嵌入式Linux中板级代码泛滥的问题。在ARM Linux早期,每个板卡都需要在 arch/arm/mach-* 目录下添加大量C语言板级文件,导致内核维护成本极高。设备树将硬件描述与内核代码解耦,实现了一次编译,多处运行。

注意:设备树不是万能硬件抽象层。它只描述硬件存在什么以及如何连接,不描述硬件如何工作。设备驱动仍然需要在内核中实现。
设备树在嵌入式Linux启动流程中的位置 Bootloader U-Boot / Barebox 设备树 .dtb 硬件描述文件 Linux 内核 zImage 加载 传递 设备树解析器 platform_device 设备驱动 启动流程说明: 1. Bootloader 从存储介质(eMMC/SD/NAND)加载 .dtb 文件 2. Bootloader 将 .dtb 在内存中的地址通过寄存器传递给内核 3. 内核启动早期调用 unflatten_device_tree() 解析 .dtb 4. 内核根据设备树节点创建 platform_device 等总线设备 5. 驱动匹配到对应设备后,从设备树获取资源并初始化

1.3 设备树与平台设备 (platform device) 的关系

设备树与平台设备的关系是嵌入式驱动开发中的核心概念。简单来说:设备树是硬件描述源,平台设备是内核中的设备对象。

在引入设备树之前,平台设备通过以下方式创建:

/* 传统方式:在板级文件中静态定义 platform_device */
static struct platform_device my_device = {
    .name = "my_driver",
    .id = -1,
    .num_resources = 2,
    .resource = my_resources,
};

/* 然后在板级初始化函数中注册 */
platform_device_register(&my_device);

引入设备树后,上述过程被自动化:

/* 设备树描述 */
my_device: my-device@48000000 {
    compatible = "my_driver";
    reg = <0x48000000 0x1000>;
    interrupts = <42>;
};

/* 内核自动完成:of_platform_populate() 扫描设备树节点
 * 为每个 compatible 匹配的节点创建 platform_device */

两者的对应关系如下表所示:

维度 设备树 (Device Tree) 平台设备 (platform device)
本质 硬件描述数据(文本/二进制) 内核运行时对象(struct platform_device)
生命周期 编译时生成,启动时加载 内核运行时创建和销毁
创建方式 编写 .dts 文件 由内核 of_platform 框架自动创建
资源来源 reg, interrupts, gpios 等属性 从设备树解析后填充 resource 结构体
匹配机制 compatible 属性 of_match_table 与 compatible 匹配
核心流程:设备树节点 → of_platform_populate() → platform_device → platform_driver 匹配 → probe() 执行。驱动开发者只需关注设备树描述和驱动 probe 逻辑,无需手动注册设备。
设备树节点 → 平台设备 → 驱动匹配流程 设备树节点 my-device@48000000 { compatible = "my_driver"; reg = <0x48000000 0x1000>; }; 内核 of_platform 框架 of_platform_populate() of_device_alloc() 平台设备 struct platform_device .name = "my_driver" .resource = {reg, irq} 解析 创建 platform_driver 匹配 of_match_table → compatible 匹配 → probe()
调试技巧:在 /sys/devices/platform/ 目录下可以查看所有已注册的平台设备。使用 ls /sys/firmware/devicetree/base/ 可以查看内核当前使用的设备树内容,这是验证设备树是否被正确加载的快速方法。