4. I2C 寄存器读写基础:协议、地址、时序与 SMBus 区别
在 RK 平台 Sensor 驱动调试中,I2C 是最常用的板级通信总线。Sensor 通常作为 I2C 从设备,主控通过 I2C 控制器读写其内部寄存器,以完成初始化配置、模式切换和数据采集。本章从协议层到驱动层,系统梳理 I2C 的核心概念与实操要点。
4.1 I2C 协议概述
I2C(Inter-Integrated Circuit)是一种同步、半双工、多主从的总线协议,由 Philips 公司(现 NXP)在 1980 年代提出。它仅需两根线:串行数据线 SDA 和串行时钟线 SCL,即可挂载多个设备。
- 物理层:开漏输出 + 上拉电阻,线与逻辑。
- 速率模式:标准模式 100 kHz,快速模式 400 kHz,高速模式 3.4 MHz。
- 地址长度:7 位地址(主流)或 10 位地址(扩展)。
- 数据格式:大端传输,MSB first,每字节后跟 ACK/NACK。
- 多主支持:通过仲裁和时钟同步实现多主共存。
4.2 7 位地址与 10 位地址
I2C 从设备地址分为 7 位模式和 10 位模式。在 RK 平台 Sensor 驱动中,绝大多数 Sensor 使用 7 位地址。
| 特性 | 7 位地址 | 10 位地址 |
|---|---|---|
| 地址范围 | 0x00 ~ 0x7F(保留 0x00~0x07) | 0x000 ~ 0x3FF |
| 理论最大设备数 | 112 个(实际受总线电容限制) | 1024 个 |
| 地址字节数 | 1 字节(高 7 位为地址,LSB 为 R/W) | 2 字节(首字节 11110XX + R/W,次字节为低 8 位) |
| 兼容性 | 所有 I2C 设备均支持 | 需从设备明确支持 10 位模式 |
| RK 平台使用场景 | 95% 以上的 Sensor(如 MPU6050、ICM-20602) | 极少数多路复用器或特殊 PMIC |
reg = <0x68>。注意:设备树中的地址是 7 位左对齐值,不包含 R/W 位。如果 Sensor 数据手册标注地址为 0xD0(含 R/W),则实际 7 位地址为 0x68。
4.3 I2C 读写时序
I2C 通信以 START 条件开始,以 STOP 条件结束。每次传输包含:从设备地址 + R/W 位、寄存器地址、数据字节,每字节后跟 ACK 应答。
4.3.1 写时序(Master → Slave)
写操作通常用于配置 Sensor 寄存器。标准格式如下:
START | 7-bit ADDR + W(0) | ACK | REG_ADDR | ACK | DATA_BYTE | ACK | ... | STOP
4.3.2 读时序(Master ← Slave)
读操作需要先写寄存器地址,再重新发送 START 条件切换为读方向:
START | 7-bit ADDR + W(0) | ACK | REG_ADDR | ACK |
RESTART | 7-bit ADDR + R(1) | ACK | DATA_BYTE | NACK | STOP
4.4 SMBus 与 I2C 的区别
SMBus(System Management Bus)基于 I2C 协议,但在时序、电压、超时和命令格式上有严格约束。RK 平台部分 PMIC 和电池管理芯片使用 SMBus,Sensor 驱动中也可能遇到兼容性问题。
| 对比项 | I2C | SMBus |
|---|---|---|
| 电压标准 | 无固定标准(常见 1.8V / 3.3V / 5V) | 固定 3.3V ± 5% |
| 最低时钟频率 | DC(可无限慢) | 最低 10 kHz(防止死锁) |
| 超时机制 | 无硬件超时 | 35 ms 超时检测(时钟低电平超时) |
| 最大速率 | 标准 100 kHz / 快速 400 kHz / 高速 3.4 MHz | 最大 100 kHz |
| 地址应答 | 允许 NACK 后继续通信 | NACK 后必须 STOP |
| 命令协议 | 无固定命令格式 | 固定协议:Quick Command、Send Byte、Receive Byte、Write Byte/Word、Read Byte/Word、Block Read/Write |
| 分组错误校验 | 无 | 可选 PEC(Packet Error Checking) |
| 典型应用 | Sensor、EEPROM、ADC/DAC | PMIC、电池管理、温度监控 |
i2c_smbus_read_byte_data()),而非通用 I2C 传输函数。SMBus 的 35 ms 超时机制可能导致 I2C 慢速设备通信失败,需在设备树中调整时钟频率或关闭超时检测。
4.5 RK 平台 I2C 驱动框架中的寄存器读写
在 Linux 内核中,I2C 驱动通过 struct i2c_client 表示从设备,通过 struct i2c_adapter 表示 I2C 控制器。Sensor 驱动通常使用以下 API 进行寄存器读写:
4.5.1 核心 API 对比
| 函数 | 说明 | 适用场景 |
|---|---|---|
i2c_smbus_read_byte_data() |
读取单个寄存器(1 字节) | SMBus 兼容设备,单寄存器读取 |
i2c_smbus_write_byte_data() |
写入单个寄存器(1 字节) | SMBus 兼容设备,单寄存器写入 |
i2c_smbus_read_i2c_block_data() |
读取连续寄存器块 | 批量读取 Sensor 数据(如加速度计 XYZ) |
i2c_transfer() |
通用 I2C 消息传输 | 非 SMBus 设备,自定义时序 |
regmap_init_i2c() |
基于 regmap 框架的 I2C 封装 | 推荐方式:自动处理缓存、批量操作、调试 |
4.5.2 使用 regmap 进行寄存器读写(推荐)
在 RK 平台 Sensor 驱动中,推荐使用 regmap 框架,它封装了 I2C 传输细节,并提供缓存、批量读写和调试接口。
/* 驱动初始化时创建 regmap */
static const struct regmap_config sensor_regmap_config = {
.reg_bits = 8, /* 寄存器地址宽度:8 位 */
.val_bits = 8, /* 寄存器数据宽度:8 位 */
.max_register = 0x7F, /* 最大寄存器地址 */
.cache_type = REGCACHE_RBTREE, /* 使用红黑树缓存 */
};
struct regmap *regmap = devm_regmap_init_i2c(client, &sensor_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Failed to init regmap\n");
return PTR_ERR(regmap);
}
/* 读写操作示例 */
int val;
regmap_read(regmap, 0x0D, &val); /* 读取 WHO_AM_I 寄存器 */
regmap_write(regmap, 0x1A, 0x10); /* 写入配置寄存器 */
cat /sys/kernel/debug/regmap/<i2c-bus>-<addr>/registers。在 RK 平台上,这比直接使用 i2cget/i2cset 更高效,且不会干扰驱动运行状态。
4.6 常见寄存器读写问题与排查方法
在 Sensor 驱动调试初期,寄存器读写失败是最常见的问题。以下是 RK 平台上的典型故障场景:
- 设备地址错误:设备树中 reg 值与 Sensor 实际 7 位地址不匹配。使用
i2cdetect -y <bus>扫描确认。 - ACK 丢失:Sensor 未响应,通常由电源未上电、复位引脚拉低或 I2C 上拉电阻缺失导致。用示波器抓取 SDA/SCL 波形确认。
- 时钟拉伸超时:部分 Sensor 在内部处理时会拉低 SCL,RK 平台 I2C 控制器默认支持时钟拉伸,但需确认超时时间配置。
- 寄存器地址自动递增:连续读取时,部分 Sensor 不支持地址自动递增,需逐字节发送寄存器地址。
- 字节序问题:16 位寄存器在 I2C 上为大端传输,但 Sensor 内部可能使用小端存储,驱动中需做字节交换。
4.7 本章小结
- I2C 是 Sensor 驱动中最核心的板级通信协议