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级总线到具体传感器设备的完整设备树层级关系:

车载I2C设备树节点层级结构 / (根节点) soc i2c@2000000 (I2C控制器) compatible="ti,am62-i2c" reg=<0x02000000 0x100> status="okay" temp-sensor@48 compatible="ti,tmp102" reg=<0x48> status="okay" touch@38 compatible="edt,edt-ft5x06" reg=<0x38> status="okay" 关键属性说明 • #address-cells=<1> #size-cells=<0>:I2C子节点地址用1个32位单元,无长度字段 • reg=<0x48>:7位I2C从设备地址,由硬件引脚决定 • compatible 字符串必须与驱动 of_match_table 完全一致,区分大小写

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文件,无需改动主设备树,降低引入新问题的风险。

4.8 本章小结