1. 课程导入

在嵌入式 Linux 开发中,板级资源管理长期是“驱动能跑”与“产品能稳定量产”之间的分水岭。过去,板级信息常被硬编码在 arch/arm/mach-xxx 的 C 文件中,每换一块板就要改内核、重编译,导致内核臃肿、维护困难。设备树(Device Tree,DT)的出现,把“硬件描述”从内核代码中剥离出来,交给独立的 .dts 文件表达,内核只负责解析并生成设备与资源。本课程的目标,就是让你从“会写驱动”进阶到“会描述板级、会管理资源、会排查设备树问题”的工程能力。

本课程的核心主线只有一句话:用设备树把硬件描述清楚,用驱动把资源消费正确,用调试手段把问题定位到节点与属性。

一、核心概念:设备树到底描述了什么

设备树本质上是一棵由节点(node)和属性(property)构成的层次化数据结构。它描述的是“系统里有哪些设备、它们如何连接、依赖哪些资源”,而不是“设备如何工作”。理解这一点非常关键:设备树负责描述,驱动负责行为。

  • 节点:代表一个设备或总线,如 i2c1、uart0、gpio-keys。
  • 属性:描述节点的特征,如 compatible、reg、interrupts、clocks、pinctrl-0。
  • compatible:驱动匹配的“身份证”,格式通常为 "厂商,型号"。
  • reg:地址与长度,描述寄存器空间或内存映射。
  • 中断、时钟、GPIO、电源域:通过 phandle 引用其他节点,形成资源依赖图。

设备树源文件 .dts 经编译后生成 .dtb,由 bootloader 传给内核。内核在启动阶段展开设备树,创建 platform_device、i2c_client 等设备对象,再与驱动匹配。这条链路是后续所有调试的根基。

二、课程框架:从语法到板级工程

本课程共 30 章,按“基础语法 → 资源模型 → 驱动对接 → 板级实战 → 调试与优化”递进。整体框架如下表所示:

阶段主要内容能力目标
基础篇DTS 语法、节点属性、编译与反编译能读懂并修改设备树
资源篇reg、interrupts、clocks、pinctrl、regulator能正确描述板级资源
驱动篇of_* API、platform_driver、匹配机制能写出消费设备树的驱动
实战篇I2C/SPI/GPIO/LED/按键/网口板级适配能独立完成板级移植
调试篇/proc/device-tree、dtc、of_overlay、常见报错能定位并修复设备树问题

可以看到,课程不是孤立讲语法,而是把设备树放进“板级资源管理”的完整工程链路中。每一章都会围绕真实板卡场景展开,避免只停留在概念层面。

三、方法流程:一块新板子的设备树适配路线

面对一块新板子,推荐的适配流程如下:

  1. 确认 SoC 基础 DTSI,找到对应的 soc.dtsi 与 board.dts。
  2. 梳理硬件资源:电源、时钟、复位、引脚、总线地址、中断号。
  3. 在板级 DTS 中使能节点,覆盖或补充属性,如 status = "okay"。
  4. 配置 pinctrl,确保引脚复用与电气属性正确。
  5. 编译 DTB,启动后检查 /proc/device-tree 与内核日志。
  6. 驱动匹配失败时,优先核对 compatible 与 reg。

下面是一段典型的板级 DTS 片段,展示 I2C 设备与 GPIO 资源的描述方式:

&i2c1 {
    status = "okay";
    clock-frequency = <400000>;

    eeprom@50 {
        compatible = "atmel,24c02";
        reg = <0x50>;
    };
};

&gpio_keys {
    compatible = "gpio-keys";
    pinctrl-names = "default";
    pinctrl-0 = <&key_pins>;

    key-power {
        label = "power";
        gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
        linux,code = <KEY_POWER>;
    };
};

这段代码体现了设备树的核心思想:总线节点负责“挂设备”,设备节点负责“声明资源”,驱动只关心如何消费这些资源。

四、案例与场景:从“能启动”到“能量产”

一个常见场景是:开发板能启动,但网口不通。排查后发现 phy-mode 写成了 rgmii,而硬件实际是 rgmii-id,导致时序偏差。另一个场景是 I2C 设备扫描不到,原因是 pinctrl 未正确配置,SDA/SCL 被复用为普通 GPIO。这些问题都不是驱动逻辑错误,而是板级描述错误。

设备树问题的典型特征是:驱动代码没改,但设备就是不出来。此时应优先怀疑 compatible、reg、status、pinctrl 和时钟/电源依赖,而不是先改驱动。

五、误区与复盘:初学者最容易踩的坑

  • 把设备树当驱动写:在 DTS 里写逻辑判断,这是错误方向。
  • 忽略 phandle 引用:时钟、电源、引脚引用错误,导致 probe 失败。
  • 直接改 SoC DTSI:应优先在板级 DTS 覆盖,保持可维护性。
  • 不看内核日志:of_* 报错信息通常已指出具体节点与属性。
  • 不验证 DTB:反编译 dtc -I dtb -O dts 可确认最终生效内容。
设备树调试的第一原则:以最终 DTB 为准,不以源文件为准。 因为覆盖、包含、宏展开都可能改变最终结果。

为了把上述概念、流程与调试路径串起来,下面用一张关系图展示“硬件描述 → 设备树 → 内核解析 → 驱动匹配 → 资源消费”的完整信号与数据流。

设备树与板级资源管理:从硬件描述到驱动消费 硬件板卡 SoC / 外设 / 引脚 电源 / 时钟 / 中断 设备树源文件 .dts / .dtsi 节点 + 属性 + phandle DTB 与内核解析 dtc 编译 / 展开 生成 device 对象 驱动匹配 compatible 匹配 probe 执行 资源消费:reg / interrupts / clocks / pinctrl / regulator / gpio 驱动通过 of_* API 读取属性,完成寄存器映射、中断注册、时钟使能与引脚配置 常见故障 compatible 不匹配 reg / 中断号错误 pinctrl 未配置 调试手段 /proc/device-tree dtc 反编译 DTB dmesg 与 of_* 日志 工程目标 板级可移植 资源可复用 问题可定位

这张图把本课程的关键对象放在同一条链路上:硬件板卡提供物理资源,设备树负责结构化描述,内核解析后生成设备对象,驱动通过 compatible 匹配并消费资源。出现问题时,沿着这条链路逐段排查,比盲目改代码高效得多。

学习本课程时,建议你始终带着三个问题阅读每一章:这个节点描述了什么硬件?这个属性被哪个驱动消费?如果它出错,会在启动日志的哪一段暴露出来?当你能稳定回答这三个问题,设备树就不再是“玄学配置”,而是可推理、可验证、可维护的板级工程语言。