资讯动态

I2C多主机仲裁与时钟延展:开漏输出、线与逻辑及非破坏性冲突解决机制

发布时间:2026/9/27 10:37:01 来源:尧图企业网站定制
1. 为什么多主机仲裁是 I2C 的灵魂设计1.1 从一根线说起开漏输出与线与逻辑I2C 总线最容易被低估的地方就是它只有两根线一根 SDA数据线一根 SCL时钟线。但这两根线要挂载多个设备而且这些设备可能同时想说话。如果每个设备都用推挽输出一个输出高电平、另一个输出低电平直接就是电源对地短路芯片当场冒烟。所以 I2C 从物理层就规定所有设备的 SDA 和 SCL 引脚必须是开漏输出Open-Drain或者开集电极输出。开漏输出的结构很简单内部只有一个 NMOS 管漏极接到引脚源极接地栅极由内部逻辑控制。引脚外部必须接一个上拉电阻到 VDD。当 NMOS 导通时引脚被拉到地输出低电平当 NMOS 截止时引脚处于高阻态由外部上拉电阻把电平拉高。这里的关键在于任何设备都只能主动拉低不能主动拉高。高电平是大家都不拉低时由上拉电阻“被动”形成的。这就形成了 I2C 最基础的“线与”Wired-AND逻辑只要有一个设备输出低电平整条总线就是低电平只有当所有设备都释放总线输出高阻总线才被上拉电阻拉高。这个特性是后面所有仲裁和时钟同步的物理基础。你可以把它想象成一群人在一个房间里举手表决只要有人举手反对拉低决议就不能通过总线为低只有所有人都放下手释放决议才通过总线为高。注意上拉电阻的阻值选择很讲究。阻值太小功耗大低电平灌电流可能超过器件的驱动能力阻值太大上升沿变缓高速通信时波形还没到高电平就被拉低了。常见取值在 2.2kΩ 到 10kΩ 之间标准模式 100kHz 常用 4.7kΩ快速模式 400kHz 常用 2.2kΩ 左右。具体要看总线电容和器件手册。1.2 多主机仲裁要解决的核心问题I2C 是多主机总线意味着总线上可以挂多个主设备。问题来了如果两个主机同时想发起传输怎么办如果它们发送的数据完全一样那还好办总线上波形一致谁发都一样。但如果它们想访问不同的从机或者写不同的数据就必然产生冲突。I2C 的仲裁机制就是用来解决这个冲突的让赢得仲裁的主机继续传输输掉仲裁的主机立刻退出并且不破坏已经传输的数据。这个机制的精妙之处在于仲裁是非破坏性的。输掉的主机不会把总线搞乱赢的主机甚至不知道有人跟它竞争过。整个过程就像两个人在同一根电话线上同时说话但系统能自动识别谁的声音更大让声音小的人闭嘴而声音大的人继续说完且完全没察觉到有人插嘴。1.3 仲裁发生在哪些位仲裁可以发生在多个位置地址帧的每一位、数据帧的每一位、甚至应答位。只要主机在发送一位时释放 SDA想发 1但检测到 SDA 实际为低被别人拉低了它就立刻知道自己输了仲裁马上切换为从机模式或者释放总线。这里有一个关键规则仲裁只发生在主机发送数据的时候从机不参与仲裁。因为从机只在被寻址后才响应不会主动发起传输。具体来说仲裁过程是这样的两个主机同时发送起始条件后开始发送地址。每个主机在 SCL 高电平期间把要发送的位放到 SDA 上然后在 SCL 高电平中间采样 SDA。如果主机 A 想发 1释放 SDA主机 B 想发 0拉低 SDA那么 SDA 实际是低。主机 A 采样到低但自己发的是 1就知道自己输了立刻停止驱动 SDA 和 SCL退出仲裁。主机 B 继续传输完全不受影响。这里有个细节主机 A 退出后它必须立即释放 SDA 和 SCL并且不能再产生时钟脉冲。但它可能还需要继续接收后面的数据因为它可能被赢得仲裁的主机寻址为从机。所以 I2C 规定输掉仲裁的主机要切换到从机接收模式继续监听总线。2. 时钟延展让慢速设备也能跟上节奏2.1 为什么需要时钟延展I2C 的时钟 SCL 是由主机产生的。标准模式下 100kHz快速模式 400kHz高速模式 3.4MHz。但总线上挂的从机五花八门有快速的 EEPROM有慢速的传感器还有更慢的微控制器。如果主机以 400kHz 的速度发数据而某个从机只能处理 100kHz那从机还没准备好主机就把下一个时钟脉冲发过来了数据就丢了。时钟延展Clock Stretching就是解决这个问题的从机可以通过拉低 SCL 来强制主机等待。当从机需要更多时间处理数据或准备下一个字节时它就在 SCL 低电平期间把 SCL 拉低并且保持住。主机发送完当前位后想拉高 SCL但发现 SCL 还是低被从机拉住了就知道从机还没准备好于是等待。直到从机释放 SCL主机才能继续产生时钟。这个机制非常巧妙它不需要任何额外的握手信号也不需要主机事先知道从机的速度。从机用自己的节奏“拖住”主机主机自动适应。就像老师讲课学生听不懂就举手老师看到举手就停下来等等学生放下手再继续讲。2.2 时钟延展的具体时序时钟延展发生在 SCL 低电平期间。正常情况下主机把 SCL 拉低一段时间然后释放SCL 被上拉电阻拉高。但在时钟延展时从机在主机释放 SCL 之后仍然把 SCL 拉低。主机检测到 SCL 没有按预期变高就知道从机在延展时钟。具体时序如下主机拉低 SCL开始一个时钟周期。主机释放 SCL准备让 SCL 变高。从机此时拉低 SCL保持低电平。主机检测到 SCL 仍为低进入等待状态。从机处理完内部事务释放 SCL。SCL 被上拉电阻拉高主机检测到高电平继续下一个时钟周期。这里有一个关键点时钟延展只能发生在 SCL 低电平期间。如果从机在 SCL 高电平期间拉低 SCL那就不是时钟延展而是总线冲突或者仲裁。因为 SCL 高电平期间所有设备都应该释放 SCL只有主机在驱动 SCL 低电平到高电平的转换。注意不是所有主机都支持时钟延展。有些硬件 I2C 控制器不支持从机延展时钟遇到从机拉低 SCL 会报超时错误。所以在选型时要确认主机的 I2C 控制器是否支持时钟延展。软件模拟 I2C 通常都支持因为你可以自己控制时序。2.3 时钟延展与仲裁的交互时钟延展和仲裁可以同时发生。比如两个主机在仲裁同时某个从机在延展时钟。这时候 SCL 可能被从机拉低而 SDA 上的仲裁还在进行。但仲裁是在 SDA 上进行的SCL 被拉低只是让仲裁过程变慢不会影响仲裁结果。因为仲裁是在 SCL 高电平期间采样 SDA如果 SCL 被从机拉低主机就等待等 SCL 变高后再采样 SDA。这里有一个容易混淆的地方时钟同步Clock Synchronization和时钟延展Clock Stretching是两个不同的概念但经常一起出现。时钟同步是指多个主机同时产生 SCL 时SCL 的低电平周期由所有主机中最长的低电平决定高电平周期由所有主机中最短的高电平决定。这样多个主机的时钟就“同步”了大家步调一致。时钟延展是指从机拉低 SCL 来延长低电平周期。两者都利用了 SCL 的线与特性但发起者不同时钟同步是主机之间的时钟延展是从机对主机的。3. 仲裁与时钟延展的硬件实现细节3.1 开漏输出的电路结构要理解仲裁和时钟延展必须先把开漏输出的电路结构搞清楚。一个典型的 I2C 引脚内部结构是这样的一个 NMOS 管漏极接引脚源极接地栅极接内部逻辑。引脚外部接一个上拉电阻 Rp 到 VDD。当内部逻辑输出 1 时NMOS 截止引脚高阻外部上拉电阻把引脚拉高当内部逻辑输出 0 时NMOS 导通引脚被拉到地输出低电平。同时引脚上还有一个输入缓冲器用来读取引脚的实际电平。这个输入缓冲器一直工作不管引脚是输出还是输入。所以主机在发送数据的同时也能读到引脚上的实际电平。这就是仲裁的硬件基础发送和读取同时进行。推挽输出则不同推挽输出有上下两个管子上管导通时输出高下管导通时输出低。如果两个推挽输出连在一起一个输出高一个输出低就会形成从 VDD 到 GND 的低阻通路电流巨大可能烧毁芯片。所以 I2C 绝对不能用推挽输出。实操心得如果你用 GPIO 模拟 I2C一定要把 GPIO 配置为开漏输出模式并且外部接上拉电阻。有些 MCU 的 GPIO 支持内部上拉但内部上拉电阻通常较大几十 kΩ高速通信时上升沿太慢建议还是用外部上拉。我实测过用内部上拉跑 100kHz 勉强可以跑 400kHz 波形已经惨不忍睹了。3.2 仲裁的逐位比较过程仲裁是逐位进行的。假设主机 A 和主机 B 同时发送起始条件然后开始发送地址。地址是 7 位加上一位读写位共 8 位。每一位的仲裁过程如下在 SCL 低电平期间主机 A 和主机 B 分别把自己的位放到 SDA 上。如果发 1就释放 SDA如果发 0就拉低 SDA。SCL 被释放被上拉电阻拉高。在 SCL 高电平期间主机 A 和主机 B 都采样 SDA。如果主机 A 发 1但采样到 SDA 为 0说明主机 B 发了 0主机 A 输掉仲裁立即停止驱动 SDA 和 SCL退出。如果主机 A 发 0采样到 SDA 为 0它不知道是自己拉低的还是别人拉低的继续。如果主机 A 发 1采样到 SDA 为 1说明没有人和它冲突继续。这个过程一直持续到某一位出现冲突或者所有位都发送完毕。如果两个主机发送的地址和数据完全一样它们会一直仲裁到最后然后同时发送停止条件或者其中一个发送停止条件另一个继续。但这种情况很少见因为两个主机同时访问同一个从机的同一个寄存器概率极低。这里有一个关键点仲裁输掉的主机必须立即释放 SDA 和 SCL并且不能再产生时钟。但它可能还需要继续接收数据因为它可能被赢得仲裁的主机寻址为从机。所以 I2C 规定输掉仲裁的主机要切换到从机接收模式继续监听总线。3.3 时钟同步的详细过程时钟同步发生在多个主机同时产生 SCL 的时候。每个主机都有自己的 SCL 低电平周期和高电平周期。由于 SCL 是线与的实际 SCL 的低电平周期由所有主机中最长的低电平决定高电平周期由所有主机中最短的高电平决定。具体过程如下主机 A 和主机 B 同时拉低 SCLSCL 变低。主机 A 的低电平周期先结束它释放 SCL。但主机 B 还在拉低 SCL所以 SCL 仍然为低。主机 A 检测到 SCL 仍为低知道主机 B 还在拉低于是等待。主机 B 的低电平周期结束释放 SCL。SCL 被上拉电阻拉高。主机 A 和主机 B 都检测到 SCL 变高开始高电平周期。主机 A 的高电平周期先结束它拉低 SCL。SCL 变低。主机 B 检测到 SCL 变低知道主机 A 先拉低了于是开始自己的低电平周期。这样SCL 的低电平周期由所有主机中最长的低电平决定高电平周期由所有主机中最短的高电平决定。最终 SCL 的频率由最慢的主机决定但高电平时间由最快的主机决定。这保证了所有主机都能在自己的时序范围内工作。注意时钟同步只发生在多主机场景。单主机时SCL 完全由主机控制从机只能通过时钟延展来影响 SCL 的低电平周期。4. 实操中如何调试仲裁与时钟延展问题4.1 用逻辑分析仪抓取仲裁过程调试仲裁问题最有效的工具是逻辑分析仪。把逻辑分析仪的通道接到 SDA 和 SCL 上设置触发条件为起始条件然后让两个主机同时发起传输。逻辑分析仪会记录下整个波形你可以清楚地看到哪一位发生了仲裁哪个主机赢了哪个主机输了。看波形时重点关注 SCL 高电平期间的 SDA 电平。如果某个主机在 SCL 高电平期间释放了 SDA想发 1但 SDA 实际为低说明它输了仲裁。你还可以看到输掉仲裁的主机在哪个时刻释放了 SDA 和 SCL以及它是否切换到了从机接收模式。实操心得逻辑分析仪的采样率要足够高至少是 SCL 频率的 10 倍以上。比如 400kHz 的 I2C采样率至少 4MHz最好 10MHz 以上。采样率太低可能会漏掉窄脉冲导致解码错误。我用过 24MHz 采样率的逻辑分析仪抓 400kHz 的 I2C 完全够用。4.2 时钟延展导致的超时问题时钟延展最常见的问题是主机超时。有些硬件 I2C 控制器在等待 SCL 变高时如果超过一定时间还没变高就会报超时错误并释放总线。这时候从机还在拉低 SCL主机已经放弃了总线就挂了。解决这个问题的方法有几种换一个支持时钟延展的主机控制器。很多 MCU 的 I2C 控制器都支持时钟延展但有些低端型号不支持。选型时要看手册。用软件模拟 I2C。软件模拟可以自己控制超时时间甚至可以无限等待。降低 SCL 频率。如果主机跑 100kHz从机处理时间相对充裕可能不需要延展时钟。优化从机固件。让从机尽快处理数据减少时钟延展的时间。我踩过的坑有一次用某款 MCU 的硬件 I2C 读一个慢速传感器传感器偶尔会延展时钟但 MCU 的 I2C 控制器超时时间设得太短导致偶尔读失败。后来把超时时间改长问题就解决了。所以遇到偶发的 I2C 读失败先查查是不是时钟延展导致的超时。4.3 仲裁失败后的恢复仲裁失败的主机需要正确地恢复到从机接收模式或者释放总线。如果恢复不当可能会导致总线死锁。比如输掉仲裁的主机没有及时释放 SDA导致赢得仲裁的主机无法继续传输。正确的恢复流程是检测到仲裁失败发 1 但采样到 0。立即释放 SDA 和 SCL。切换到从机接收模式继续监听总线。如果赢得仲裁的主机寻址的是自己就正常响应如果不是就忽略后续数据直到停止条件。注意有些 I2C 控制器在仲裁失败后会自动切换到从机模式有些则需要软件干预。用硬件 I2C 时要仔细看手册确认仲裁失败后的行为。用软件模拟时要自己实现这个逻辑。4.4 常见问题速查表问题现象可能原因排查方法解决方案总线一直为低某个设备拉低 SDA 或 SCL 不放逐个断开设备看总线是否恢复检查该设备的 I2C 状态机确保正确释放总线仲裁失败后总线死锁输掉仲裁的主机没有释放 SDA用逻辑分析仪看仲裁失败后的波形确保仲裁失败后立即释放 SDA 和 SCL时钟延展导致超时主机不支持时钟延展或超时太短看波形SCL 被从机拉低超过主机超时时间换主机、改软件模拟、降低频率、优化从机上升沿太慢上拉电阻太大或总线电容太大看波形上升沿是否超过 1us减小上拉电阻减少总线电容偶发读失败时钟延展、电源噪声、地址冲突用逻辑分析仪抓失败时的波形针对性解决5. 从仲裁和时钟延展看 I2C 的设计哲学5.1 用最简单的硬件实现最复杂的功能I2C 只用两根线就实现了多主机、多从机、仲裁、时钟同步、时钟延展、应答、错误检测等功能。这背后是极其精巧的设计开漏输出实现了线与逻辑线与逻辑实现了仲裁和时钟同步时钟同步实现了多主机协调时钟延展实现了速度适配。每一层都建立在下一层的基础上层层递进环环相扣。这种设计哲学值得每个嵌入式工程师学习用最简单的硬件实现最复杂的功能。不需要额外的仲裁线不需要额外的握手信号不需要复杂的协议栈。两根线一个上拉电阻就够了。5.2 非破坏性仲裁的启示非破坏性仲裁是 I2C 最精妙的设计之一。输掉仲裁的主机不会破坏已经传输的数据赢的主机甚至不知道有人跟它竞争过。这种“无感”的冲突解决方式在分布式系统中非常有借鉴意义。比如在多核系统中多个核心同时访问共享内存也可以用类似的机制每个核心在写数据的同时读回数据如果发现读回的数据和自己写的不一样说明有冲突就退出重试。这样不需要全局锁也能保证数据一致性。5.3 时钟延展的“慢者优先”原则时钟延展体现了一种“慢者优先”的原则快的主机要等慢的从机。这在硬件设计中是反直觉的因为通常都是快的等慢的但 I2C 把它做成了标准机制。这种设计保证了不同速度的设备可以共存于同一总线不需要事先协商速度也不需要额外的速度选择信号。我个人在实际操作中的体会是I2C 的仲裁和时钟延展看起来是协议细节实际上是整个协议的灵魂。理解了这两个机制你就理解了 I2C 为什么能成为最流行的低速总线之一。很多人在调试 I2C 时遇到问题往往是因为没有理解这两个机制导致排查方向错误。比如总线死锁很多时候是仲裁失败后没有正确释放总线比如偶发读失败很多时候是时钟延展导致的超时。把这两个机制吃透I2C 的调试就成功了一大半。最后再分享一个小技巧如果你用软件模拟 I2C在检测仲裁失败时不要立即完全释放总线而是先释放 SDA再释放 SCL并且确保释放的顺序正确。因为如果 SCL 还被拉低SDA 的变化可能被从机误认为是数据。正确的顺序是先释放 SDA再释放 SCL然后切换到从机接收模式。这个细节在手册里通常不会写但实际调试时非常关键。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑