[具身智能-582]:I2C 完整读写时序详解(标准 7 位地址、400kHz 快速模式,适配 RDK i2cdev 底层逻辑)
·

前置基础规则
- 硬件:SCL 时钟线、SDA 双向开漏数据线,外部 4.7k 上拉,空闲均为高电平;
- 比特传输:高位先行 MSB First;SCL 高电平时采样 SDA 电平,SCL 低电平时切换 SDA;
- 基础时序单元:START、STOP、Repeated START、ACK/NACK 应答;
- 单字节结构:8bit 数据 + 1bit 应答位,共 9 个 SCL 周期。
一、4 种基础信号时序
1. 起始信号 START(通信开启)
条件:SCL = 高电平期间,SDA 由高 → 拉低 时序步骤:
- 总线空闲:SCL=1,SDA=1
- 保持 SCL 不变为高
- SDA 拉低,产生下降沿 → START 之后 SCL 拉低,准备传输字节
2. 停止信号 STOP(通信结束,释放总线)
条件:SCL = 高电平期间,SDA 由低 → 释放拉高 时序步骤:
- 传输完最后一字节 + ACK 后,SCL 拉低
- SDA 先拉低
- SCL 拉高保持不变
- 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 完整时序流程:
- START 起始信号
- 发送 1 字节寻址字节:
7位从地址 + 读写位(0=写)例:0x51 << 1 | 0 = 0xA2 - 等待从机拉低 SDA 返回 ACK
- 发送 1 字节寄存器偏移地址 0x02
- 等待从机 ACK
- 发送待写入数据 0xAA
- 等待从机 ACK
- STOP 停止信号,传输结束
时序分层说明:
- 第 1 段:寻址从设备,告知 “我要写你”;
- 第 2 段:发送目标寄存器编号,指定写入位置;
- 第 3 段:发送要存入寄存器的数据;
对应代码里的底层逻辑:
python
运行
i2c.write(0x02) # 先发寄存器地址
i2c.write(0xAA) # 再发写入数据
三、I2C 读寄存器完整时序(主机读取从机数据)
I2C 读不能直接读,必须先写寄存器地址,使用 Repeated START 切换读模式; 需求:读取 0x51 设备寄存器 0x01 的 1 字节数据 时序流程:
- START
- 发送写寻址字节 0xA2(0x51 写)
- ACK
- 发送寄存器偏移 0x01
- ACK
- Repeated START 重复起始(关键,不发 STOP)
- 发送读寻址字节 0xA3(0x51 | 1 读标志)
- ACK(从机确认读指令)
- 从机在 SCL 节拍下,把寄存器数据逐位输出到 SDA,主机读取 8bit
- 主机输出 NACK(告诉从机不再继续读)
- STOP 结束
对应你代码的操作逻辑:
python
运行
i2c.write(0x01) # 第一步:发送寄存器地址
val = i2c.read(1) # 第二步:发起读时序获取数据
四、时序关键约束(开发避坑)
- SDA 电平切换只能在 SCL = 低 时操作;SCL 高时 SDA 电平代表有效数据 / START/STOP;
- 所有字节高位先输出,不能低位先行;
- 读操作读完所需字节后主机必须发 NACK,持续 ACK 会触发从机持续输出下一个寄存器;
- 总线挂死故障:若从机拉死 SDA 为低,无法产生 STOP,整条 I2C 总线卡死;软件需增加超时复位逻辑;
- 速率与时序裕量:
- 标准 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()是底层裸字节接口,不会自动生成寄存器寻址时序:
- 读寄存器必须手动先 write (reg) 发送偏移地址,再 read;
- 库内部自动封装 START、STOP、ACK 时序,无需用户操作电平;
- 如果直接调用
i2c.read(1)不先发寄存器地址,从机无目标地址,读出随机无效乱码
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)