3、I2C设备树配置实战:在Zephyr项目中对I2C控制器和从设备进行设备树节点配置
设备树(Device Tree)是 Zephyr OS 中描述硬件拓扑和资源配置的核心机制。对于 I2C 子系统,正确的设备树配置是驱动正常工作的前提。本节将带领你完成从 I2C 控制器使能到从设备节点绑定的完整配置流程,并解析常见配置陷阱。
核心原则:Zephyr 的设备树配置遵循“控制器 → 总线 → 从设备”的层级结构。必须先确保 I2C 控制器节点在 SoC 级设备树中被正确使能,然后才能在板级设备树中添加挂载在该总线上的从设备节点。
3.1 I2C 控制器节点配置
I2C 控制器节点通常定义在 SoC 的 .dtsi 文件中,板级 .dts 文件负责使能并调整参数。以下是一个典型的 I2C 控制器节点结构:
// soc.dtsi (SoC 级定义)
soc {
i2c1: i2c@40005400 {
compatible = "nordic,nrf-twim";
reg = <0x40005400 0x400>;
interrupts = <5 1>;
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
clock-frequency = <100000>;
};
};
在板级 .dts 文件中,通过 &i2c1 引用并覆盖属性:
// board.dts (板级使能)
&i2c1 {
status = "okay";
clock-frequency = <400000>; // 提升至 400kHz (Fast Mode)
pinctrl-0 = <&i2c1_default>;
pinctrl-names = "default";
};
关键参数说明:
compatible:匹配驱动绑定,必须与 I2C 控制器驱动名称一致。clock-frequency:总线时钟频率,单位 Hz。标准模式 100000,快速模式 400000。pinctrl-*:引脚控制配置,确保 SDA 和 SCL 引脚被正确复用。status:"okay"使能,"disabled"禁用。
3.2 I2C 从设备节点配置
从设备节点必须作为 I2C 控制器节点的子节点存在。每个从设备需要指定唯一的 reg 属性(即 7 位从机地址)和 compatible 字符串。
// board.dts — 在 i2c1 总线挂载两个从设备
&i2c1 {
status = "okay";
clock-frequency = <400000>;
// 温度传感器 TMP102
tmp102: temperature-sensor@48 {
compatible = "ti,tmp102";
reg = <0x48>;
label = "TEMP_SENSOR_0";
};
// 加速度计 LIS2DH12
lis2dh12: accelerometer@19 {
compatible = "st,lis2dh12";
reg = <0x19>;
label = "ACCEL_0";
irq-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
};
};
地址冲突陷阱:同一 I2C 总线上不允许出现两个
reg 值相同的从设备节点。如果硬件上两个设备地址相同,必须通过硬件地址引脚(如 ADDR 引脚)修改其中一个的地址,或在设备树中只使能一个设备。
3.3 设备树绑定(YAML 绑定文件)
每个 compatible 字符串必须对应一个 YAML 绑定文件,位于 dts/bindings/ 目录下。绑定文件定义了该设备支持的属性和必需属性。
以 ti,tmp102 为例,其绑定文件 ti,tmp102.yaml 内容如下:
# dts/bindings/sensor/ti,tmp102.yaml
description: TI TMP102 Temperature Sensor
compatible: "ti,tmp102"
include: [i2c-device.yaml]
properties:
label:
type: string
required: true
description: Human-readable label
| 绑定字段 | 说明 | 示例值 |
|---|---|---|
compatible |
与设备树节点中的 compatible 匹配 | "ti,tmp102" |
include |
继承基础绑定(如 i2c-device.yaml) | [i2c-device.yaml] |
properties |
定义设备特有属性及其类型 | label: type: string |
required |
标记该属性是否为必需 | true / false |
3.4 常见配置错误与排查
设备树配置错误是 I2C 驱动无法初始化的首要原因。下表列出了最常见的错误类型及其排查方法:
| 错误现象 | 可能原因 | 排查方法 |
|---|---|---|
编译报错:undefined node |
引用的节点别名不存在 | 检查 &i2c1 是否在 .dtsi 中正确定义 |
编译报错:duplicate unit-address |
同一总线存在相同 reg 地址 |
核对所有从设备节点的 reg 值是否唯一 |
运行时:I2C transfer timeout |
时钟频率过高或引脚配置错误 | 检查 clock-frequency 是否超出设备规格;验证 pinctrl 引脚复用 |
运行时:device not found |
compatible 字符串与绑定文件不匹配 | 运行 west build -t guiconfig 查看设备树生成状态 |
运行时:NACK on address |
从设备地址错误或设备未上电 | 用逻辑分析仪抓取 SDA 波形,确认地址字节是否正确 |
调试利器:使用
west build -t devicetree 可以生成最终的设备树输出文件 build/zephyr/zephyr.dts,查看该文件可以确认所有节点是否按预期展开。
3.5 实战:配置一个 I2C 温度传感器
以下是一个完整的实战步骤,从零开始配置 TMP102 温度传感器:
- 确认硬件连接:TMP102 的 SDA 接 I2C1_SDA(如 P0.26),SCL 接 I2C1_SCL(如 P0.27),ADDR 引脚接地(地址 0x48)。
- 使能 I2C 控制器:在
board.dts中添加&i2c1 { status = "okay"; ... }。 - 添加从设备节点:在
&i2c1内部添加tmp102: temperature-sensor@48 { ... }。 - 验证绑定文件:确保
dts/bindings/sensor/ti,tmp102.yaml存在且内容正确。 - 编译并检查:运行
west build -b your_board app,然后查看build/zephyr/zephyr.dts确认节点已展开。 - 编写驱动测试:使用
device_get_binding("TEMP_SENSOR_0")获取设备句柄,调用sensor_sample_fetch读取数据。
// 应用层代码示例:读取 TMP102 温度
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
void main(void)
{
const struct device *dev = DEVICE_DT_GET_ONE(ti_tmp102);
if (!device_is_ready(dev)) {
printk("Device %s is not ready\n", dev->name);
return;
}
while (1) {
struct sensor_value temp;
sensor_sample_fetch(dev);
sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
printk("Temperature: %d.%06d C\n", temp.val1, temp.val2);
k_sleep(K_MSEC(1000));
}
}
提示:如果
DEVICE_DT_GET_ONE 编译失败,请检查 ti_tmp102 的 compatible 字符串是否与绑定文件完全一致。Zephyr 的宏会将 compatible 中的逗号和斜杠转换为下划线。
通过以上步骤,你已经掌握了在 Zephyr 项目中配置 I2C 控制器和从设备节点的完整方法。正确的设备树配置是 I2C 通信稳定的基石,务必在硬件调试前仔细核对节点属性。