4. 设备树(Device Tree)基础:DTS语法、节点、属性、reg、compatible、status等关键属性
在车载Linux系统中,设备树(Device Tree)是描述硬件拓扑结构的核心机制。它替代了传统板级文件中的大量硬件编码,将硬件配置信息从内核源码中剥离,形成独立的数据结构。对于I2C设备挂载而言,设备树是驱动与硬件之间的“桥梁”——驱动通过解析设备树节点获取设备地址、中断号、时序参数等关键信息。
4.1 设备树源文件(DTS)语法基础
设备树源文件(.dts)采用树形层级结构,以节点(node)为基本单元,每个节点包含若干属性(property)。其语法简洁,类似C语言风格,但无执行逻辑,仅描述硬件。
核心语法规则:
- 每个设备树文件以
/dts-v1/;开头,标识版本。 - 根节点为
/,所有设备节点挂载其下。 - 节点名称格式:
节点名@地址,地址为寄存器基址或设备地址。 - 属性值支持:
<u32>数组、"字符串"、[字节序列]、&phandle引用。 - 支持
#include预处理指令,用于包含头文件或.dtsi文件。
// 最小设备树示例
/dts-v1/;
/ {
model = "TI AM62x Automotive EVM";
compatible = "ti,am62x-evm", "ti,am62x";
#address-cells = <2>;
#size-cells = <2>;
soc {
#address-cells = <2>;
#size-cells = <2>;
ranges;
i2c@2000000 {
compatible = "ti,am62-i2c", "simple-bus";
reg = <0x00 0x02000000 0x00 0x100>;
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
clock-frequency = <400000>;
temp-sensor@48 {
compatible = "ti,tmp102";
reg = <0x48>;
status = "okay";
};
};
};
};
4.2 节点与属性详解
设备树中每个节点代表一个硬件设备或总线控制器,属性则描述设备的配置参数。下表列出车载I2C场景中最关键的属性及其含义:
| 属性名 | 类型 | 说明 | I2C设备示例 |
|---|---|---|---|
compatible |
字符串列表 | 设备驱动匹配标识,格式为"厂商,型号" | "ti,tmp102" |
reg |
u32数组 | 寄存器地址或I2C从设备地址 | <0x48> (7位地址) |
status |
字符串 | 设备启用状态:"okay"、"disabled"、"fail" | "okay" |
#address-cells |
u32 | 子节点地址字段占用的单元数(每个单元32位) | <1> (I2C子节点用1个单元) |
#size-cells |
u32 | 子节点地址长度字段占用的单元数 | <0> (I2C设备无地址范围) |
clock-frequency |
u32 | I2C总线时钟频率,单位Hz | <400000> (400kHz快速模式) |
interrupt-parent |
phandle | 中断控制器引用 | &gpio0 |
interrupts |
u32数组 | 中断号与触发类型 | <5 IRQ_TYPE_EDGE_FALLING> |
4.3 关键属性深度解析
4.3.1 compatible —— 驱动匹配的“身份证”
compatible 是设备树中最核心的匹配属性。内核通过该属性将设备节点与驱动绑定。其值通常为 "厂商,型号" 格式,支持多个备选值,内核按优先级依次尝试匹配。
车载实践建议: 对于I2C传感器设备,compatible字符串必须与驱动中
of_match_table 定义完全一致。例如驱动中定义 { .compatible = "ti,tmp102", .data = &tmp102_data },设备树中必须写 compatible = "ti,tmp102"。大小写、逗号、空格均敏感。
4.3.2 reg —— 地址与地址空间
reg 属性描述设备的地址信息。对于I2C设备,它表示从设备地址(7位或10位);对于I2C控制器,它表示寄存器基址和长度。其格式由父节点的 #address-cells 和 #size-cells 决定。
// I2C控制器节点:reg = <基址高32位 基址低32位 长度高32位 长度低32位>
// 当 #address-cells=<2>, #size-cells=<2> 时
reg = <0x00 0x02000000 0x00 0x100>;
// I2C从设备节点:reg = <从设备地址>
// 当 #address-cells=<1>, #size-cells=<0> 时
reg = <0x48>; // 7位地址0x48
4.3.3 status —— 设备启用控制
status 属性控制设备是否被内核枚举。在车载开发中,常利用该属性在调试阶段临时禁用设备,或在产品变体中通过设备树覆盖(overlay)启用/禁用特定外设。
| status值 | 含义 | 内核行为 | 车载典型场景 |
|---|---|---|---|
"okay" |
设备正常启用 | 创建设备并尝试绑定驱动 | 量产配置 |
"disabled" |
设备禁用 | 不创建设备,驱动不加载 | 调试时临时关闭冲突设备 |
"fail" |
设备检测失败 | 标记为失败,不绑定驱动 | 硬件检测异常时由bootloader设置 |
"reserved" |
保留给固件使用 | 内核不处理 | 安全域独占设备 |
4.4 I2C设备树节点结构图
下图展示车载I2C子系统中,从SoC级总线到具体传感器设备的完整设备树层级关系:
4.5 设备树覆盖(Overlay)与车载调试
在车载开发中,设备树覆盖机制允许在不重新编译整个设备树的情况下,动态添加或修改I2C设备节点。这对于调试阶段频繁更换传感器型号、调整I2C地址非常实用。
// I2C设备树覆盖示例:添加一个调试用的温度传感器
// 文件:tmp102-overlay.dts
/dts-v1/;
/plugin/;
/ {
fragment@0 {
target = &i2c0; // 引用I2C控制器
__overlay__ {
#address-cells = <1>;
#size-cells = <0>;
tmp102@48 {
compatible = "ti,tmp102";
reg = <0x48>;
status = "okay";
};
};
};
};
车载调试注意事项:
- I2C地址冲突:确保覆盖中添加的设备地址不与现有设备冲突,否则内核会报
i2c i2c-0: Failed to register device错误。 - status属性优先级:覆盖中的
status = "disabled"可以屏蔽父节点已启用的设备,常用于临时隔离故障传感器。 - 编译覆盖文件:使用
dtc -@ -I dts -O dtb -o tmp102-overlay.dtbo tmp102-overlay.dts生成.dtbo文件。
4.6 常见I2C设备树配置错误与排查
| 错误现象 | 根因 | 排查方法 |
|---|---|---|
| 驱动probe不调用 | compatible字符串不匹配 | 检查 /sys/bus/i2c/devices/.../uevent 中的OF_COMPATIBLE值 |
| I2C通信失败 | reg地址错误或地址格式不对 | 使用 i2cdetect -y <bus> 验证实际设备地址 |
| 设备未枚举 | status="disabled" 或父节点status禁用 | 检查 /proc/device-tree/ 下对应节点的status值 |
| 中断不触发 | interrupt-parent引用错误或中断号不对 | 查看 /proc/interrupts 确认中断是否注册 |
| 时钟频率异常 | clock-frequency属性缺失或值超范围 | 使用示波器测量SCL波形,或检查内核日志中I2C时序配置 |
4.7 设备树编译与反编译
设备树源文件(.dts)需要编译为二进制设备树(.dtb)才能被内核加载。车载开发中常用的操作命令:
# 编译设备树
dtc -I dts -O dtb -o am62x-evm.dtb am62x-evm.dts
# 反编译设备树(调试用)
dtc -I dtb -O dts -o am62x-evm-decompiled.dts am62x-evm.dtb
# 查看设备树中I2C节点
dtc -I dtb -O dts am62x-evm.dtb | grep -A 20 "i2c@"
# 验证设备树语法
dtc -I dts -O dtb -o /dev/null am62x-evm.dts 2>&1
车载开发效率技巧: 在调试阶段,建议将I2C设备节点放在独立的.dtsi文件中,通过
#include 包含到主设备树。这样当需要更换传感器时,只需修改.dtsi文件,无需改动主设备树,降低引入新问题的风险。