4. 设备树I2C节点:设备树语法、I2C子节点属性与地址属性解析

在RK平台Sensor驱动开发中,设备树(Device Tree)是描述硬件拓扑与资源分配的核心机制。I2C总线上的Sensor设备,必须通过设备树中的I2C子节点向内核传递设备地址、中断号、电源域等关键信息。本章将深入解析设备树I2C节点的语法结构、三大核心属性(compatible、reg、interrupts)以及地址映射的底层逻辑。

核心认知:设备树中的I2C子节点,本质上是将Sensor的物理连接信息(挂载在哪条I2C总线、从设备地址是多少、使用哪个中断引脚)转化为内核可解析的数据结构。驱动通过of_match_table匹配compatible属性,通过reg属性获取I2C从设备地址,通过interrupts属性注册中断处理函数。

4.1 设备树I2C节点语法结构

设备树采用树形结构描述硬件,I2C控制器作为总线节点,其下挂载的Sensor作为子节点。一个标准的I2C设备树节点包含以下层级:

  • 根节点:/,包含所有硬件信息。
  • I2C控制器节点:如&i2c1,描述I2C总线本身的属性(时钟频率、引脚配置等)。
  • Sensor子节点:在I2C控制器节点内部,描述具体的Sensor设备。

以下是一个RK3588平台上I2C1总线挂载加速度传感器(BMI160)的完整设备树片段:

&i2c1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2c1m0_xfer>;
    clock-frequency = <400000>;  /* 400kHz 高速模式 */

    bmi160: accelerometer@68 {
        compatible = "bosch,bmi160";
        reg = <0x68>;
        interrupt-parent = <&gpio1>;
        interrupts = <RK_PB2 IRQ_TYPE_EDGE_RISING>;
        vdd-supply = <&vcc_3v3>;
        vddio-supply = <&vcc_1v8>;
        mount-matrix = "0", "1", "0",
                       "-1", "0", "0",
                       "0", "0", "1";
    };
};
语法要点:
  • 子节点名称格式:设备名@I2C地址(如accelerometer@68),地址必须与reg属性一致。
  • status = "okay" 使能节点;status = "disabled" 禁用节点。
  • pinctrl-0 引用引脚复用配置,确保I2C总线引脚功能正确。
I2C设备树节点层级结构 根节点 "/" I2C控制器节点 "&i2c1" clock-frequency = <400000> pinctrl-0 = <&i2c1m0_xfer> Sensor子节点 "accelerometer@68" compatible = "bosch,bmi160" reg = <0x68> interrupts = <RK_PB2 ...> vdd-supply = <&vcc_3v3> 设备树节点层级关系说明 1. 根节点是设备树的入口,包含所有硬件总线节点。 2. I2C控制器节点定义总线属性(时钟、引脚),是Sensor子节点的父节点。 3. Sensor子节点继承父节点的I2C总线资源,通过reg属性指定从设备地址。 4. 中断、电源等资源在子节点中独立声明,不与同总线其他设备共享。

4.2 三大核心属性详解

4.2.1 compatible 属性:驱动匹配的钥匙

compatible属性是设备与驱动之间的“身份证”。内核通过设备树中的compatible字符串,在驱动注册的of_match_table中查找匹配项,从而将设备绑定到正确的驱动。

属性字段 格式 示例 说明
compatible "厂商,设备型号" "bosch,bmi160" 厂商前缀+设备型号,内核优先匹配最具体的字符串
兼容列表 多个字符串,用空格分隔 "bosch,bmi160", "bosch,bmi" 可提供多个兼容型号,内核按顺序匹配
避坑指南:
  • compatible字符串必须与驱动中of_device_id表完全一致,包括大小写和标点符号。
  • 厂商前缀应遵循内核规范(如bosch、invensense、st),不要随意自定义。
  • 如果设备兼容多个型号,将最具体的型号放在最前面。

4.2.2 reg 属性:I2C从设备地址的载体

reg属性定义了设备在I2C总线上的从设备地址(7位或10位地址)。对于I2C设备,reg的第一个单元格是地址值,第二个单元格(可选)是地址长度。

/* 7位地址示例:0x68 */
reg = <0x68>;

/* 10位地址示例:0x3C0,需要指定地址长度 */
reg = <0x3C0 0x2>;  /* 0x2 表示10位地址占用2个字节 */
地址类型 地址范围 reg格式 常见设备
7位地址 0x08 ~ 0x77 reg = <0x68> BMI160、MPU6050、LSM6DS3
10位地址 0x00 ~ 0x3FF reg = <0x3C0 0x2> 部分数字麦克风、扩展IO芯片
地址映射实战要点:
  • Sensor的I2C地址通常由芯片的ADDR引脚电平决定(高电平或低电平对应不同地址)。
  • 在设备树中配置的reg地址必须与硬件原理图一致,否则驱动无法访问设备。
  • RK平台I2C驱动通过i2c_client结构体的addr字段获取reg值,驱动开发时可通过client->addr读取。
I2C地址映射与reg属性解析流程 I2C地址映射与reg属性解析流程 硬件原理图 ADDR引脚电平 确定I2C从设备地址 例如:0x68 (7位) 设备树reg属性 reg = <0x68> 内核I2C核心解析 i2c_client->addr Sensor驱动使用 i2c_smbus_read/write 地址映射关键路径 硬件原理图 → 确定I2C地址 → 写入设备树reg属性 → 内核解析为i2c_client.addr → 驱动通过该地址访问Sensor寄存器 任何环节的地址不一致,都会导致I2C通信失败(NACK错误)。

4.2.3 interrupts 属性:中断资源的声明

interrupts属性声明Sensor设备使用的中断资源,包括中断控制器、中断引脚、触发类型。RK平台通常使用GPIO作为中断源,通过interrupt-parent指定GPIO控制器。

/* 完整中断属性示例 */
interrupt-parent = <&gpio1>;           /* 中断父控制器:GPIO1 */
interrupts = <RK_PB2 IRQ_TYPE_EDGE_RISING>;  /* 引脚PB2,上升沿触发 */
中断属性字段 说明 RK平台常用值
interrupt-parent 指定中断控制器(GPIO或GIC) &gpio0 ~ &gpio4
interrupts 中断号、触发类型 RK_PA0 IRQ_TYPE_EDGE_RISING
IRQ_TYPE_EDGE_RISING 上升沿触发 适用于数据就绪中断
IRQ_TYPE_LEVEL_LOW 低电平触发 适用于中断引脚低电平有效
中断配置常见错误:
  • interrupt-parent指向错误的GPIO控制器,导致中断无法注册。
  • 中断触发类型与Sensor芯片规格不匹配(如芯片要求下降沿,却配置为上升沿)。
  • 多个Sensor共享同一中断引脚时,未配置IRQ_TYPE_NONE或共享中断处理逻辑。

4.3 地址属性解析与I2C核心交互

当内核启动并解析设备树时,I2C核心框架会遍历每个I2C控制器节点的子节点,提取reg属性中的地址值,并调用i2c_new_device或of_i2c_register_devices创建i2c_client结构体。驱动通过i2c_client的addr字段获取设备地址,进而发起I2C通信。

/* 内核I2C核心解析reg属性的简化流程 */
static struct i2c_client *of_i2c_register_device(struct device_node *node)
{
    struct i2c_client *client;
    u32 addr;

    /* 从设备树节点读取reg属性中的地址值 */
    if (of_property_read_u32(node, "reg", &addr)) {
        dev_err(dev, "missing reg property\n");
        return ERR_PTR(-EINVAL);
    }

    /* 创建i2c_client,并将地址赋值给client->addr */
    client = kzalloc(sizeof(*client), GFP_KERNEL);
    client->addr = addr;
    /* ... 其他初始化 ... */
    return client;
}
驱动开发实战:在Sensor驱动的probe函数中,通过client->addr获取设备地址,并使用i2c_smbus_read_byte_data或i2c_transfer进行寄存器读写。如果设备树中reg配置错误,驱动将无法找到设备,i2c_smbus_read会返回-ENXIO(无此设备)错误。
设备树地址解析与I2C核心交互流程 设备树地址解析与I2C核心交互流程 阶段1:设备树解析 读取reg属性 → 获取addr值 阶段2:创建i2c_client client->addr = addr 阶段3:驱动probe 使用client->addr 阶段4:I2C通信 i2c_smbus_read/write(client, ...) 返回 -ENXIO 错误 交互流程关键点 1. 设备树解析阶段:内核读取reg属性,获取原始地址值(如0x68)。 2. i2c_client创建阶段:地址值被赋值给client->addr,驱动通过该字段访问设备。 3. 如果地址不匹配,I2C控制器会收到NACK,通信失败,驱动probe应返回错误。

4.4 设备树I2C节点实战案例:RK3588 + BMI160

以下是一个完整的RK3588平台设备树I2C节点配置案例,包含所有关键属性,并附有详细注释:

/* RK3588设备树 - I2C1总线挂载BMI160加速度传感器 */
&i2c1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2c1m0_xfer>;
    clock-frequency = <400000>;  /* 400kHz高速模式 */

    /* BMI160传感器子节点 */
    bmi160: accelerometer@68 {
        /* 驱动匹配标识:厂商前缀+设备型号 */
        compatible = "bosch,bmi160";

        /* I2C从设备地址:7位地址0x68(ADDR引脚接GND) */
        reg = <0x68>;

        /* 中断配置:GPIO1_B2,上升沿触发 */
        interrupt-parent = <&gpio1>;
        interrupts = <RK_PB2 IRQ_TYPE_EDGE_RISING>;

        /* 电源域:数字核心电压和IO电压 */
        vdd-supply = <&vcc_3v3>;
        vddio-supply = <&vcc_1v8>;

        /* 安装方向矩阵:定义Sensor坐标系与设备坐标系的映射 */
        mount-matrix = "0", "1", "0",
                       "-1", "0", "0",
                       "0", "0", "1";
    };
};
实战检查清单:
  • ✅ compatible是否与驱动of_device_id表完全一致?
  • ✅ reg地址是否与硬件原理图上的I2C地址一致?
  • ✅ interrupt-parent是否指向正确的GPIO控制器?
  • ✅ 中断触发类型是否与Sensor数据手册一致?
  • ✅ 电源域vdd-supply和vddio-supply是否已正确定义?

4.5 本章小结

设备树I2C节点是Sensor驱动与硬件之间的桥梁。通过本章学习,你应掌握:

  • 设备树节点层级:根节点 → I2C控制器节点 → Sensor子节点,每个层级承载不同的硬件描述。
  • 三大核心属性:compatible(驱动匹配)、reg(I2C地址)、interrupts(中断资源),是驱动开发中最常配置的字段。
  • 地址解析流程:从设备树reg属性到i2c_client->addr,再到I2C通信的完整链路,任何环节的地址不一致都会导致通信失败。
下章预告:第5章将深入I2C通信协议层,讲解如何在RK平台上实现Sensor寄存器的读写操作,包括SMBus协议、I2C消息传输以及调试技巧。