设备树实战指南
本文是 嵌入式 Linux 学习路径 第五阶段:从 原理图 → DTS 节点 → 内核 probe 完成闭环。完成后你应能:读 SoC .dtsi、写板级 .dts、编译 dtb、用 dmesg 验证 compatible 匹配。
学习目标
- 理解 DTS 语法、标签/引用、
/include/与.dtsi分层。 - 为 GPIO 控制的 LED 编写节点,并与 现有或最小驱动 匹配。
- 使用
dtc、内核make dtbs编译;在运行系统上读/proc/device-tree。 - 查阅
Documentation/devicetree/bindings核对属性合法性。
设备树在启动中的位置
- Bootloader 将 dtb 指针 传给内核(ARM64 booti 第三参数等)。
- 内核 early init 解析 DTB 为 unflattened device-tree;驱动 probe 时匹配 compatible。
语法基础
节点与属性
/ {
model = "My Board";
compatible = "vendor,myboard", "vendor,socfamily";
leds {
compatible = "gpio-leds";
status = "okay";
led_user: led-user {
label = "user";
gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
default-state = "off";
};
};
};常见属性
| 属性 | 含义 |
|---|---|
| compatible | 驱动匹配字符串列表(从具体到通用 fallback) |
| reg | 地址与长度(MMIO/总线地址) |
| address-cells / size-cells | 子节点 reg 格式 |
| interrupts | 中断 specifier(依赖 interrupt-parent) |
| gpios | GPIO specifier(依赖 gpio-controller) |
| status | "okay" / "disabled" |
| phandle / &label | 引用其他节点 |
引用 SoC 已有控制器
板级 .dts 通常:
/dts-v1/;
#include "soc.dtsi"
/ {
compatible = "vendor,myboard", "vendor,soc";
/* 覆盖或追加 */
};
&gpio0 {
status = "okay";
};
&i2c1 {
status = "okay";
/* 挂外设节点 */
};实战:GPIO LED(对照原理图)
步骤
- 在原理图找到 LED 网络名、所接 GPIO 端口与引脚、有效电平(高亮还是低亮)。
- 在 SoC dtsi 确认 gpio controller 节点标签(如
gpio0)。 - 在 板级 dts 增加
gpio-leds子节点(内核自带 leds-gpio 驱动,compatiblegpio-leds)。
编译 dtb
在内核树:
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- dtbs
# 或单独:
dtc -I dts -O dtb -o board.dtb board.dts部署与验证
- 替换 boot 分区 dtb 或通过 U-Boot
fdt命令 修改(高级)。 - 启动后:
dmesg | grep -i led
ls /sys/class/leds/
echo 1 > /sys/class/leds/user/brightnessprobe 失败排障
| 现象 | 检查 |
|---|---|
| 无 led 相关 dmesg | status、compatible、dtb 是否真正加载 |
deferred probe | 依赖的 clock/regulator/gpio 未就绪 |
| GPIO 错误 | 引脚号、bank、pinctrl 是否配置 |
pinctrl(多数 SoC 必配)
- GPIO 常需 pin mux 从复用功能切到 GPIO;在 pinctrl 节点 定义 state,在设备节点
pinctrl-names/pinctrl-0引用。
&pinctrl {
led_pins: led-pins {
pins = "GPIO12";
function = "gpio";
};
};
&led_user {
pinctrl-names = "default";
pinctrl-0 = <&led_pins>;
};(具体属性名 必须以 SoC binding 为准。)
调试工具
# 运行中查看(路径随内核配置)
ls /proc/device-tree/
hexdump -C /proc/device-tree/leds/led-user/gpios
of_node - 通过 sysfs /sys/firmware/devicetree/...(较新内核)
dtc -I fs -O dts /proc/device-tree > live.dts # 反编译查看(教学用)libfdt / fdtdump:离线检查 dtb。
与驱动的关系
platform_driver或 gpio-leds 平台驱动 在注册时遍历 DT 匹配 compatible。- 匹配成功后 probe 解析 gpios 属性,注册 led_classdev。
- 自定义驱动 时
of_match_table与 DTS compatible 字符串 完全一致(包括 vendor 前缀)。
实践练习
- 为 两个 LED 增加节点,default-state 一 on 一 off。
- 故意写错 gpio 编号,记录 dmesg,再改对。
- 读
Documentation/devicetree/bindings/leds/leds-gpio.yaml(或旧 .txt),列出必填属性。 - 用 设备树插件 dtbo(若平台支持)动态加载(选做)。
阶段验收
- 从原理图 标出 gpio 与 active level,与 dts 一致。
- 解释
&gpio0 12 GPIO_ACTIVE_HIGH每个 cell 含义(查 gpio binding)。 - 能在 5 步内 完成 dts 改 → dtb 编 → 刷入 → sysfs 点灯。
下一阶段衔接
- 字符设备驱动:自定义 compatible 与 platform_driver 替代 gpio-leds 框架(教学)。
- I2C/SPI 传感器:同一套 reg/interrupts 思维扩展到总线设备。
参考
- 内核 devicetree-specification(规范)
Documentation/devicetree/bindings/- Device Tree for Dummies(elinux.org,入门)
一个 dts 错误可能导致 boot hang;保留 U-Boot fdt 回退 或 双 dtb 分区。