Loading

I2C总线通信

I2C 总线协议:两根线搞定多芯片通信

配套短视频已发布,本文为独立知识文章,适合系统学习 I2C 协议。

为什么是 I2C?

在嵌入式系统中,MCU 需要和各种外设通信:EEPROM 存参数、IMU 读姿态、OLED 刷屏、RTC 读时间……如果每个外设都独占一组通信线,引脚根本不够用。

I2C(Inter-Integrated Circuit)的核心价值:只用两根线(SDA + SCL),就能挂多个外设。这在 PCB 面积紧张、引脚有限的场景下非常实用。

1. 开漏 + 上拉:线与总线

I2C 的物理层设计很巧妙:

  • SDA(数据线):双向,所有设备共享
  • SCL(时钟线):主机驱动
  • 开漏输出 + 外部上拉电阻
VCC ─────────────────────────────────
         │           │
        [Rp]        [Rp]
         │           │
SDA ─────┼───────────┼──── Master / Slave1 / Slave2 ...
         │
SCL ─────┼──────────────── Master 驱动

关键规则:任何器件只能把线拉低,松手后由上拉电阻拉高

这就是"线与"逻辑——只要有一个器件拉低,总线就是低电平。这个特性是后面仲裁机制的基础。

工程细节

上拉电阻的选择影响通信速率和功耗:

  • 标准模式(100kHz):4.7kΩ 典型
  • 快速模式(400kHz):2.2kΩ ~ 3.3kΩ
  • 电阻太大 → 上升沿慢,限制速率
  • 电阻太小 → 拉低时电流大,功耗增加

2. 起始与停止条件

I2C 用 SDA 在 SCL 高电平期间的跳变来标识控制信号:

条件 定义 含义
START (S) SCL=H 时,SDA 从 H→L 总线被占用,通信开始
STOP (P) SCL=H 时,SDA 从 L→H 释放总线,通信结束

记忆点:正常数据传输时,SDA 只在 SCL 低电平期间变化。SCL 高电平时 SDA 变化 = 控制信号,不是数据。

SCL:  ────────┐     ┌─────────────┐     ┌────────
              │     │             │     │
              └─────┘             └─────┘

SDA:  ──────┐                         ┌──────────
            │    START                │   STOP
            └─────────────────────────┘

3. 数据传输与 ACK 应答

每次传输 8 bit 数据(MSB 优先),第 9 个时钟周期是应答位:

  • ACK:接收方拉低 SDA → "我收到了"
  • NACK:接收方不拉低(SDA 保持高)→ "没收到 / 不要了"
        D7  D6  D5  D4  D3  D2  D1  D0  ACK
SCL:   ┌┐  ┌┐  ┌┐  ┌┐  ┌┐  ┌┐  ┌┐  ┌┐  ┌┐
       ┘└──┘└──┘└──┘└──┘└──┘└──┘└──┘└──┘└──

SDA:   ─── 发送方驱动 8bit ───┤ 接收方拉低 = ACK

ACK 机制让发送方知道对面是否在线、是否正确接收。如果发了地址没收到 ACK,说明总线上没有这个地址的设备。

4. 地址帧:7bit 地址 + R/W 方向位

START 之后的第一个字节是地址帧:

[ A6 | A5 | A4 | A3 | A2 | A1 | A0 | R/W ]
|<-------- 7bit 设备地址 -------->|  方向位
  • R/W = 0:Master 写数据给 Slave
  • R/W = 1:Master 从 Slave 读数据

常见坑:数据手册给的地址是 7bit,但实际发送时要左移一位再加 R/W 位。

例如 MPU6050 地址 0x68:

  • 写操作:0x68 << 1 | 0 = 0xD0
  • 读操作:0x68 << 1 | 1 = 0xD1

5. 多主仲裁:线与决定谁留下

I2C 支持多主机(Multi-Master),两个 Master 可能同时发起通信。仲裁规则:

  1. 两个 Master 同时发 START
  2. 逐 bit 发送,同时回读 SDA
  3. 如果自己发 1 但读到 0 → 说明对方在拉低 → 自己输了,退出
  4. 发 0 的一方"赢",继续通信
Master A:  1  1  0  1  0  0  0
Master B:  1  1  0  1  0  1  0
                              ↑
                    A发00≠1 → B退让
总线实际:  1  1  0  1  0  0  0  ← A的数据完整保留

关键:线与逻辑保证仲裁过程中数据不会损坏,赢家的数据始终完整。

6. 完整读时序

读取一个寄存器的完整流程:

Start → [SlaveAddr+W] → ACK → [RegAddr] → ACK → Sr → [SlaveAddr+R] → ACK → [Data] → NACK → Stop

分解:

  1. Start:起始条件
  2. SlaveAddr+W:告诉 Slave "我要写"(其实是写寄存器地址)
  3. RegAddr:写入要读取的寄存器地址
  4. Sr:重复起始(Repeated START),不释放总线直接切方向
  5. SlaveAddr+R:切换为读模式
  6. Data:Slave 返回数据
  7. NACK:Master 发 NACK 表示"不再读了"
  8. Stop:停止条件

为什么需要 Repeated START? 因为要先告诉 Slave 读哪个寄存器(写操作),再切换为读模式。如果中间发 STOP 再 START,总线会被释放,可能被其他 Master 抢走。

7. 工程应用场景

I2C 适合的场景:

  • 常见 I2C 应用
外设 用途 典型芯片
EEPROM 存储参数 AT24C02/04
LED 驱动 多通道 PWM 调光 PCA9685(16路)
功率监测 电流/电压/功率采集 INA219/INA226
IO 扩展 按键/指示灯扩展 PCF8574
OLED 显示 调试信息/状态显示 SSD1306

不适合的场景

  • 长距离传输(> 1m,信号衰减严重)
  • 高速数据流(摄像头、音频流用 SPI 或并口)
  • 强电磁干扰环境(开关电源附近走 I2C 要注意布线)

布线建议

  1. SDA/SCL 走线尽量短,控制在 30cm 以内
  2. 远离开关电源的功率走线和磁性元件
  3. 上拉电阻靠近主机端放置
  4. 如果总线上挂的设备多(>5个),考虑降低上拉电阻或加总线缓冲器(如 PCA9600)

总结

特性 I2C
线数 2根(SDA + SCL)
拓扑 多主多从,总线型
速率 100k / 400k / 1M / 3.4M
寻址 7bit(最多 112 个有效地址)
应答 每字节后 ACK/NACK
仲裁 线与逻辑,无损仲裁
适用 板内短距离、低速、多外设

I2C 不是最快的协议,但它用最少的线解决了"一个 MCU 管多个外设"的问题。在控制器、传感器节点、小型嵌入式系统中,I2C 几乎是标配。

posted @ 2026-05-31 16:52  cc_record  阅读(24)  评论(0)    收藏  举报