3. I2C协议基础:时钟同步与仲裁、高速模式与标准模式区别
I2C协议,说白了就是两根线(SCL时钟、SDA数据)搞定一堆设备通信。很多刚入行的朋友觉得它简单,不就是发个起始条件、传个地址、读写数据嘛。但真到项目里,时钟同步、仲裁这些细节才是坑。我最早做传感器驱动时,就因为在多主机场景下没处理好仲裁,导致数据错乱,排查了两天才找到原因。
3.1 时钟同步与仲裁——多主机怎么“不打架”
先问一个问题:如果两个主机同时往总线上发数据,会发生什么?
答案是:总线会通过线与(Wired-AND)机制自动处理。谁先拉低SCL,谁就赢得时钟同步权;谁先拉低SDA,谁就赢得数据仲裁权。
3.1.1 时钟同步(Clock Synchronization)
每个主机都有自己的SCL时钟。当多个主机同时工作时,SCL线会被拉低到最慢的那个主机的低电平时间。说白了,就是“谁慢听谁的”。
时钟同步流程:
- 所有主机同时产生SCL时钟
- 第一个拉低SCL的主机决定低电平起始
- 所有主机检测到SCL为低后,都开始计时自己的低电平周期
- 当所有主机都释放SCL(拉高)后,SCL才真正变高
- 高电平时间由最快释放的主机决定
我在项目中遇到过这样的情况:一个主机跑400kHz,另一个跑100kHz,结果总线实际频率被拖到了100kHz。这就是时钟同步在起作用。你想想看,如果没这个机制,两个主机各发各的时钟,总线早就乱套了。
3.1.2 仲裁(Arbitration)
仲裁发生在SDA线上。多个主机同时发送数据时,谁先输出低电平,谁就赢得仲裁。输掉的主机自动转为从机模式,不再参与后续传输。
仲裁的关键规则:
- 仲裁只在SDA线上进行,SCL由时钟同步机制协调
- 仲裁不丢失数据,输掉的主机在仲裁过程中不会破坏总线状态
- 仲裁可以发生在地址阶段,也可以发生在数据阶段
- 输掉的主机必须在仲裁丢失时释放SDA和SCL
我曾经调试过一个多主机的温湿度采集系统,两个MCU都想读取同一个传感器。一开始没做仲裁处理,结果数据全是乱码。后来加上仲裁检测,每次仲裁失败后重试,问题就解决了。
3.2 标准模式与高速模式——速度不是唯一区别
I2C有几种常见的速度模式:标准模式(100kHz)、快速模式(400kHz)、高速模式(3.4MHz)。很多人以为只是频率不同,其实内部机制差异很大。
| 特性 | 标准模式(Sm) | 快速模式(Fm) | 高速模式(Hs) |
|---|---|---|---|
| 最大频率 | 100 kHz | 400 kHz | 3.4 MHz |
| 上升时间 | ≤ 1000 ns | ≤ 300 ns | ≤ 160 ns |
| 下降时间 | ≤ 300 ns | ≤ 300 ns | ≤ 80 ns |
| 输入阈值 | 固定(0.3VDD/0.7VDD) | 固定(0.3VDD/0.7VDD) | 可调(0.2VDD/0.8VDD) |
| 从机地址 | 7位/10位 | 7位/10位 | 7位/10位 |
| 总线电容 | ≤ 400 pF | ≤ 400 pF | ≤ 100 pF |
| 电流驱动能力 | 3 mA | 6 mA | 20 mA |
3.2.1 高速模式的核心变化
高速模式不是简单地把时钟频率提高。它有几个关键改动:
- 主从角色固定:高速模式下,只有当前主机能发起传输,从机不能主动拉低SCL进行时钟拉伸
- 电流驱动增强:为了应对更快的上升时间,输出级需要更强的驱动能力
- 输入阈值可调:标准模式是固定的施密特触发器阈值,高速模式允许调整阈值以适应更小的信号摆幅
- 总线电容限制更严:100pF的限制意味着走线要短,负载要少
注意:高速模式不是所有从机都支持。在进入高速模式前,主机必须先发送一个“主码”(Master Code,00001XXX)来通知总线上的设备切换模式。不支持高速模式的设备会忽略后续传输。
3.2.2 实际项目中的选择
我个人习惯:能用标准模式就别用高速模式。为什么?
- 标准模式对PCB走线要求低,抗干扰能力强
- 高速模式对上升时间敏感,稍有不慎就出时序问题
- 很多传感器本身采样率不高,100kHz完全够用
我记得有一次做摄像头模组驱动,传感器要求用400kHz快速模式。结果因为PCB走线太长,总线电容超标,信号上升沿变缓,导致数据采样错误。最后不得不降低频率到200kHz才稳定工作。
3.3 知识结构图
下面这张图把I2C协议的核心机制串起来了,你可以对照着理解:
3.4 避坑指南
我曾经踩过的坑:
- 时钟拉伸(Clock Stretching):有些从机在高速模式下不支持时钟拉伸。如果你的从机需要时钟拉伸,就别用高速模式。
- 上拉电阻选择:高速模式下,上拉电阻要更小(通常1kΩ~2kΩ),否则上升时间不满足要求。但电阻太小又会增加功耗,需要权衡。
- 仲裁丢失检测:多主机系统中,一定要在发送每个字节后检查仲裁是否丢失。我见过有人只在地址阶段检查,结果数据阶段仲裁失败导致总线锁死。
我的建议:
- 新手先从标准模式开始,跑通后再尝试高速模式
- 多主机系统务必加上仲裁重试机制,重试次数建议3~5次
- 用示波器抓SCL和SDA波形,确认上升时间和下降时间是否达标
- 如果总线电容不确定,先按100pF设计,留足余量