嵌入式Linux设备树实战:零基础看懂并修改外设节点配置
很多刚接触嵌入式Linux的工程师看到arch/arm64/boot/dts/下成片的.dts、.dtsi文件就头大。其实设备树(Device Tree)本质就是一份"硬件简历"——用树形结构告诉内核:板子上有啥外设、挂在哪个地址、用哪根中断线、连到哪个GPIO。内核据此匹配驱动,不再把硬件信息写死在代码里。
设备树的"语法三件套"
一个典型的设备节点长这样:
/ {
mydev: uart@40000000 {
compatible = "mycorp,my-uart";
reg = <0x40000000 0x1000>;
interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk 12>;
status = "disabled";
};
};
抓住几个核心属性就够了:
compatible:驱动匹配的"身份证",字符串须与驱动中的of_device_id完全一致
reg:设备寄存器基地址和长度,<基地址 大小>
interrupts:中断号和触发方式
status:"okay"启用,"disabled"禁用——SoC级.dtsi通常默认全disabled,板级.dts按需开启
.dtsi是芯片级通用描述(SoC内置IP),.dts是板级描述,通过#include引用dtsi,并用&标签语法覆写节点属性。
实战:给板子加一颗LED
假设要在GPIO0_A0上加一颗状态LED,步骤如下:
1. 在根节点下添加LED节点
/ {
my_led: led@0 {
compatible = "gpio-leds";
label = "sys:status";
gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
default-state = "on";
pinctrl-names = "default";
pinctrl-0 = <&led_pin>;
status = "okay";
};
};
2. 配置引脚复用
&pinctrl {
led_pin: led-grp {
rockchip,pins = <0 0 0 &pcfg_pull_none>;
};
};
3. 编译并部署
# 内核源码树内编译
make dtbs -j8
# 或单文件编译
dtc -I dts -O dtb -o myboard.dtb myboard.dts
生成的.dtb放到U-Boot加载路径,重启即可。
上板验证四件套
改完设备树最怕"编译过了但外设不干活",按下面顺序排查:
# 1. 看内核是否解析到节点
dmesg | grep -i "device tree"
# 2. 查看运行时设备树
ls /proc/device-tree/
cat /proc/device-tree/my_led/status
# 3. 反编译当前dtb,确认烧录无误
dtc -I dtb -O dts -o check.dts myboard.dtb
# 4. 确认LED设备是否出现
ls /sys/class/leds/
排查口诀:设备没生效先看status是不是okay;驱动没加载先查compatible字符串是否精确匹配;中断不触发检查interrupt-parent是否指向正确的中断控制器。
新手常踩的三个坑
第一,节点加错位置。必须放在根节点/ { ... };的大括号内,加在外面编译不报错,但节点不会被解析。
第二,引脚复用冲突。同一个物理引脚被两个外设同时声明,SoC会静默使用最后配置的那个,导致前一个外设莫名失效。改DTS前务必核对芯片手册的引脚分配表。
第三,只改.dts忘记重编.dtb。代码改了但烧的还是旧dtb,现象就是"我明明改了啊"——此时用dtc -I dtb -O dts反编译烧录后的dtb一看便知。
写在最后
设备树的学习曲线并不陡,关键是建立"分层修改"的思维:.dtsi不动,只在板级.dts里用&标签覆写;外设默认disabled,用哪个开哪个;所有配置必须对照芯片手册和原理图。掌握这一套,无论是加CAN、调I2C传感器、配SPI屏,还是使能RS485,套路都是一样的——这也是嵌入式Linux硬件适配最硬核的通用能力。





