I2C
I2C用SDA、SCL两根开漏线上拉实现同步半双工板级总线,靠地址帧、ACK、起始停止位与时钟拉伸完成一主多从通信,边界是短距低速。
一、两根线,各司其职
I2C(Inter-Integrated Circuit,读作 “I 方 C”)是飞利浦(现 NXP)在 1982 年推出的。它的物理层极简:
表格
| 引脚 | 名称 | 作用 |
|---|---|---|
| SDA | Serial Data | 数据线,双向传输 |
| SCL | Serial Clock | 时钟线,由主机驱动(从机可拉低”暂停”) |
没有 TX/RX 之分,两根线都是开漏(Open-Drain)输出,必须外接上拉电阻到 VCC。正因为是开漏,多个设备可以直接把 SDA/SCL 并在一起——谁想说话,就把线拉低;不想说话,就松手(线被电阻拉回高电平)。
这就是 I2C 的**“线与”逻辑**:只要有一个设备拉低,整条线就是低电平。天然支持多设备仲裁。
二、同步通信:时钟说了算
和 UART 的异步不同,I2C 是同步通信。主机通过 SCL 线发出时钟脉冲,SDA 上的数据必须在 SCL 高电平期间保持稳定,在 SCL 低电平期间翻转。
plain
┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐SCL ───┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘─────┘ └─ ┌───┐ ┌───────┐ ┌─────────┐SDA ───┘ └───┘ └───────┘ └─── D7 D6 D5 D4 D3 D2 D1 D0 ↑ SCL 高电平时 SDA 必须稳定 ↑接收端不用猜”什么时候采样”,盯着 SCL 的上升沿或高电平中段读 SDA 就行。所以 I2C 的时钟可以慢到几 Hz,也可以根据从机能力动态调整——这是异步串口做不到的。
三、一帧数据的结构
I2C 没有”起始位/停止位”的概念,它靠特殊的电平跳变来标记开始和结束。
1. 起始位(Start, S)
SCL 为高电平时,SDA 从高变低。
2. 停止位(Stop, P)
SCL 为高电平时,SDA 从低变高。
3. 数据帧格式
plain
[ S ] [ 从机地址(7bit) + R/W(1bit) ] [ ACK ] [ 数据1(8bit) ] [ ACK ] ... [ 数据N ] [ ACK/NACK ] [ P ] ↑ ↑ ↑ ↑ ↑起始 地址帧,第8位是方向位 从机应答 每8位数据 每字节应答-
地址帧:7 位地址(范围 0x00~0x7F,但 0x00 广播保留,部分地址保留给特殊用途)+ 1 位读写标志(0=写,1=读)。
-
ACK(应答):每传输完 8 位,接收方必须在第 9 个时钟把 SDA 拉低,表示”我收到了”。如果保持高电平,就是 NACK(没收好/没空/结束了)。
-
数据帧:真正的 payload,每字节 8 位,MSB 先传(和 UART 的 LSB 先传相反)。
四、一次完整的读写对话
写操作:主机给从机发数据
假设主机(MCU)要给地址为 0x50 的 EEPROM 写入数据 0xAB 到寄存器 0x10:
plain
主机: [S] [0x50 + W(0)] [ACK] [0x10] [ACK] [0xAB] [ACK] [P] ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ 起始 地址+写 从机应答 寄存器地址 应答 数据 应答 停止读操作:主机从从机取数据
假设主机要从地址 0x50 的 EEPROM 读取寄存器 0x10 的值:
plain
主机: [S] [0x50 + W(0)] [ACK] [0x10] [ACK] [Sr] [0x50 + R(1)] [ACK]从机: [0xAB] [NACK] [P] ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ 起始 地址+写 应答 寄存器 应答 重复起始 地址+读 应答 数据 主机NACK 停止注意读操作通常需要两次起始:第一次告诉从机”我要读哪个寄存器”,第二次切换成读模式。中间的
Sr是重复起始(Repeated Start),不释放总线,防止被其他主机抢走。
五、时钟拉伸:从机”喊暂停”
I2C 里从机不是完全被动的。如果某个从机处理不过来(比如 EEPROM 正在写内部 Flash),它可以把 SCL 拉低,强制主机等待:
plain
SCL ─────┐ ┌─────┐ ┌───┐ ┌───┐ └─────┘ └─────┘ └────────┘ └─── ↑_________↑ 从机把SCL拉低,主机必须等主机检测到 SCL 被自己释放后仍然为低,就进入等待状态,直到从机松手。这叫 Clock Stretching,是 I2C 优雅处理速度不匹配的关键机制。
六、速度等级
I2C 不像 UART 那样随意设波特率,它有几个标准模式:
表格
| 模式 | 速率 | 典型应用 |
|---|---|---|
| 标准模式(Sm) | 100 kHz | 老传感器、EEPROM |
| 快速模式(Fm) | 400 kHz | 主流 MEMS、触摸屏 |
| 快速模式+(Fm+) | 1 MHz | 较新的外设 |
| 高速模式(Hs) | 3.4 MHz | 需要高速吞吐的场景 |
| 超快速模式(UFm) | 5 MHz | 单向传输,较少见 |
实际开发中,如果总线挂的设备多、线缆长,往往需要把速率降到 100 kHz 甚至更低,保证信号完整性。
七、和 UART / RS-485 的对比
表格
| 特性 | UART | RS-485 | I2C |
|---|---|---|---|
| 线数 | 2~3 根(TX/RX/GND) | 2 根(A/B,差分) | 2 根(SDA/SCL) |
| 时钟 | 异步,双方约定波特率 | 异步 | 同步,主机带时钟 |
| 设备数 | 点对点 | 一主多从(32~256) | 一主多从+多主仲裁 |
| 方向 | 全双工 | 半双工为主 | 半双工 |
| 寻址 | 无,靠物理接线区分 | 无,靠轮询或上层协议 | 内置 7/10 位地址 |
| 传输距离 | 短(<15m) | 长(1200m) | 短(<1m 板级,<10m 低速) |
| 速率 | 常见 115200 | 最高 10 Mbps | 最高 3.4 Mbps |
| 应答机制 | 无(硬件层) | 无(硬件层) | 每字节硬件 ACK |
| 抗干扰 | 弱 | 强(差分) | 弱(单端开漏) |
I2C 的设计哲学是**“板级互联”**——同一块电路板上,MCU 和周边芯片之间的短距通讯。如果要跨板、跨机柜、抗强干扰,请回 RS-485 的怀抱。
八、实际接线与上拉电阻
plain
VCC (3.3V / 5V) │ ┌┴┐ │ │ Rp(典型 4.7kΩ,取决于总线电容和速率) └┬┘ ├──────────┬──────────┐ SDA SCL │ │ │ GND ┌────┴────┐ │ MCU 传感器A 传感器B (主机) (从机) (从机)上拉电阻选多大?
-
电阻太小:功耗大,低电平时电流大,开漏管可能拉不动
-
电阻太大:上升沿变缓,高速时信号变形
-
经验值:4.7kΩ @ 100kHz,2.2kΩ @ 400kHz,1kΩ @ 1MHz
-
多设备并联时,总线电容增大,电阻适当减小或降低速率
九、常见问题与排雷
表格
| 现象 | 可能原因 |
|---|---|
| 总线一直低,无法启动 | 某个设备死锁,持续拉低 SDA 或 SCL;或上拉电阻虚焊 |
| 偶尔丢数据 | 线缆太长、上拉电阻太大、速率太高;或缺少 ACK 处理 |
| 多设备冲突 | 地址冲突(两个从机地址相同);或多主机场景没做仲裁 |
| 读数据全为 0xFF | 从机没应答(NACK),SDA 被电阻拉高了 |
十、总结
表格
| 概念 | 一句话理解 |
|---|---|
| SDA / SCL | 数据线和时钟线,开漏输出,必须上拉 |
| 起始/停止 | SCL 高时,SDA 下降沿=开始,上升沿=结束 |
| 7 位地址 + R/W | 先喊门牌号,第 8 位告诉对方”我要读还是写” |
| ACK | 每传 8 位,接收方拉低 SDA 表示”收到” |
| 时钟拉伸 | 从机拉低 SCL 喊暂停,主机乖乖等着 |
| 重复起始 | 不挂电话直接转读模式,防止总线被抢 |
| 上拉电阻 | 开漏的命根子,决定信号质量和功耗 |
十一、一点延伸
I2C 的地址只有 7 位(128 个),现代芯片越来越多,出现了地址冲突。解决方案:
-
10 位地址扩展:用两个字节发地址,兼容老设备
-
I2C 开关/多路复用器(如 TCA9548A):一条总线分成多条,软件选通
-
SMBus / PMBus:基于 I2C 的工业/电源管理子集,增加了超时、PEC 校验等
如果你的项目只是在同一块 PCB 上连几个传感器、RTC、OLED,I2C 是最省引脚、最省 PCB 走线的选择。两根线,一排设备,优雅又简洁。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
评论区
正在加载 GitHub 评论…