前置基础规则

  1. 硬件:SCL 时钟线、SDA 双向开漏数据线,外部 4.7k 上拉,空闲均为高电平;
  2. 比特传输:高位先行 MSB First;SCL 高电平时采样 SDA 电平,SCL 低电平时切换 SDA;
  3. 基础时序单元:START、STOP、Repeated START、ACK/NACK 应答;
  4. 单字节结构:8bit 数据 + 1bit 应答位,共 9 个 SCL 周期。

一、4 种基础信号时序

1. 起始信号 START(通信开启)

条件:SCL = 高电平期间,SDA 由高 → 拉低 时序步骤:

  1. 总线空闲:SCL=1,SDA=1
  2. 保持 SCL 不变为高
  3. SDA 拉低,产生下降沿 → START 之后 SCL 拉低,准备传输字节

2. 停止信号 STOP(通信结束,释放总线)

条件:SCL = 高电平期间,SDA 由低 → 释放拉高 时序步骤:

  1. 传输完最后一字节 + ACK 后,SCL 拉低
  2. SDA 先拉低
  3. SCL 拉高保持不变
  4. SDA 释放变高,上升沿 → STOP 总线回到空闲状态 SCL=1、SDA=1

3. 应答位 ACK / NACK(每 8bit 后必传)

主机发 8bit 数据后,释放 SDA,等待从机控制 SDA:

  • ACK(确认收到):从机在第 9 个 SCL 低周期把 SDA 拉低;
  • NACK(无应答):从机不动作,SDA 保持高; 主机收到 NACK 后应发送 STOP 终止通信。

4. 重复起始 Repeated START

不发送 STOP,直接再次生成 START,用于「写寄存器地址→连续读数据」场景,不释放总线,避免总线冲突。

二、I2C 写寄存器完整时序(主机写从机)

需求:向从机地址 0x51,寄存器 0x02 写入数值 0xAA 完整时序流程:

  1. START 起始信号
  2. 发送 1 字节寻址字节:7位从地址 + 读写位(0=写) 例:0x51 << 1 | 0 = 0xA2
  3. 等待从机拉低 SDA 返回 ACK
  4. 发送 1 字节寄存器偏移地址 0x02
  5. 等待从机 ACK
  6. 发送待写入数据 0xAA
  7. 等待从机 ACK
  8. STOP 停止信号,传输结束

时序分层说明:

  • 第 1 段:寻址从设备,告知 “我要写你”;
  • 第 2 段:发送目标寄存器编号,指定写入位置;
  • 第 3 段:发送要存入寄存器的数据;

对应代码里的底层逻辑:

python

运行

i2c.write(0x02)  # 先发寄存器地址
i2c.write(0xAA)  # 再发写入数据

三、I2C 读寄存器完整时序(主机读取从机数据)

I2C 读不能直接读,必须先写寄存器地址,使用 Repeated START 切换读模式; 需求:读取 0x51 设备寄存器 0x01 的 1 字节数据 时序流程:

  1. START
  2. 发送写寻址字节 0xA2(0x51 写)
  3. ACK
  4. 发送寄存器偏移 0x01
  5. ACK
  6. Repeated START 重复起始(关键,不发 STOP)
  7. 发送读寻址字节 0xA3(0x51 | 1 读标志)
  8. ACK(从机确认读指令)
  9. 从机在 SCL 节拍下,把寄存器数据逐位输出到 SDA,主机读取 8bit
  10. 主机输出 NACK(告诉从机不再继续读)
  11. STOP 结束

对应你代码的操作逻辑:

python

运行

i2c.write(0x01)    # 第一步:发送寄存器地址
val = i2c.read(1)  # 第二步:发起读时序获取数据

四、时序关键约束(开发避坑)

  1. SDA 电平切换只能在 SCL = 低 时操作;SCL 高时 SDA 电平代表有效数据 / START/STOP;
  2. 所有字节高位先输出,不能低位先行;
  3. 读操作读完所需字节后主机必须发 NACK,持续 ACK 会触发从机持续输出下一个寄存器;
  4. 总线挂死故障:若从机拉死 SDA 为低,无法产生 STOP,整条 I2C 总线卡死;软件需增加超时复位逻辑;
  5. 速率与时序裕量:
    • 标准 100kHz:高低电平最小 4.7us;
    • 快速 400kHz:高低电平最小 0.6us;RDK 默认常用 400kHz。

五、读写时序对比简表

表格

操作类型 核心时序特征 核心信号
I2C 写寄存器 START→写地址→ACK→寄存器→数据→ACK→STOP 单次 START、无重复起始
I2C 读寄存器 START→写地址→ACK→重复 START→读地址→读取数据→NACK→STOP 必须 Repeated START 切换读写方向

六、时序和之前 i2cdev 代码的关联

使用的i2c.read()/i2c.write()底层裸字节接口不会自动生成寄存器寻址时序

  1. 读寄存器必须手动先 write (reg) 发送偏移地址,再 read;
  2. 库内部自动封装 START、STOP、ACK 时序,无需用户操作电平;
  3. 如果直接调用i2c.read(1)不先发寄存器地址,从机无目标地址,读出随机无效乱码
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐