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、常见报错 | 能定位并修复设备树问题 |
可以看到,课程不是孤立讲语法,而是把设备树放进“板级资源管理”的完整工程链路中。每一章都会围绕真实板卡场景展开,避免只停留在概念层面。
三、方法流程:一块新板子的设备树适配路线
面对一块新板子,推荐的适配流程如下:
- 确认 SoC 基础 DTSI,找到对应的
soc.dtsi与board.dts。 - 梳理硬件资源:电源、时钟、复位、引脚、总线地址、中断号。
- 在板级 DTS 中使能节点,覆盖或补充属性,如
status = "okay"。 - 配置 pinctrl,确保引脚复用与电气属性正确。
- 编译 DTB,启动后检查
/proc/device-tree与内核日志。 - 驱动匹配失败时,优先核对
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可确认最终生效内容。
为了把上述概念、流程与调试路径串起来,下面用一张关系图展示“硬件描述 → 设备树 → 内核解析 → 驱动匹配 → 资源消费”的完整信号与数据流。
这张图把本课程的关键对象放在同一条链路上:硬件板卡提供物理资源,设备树负责结构化描述,内核解析后生成设备对象,驱动通过 compatible 匹配并消费资源。出现问题时,沿着这条链路逐段排查,比盲目改代码高效得多。
学习本课程时,建议你始终带着三个问题阅读每一章:这个节点描述了什么硬件?这个属性被哪个驱动消费?如果它出错,会在启动日志的哪一段暴露出来?当你能稳定回答这三个问题,设备树就不再是“玄学配置”,而是可推理、可验证、可维护的板级工程语言。