I2C物理层深度解析:开漏输出、上拉电阻与两线制通信原理
2026/9/24 4:04:26 网站建设 项目流程

1. 从一次诡异的通信失败说起

几年前调一块传感器板子,主控和从设备离得不到三厘米,走线短得不能再短,按道理这种距离下I2C应该稳如老狗。结果上电之后,逻辑分析仪抓出来的波形让我愣了半天:SCL时钟干干净净,SDA数据线却像被人用手按住了一样,始终趴在一个不高不低的电平上,既不是明确的高,也不是明确的低。主控发地址没回应,读数据全是0xFF。

我第一反应是器件坏了,换了一片,一样。又怀疑是地址配错,翻手册确认了三遍,没问题。最后拿万用表一量SDA对地的电阻,才发现问题所在——这条线上挂了一个推挽输出的器件,它只要一拉高,就和另一个器件的下拉形成了硬碰硬的对拉,两条低阻抗通路直接顶在一起,电平自然卡在中间。

那次之后我才真正把"I2C为什么必须开漏"这件事从"考试会考"变成了"肌肉记忆"。这篇就围绕两线制物理层这个最底层的话题,把I2C的开漏结构、上拉电阻、时序约束、常见故障一次讲透。不管你是刚接触I2C的新手,还是调过很多板子但没系统梳理过物理层的老手,这篇都值得从头看一遍——因为绝大多数I2C通信问题,根子都埋在这一层。

2. 两线制到底"两"在哪:SDA与SCL的角色分工

2.1 一根线传数据,一根线定节拍

I2C全称Inter-Integrated Circuit,物理层上只有两根信号线:SDA(Serial Data)负责承载数据位和地址位,SCL(Serial Clock)负责提供同步时钟。加上地线,一个最简的I2C连接就是三根线(SDA、SCL、GND),如果从设备需要供电,再加一根VCC,四根线搞定。

这种极简的连线方式是I2C最大的卖点。对比一下:UART点对点至少要TX、RX两根加地线,而且只能一对一;SPI要四根线(SCLK、MOSI、MISO、CS),每多一个从设备就多一根片选;而I2C用两根线就能挂一整个总线上的几十个器件,靠的是地址寻址而不是片选线。这就是为什么板级低速外设——EEPROM、温度传感器、加速度计、IO扩展芯片、PMBus电源管理——几乎清一色选I2C。

但"两线"这个设计不是没有代价的。正因为两根线要同时承担"多个器件共享"和"双向通信"两个职责,物理层就必须解决一个核心矛盾:多个器件怎么在同一根线上和平共处,而不互相打架。这个矛盾的答案,就是开漏输出加外部上拉。

2.2 总线是"线与"逻辑,不是"或"逻辑

理解I2C物理层,最关键的一句话是:I2C总线上的电平是所有挂载器件输出的"线与"(wired-AND)结果

什么叫线与?就是只要有一个器件把线拉低,整条线就是低;只有当所有器件都不拉低(都释放总线)时,线才通过上拉电阻变成高。这跟"或"逻辑正好相反——或逻辑是有一个拉高就是高。

这个特性直接决定了I2C能做时钟同步总线仲裁。比如两个主设备同时发起传输,它们各自输出的SCL周期可能不同,但因为线与,实际SCL是高电平最短的那个决定(谁先把线拉低谁说了算),低电平最长的那个决定,最终两个主设备会自动同步到一个共同时钟上。仲裁也是同理,谁发的数据是1(释放)而别人发的是0(拉低),谁就立刻知道自己输了,主动退出。

如果换成推挽输出,一个器件拉高、另一个拉低,就是电源到地的低阻通路,轻则电平错乱,重则烧器件。所以线与逻辑是I2C多设备共享的物理基础,而开漏是实现线与的唯一手段

3. 开漏输出的内部结构:一颗MOS管撑起的双向总线

3.1 开漏的"漏"指的是什么

开漏(Open-Drain)这个名字来自MOSFET的结构。一个N沟道MOS管有三个主要端子:栅极(Gate)、源极(Source)、漏极(Drain)。开漏输出就是把漏极直接引到引脚上,不接任何内部上拉,源极接地,栅极由内部驱动电路控制。

工作逻辑非常简单:

  • 栅极给高电平,MOS管导通,漏极被拉到地,引脚输出低电平
  • 栅极给低电平,MOS管截止,漏极悬空(高阻态),引脚不输出任何电平,此时靠外部上拉电阻把线拉到高电平。

注意这里的关键:开漏输出只能主动拉低,不能主动拉高。高电平是"被动"来的,是外部上拉电阻给的。这就是为什么I2C必须外接上拉电阻——没有上拉,线永远悬空,读到的永远是无效电平。

3.2 为什么不是开集(Open-Collector)

老一点的资料里会提到开集电极输出,那是双极型晶体管(BJT)时代的说法,集电极开路。原理完全一样:三极管导通时集电极拉低,截止时集电极悬空。现在CMOS工艺下用MOS管,就叫开漏。两者在逻辑行为上等价,只是器件类型不同。你在看一些老芯片手册时看到"open-collector"不用慌,理解成开漏就行。

3.3 双向引脚是怎么实现的

I2C的SDA是双向线,同一个引脚既要输出又要输入。开漏结构天然支持这一点:输出级只有一个下拉MOS管,输入级是一个高输入阻抗的缓冲器接在引脚上。当器件要读总线时,把自己的下拉管关掉(释放总线),此时引脚对外呈现高阻,输入缓冲器就能读到线上其他器件或上拉电阻决定的电平。

这种"输出时只管拉低、读的时候先释放"的机制,是I2C能做应答(ACK/NACK)时钟拉伸(Clock Stretching)的根本。从设备收到一个字节后,在第九个时钟把SDA拉低表示ACK,这个拉低动作就是通过它自己的开漏管完成的,主设备此时已经释放了SDA,所以不会冲突。

4. 推挽输出为什么在I2C上会出事

4.1 推挽的结构与它的"强势"

推挽(Push-Pull)输出内部有一对互补的MOS管:上面一个P沟道管接VCC,下面一个N沟道管接地。输出高时上管导通、下管截止,引脚被主动拉到VCC;输出低时下管导通、上管截止,引脚被主动拉到地。

推挽的优点是驱动能力强、上升沿陡峭、速度快,所以SPI、UART这些点对点或带片选的协议都用推挽。但它的"强势"恰恰是I2C的灾难:它不允许别人插手,输出高就是硬拉到VCC,输出低就是硬拉到地。

4.2 两个推挽器件对拉会发生什么

假设总线上有两个推挽器件A和B。A输出高(上管导通,引脚接VCC),B输出低(下管导通,引脚接地)。此时从VCC经过A的上管、再经过B的下管到地,形成一条完整的低阻通路。这条通路的电流只受两个MOS管的导通电阻限制,可能达到几十甚至上百毫安。

后果分几档:

情况现象
导通电阻较大电平卡在中间值,逻辑判断混乱,通信失败
导通电阻较小大电流持续流过,芯片发热,长期可能烧毁
多器件同时冲突电流叠加,最坏情况直接冒烟

这就是我开头那次踩坑的本质。那个推挽器件只要一输出高,就和总线上其他拉低的器件顶上了牛。

4.3 开漏与推挽的对比

特性开漏输出推挽输出
高电平来源外部上拉电阻内部上管主动拉高
能否主动拉高不能
多器件共享同一线可以(线与)不可以(会冲突)
上升沿速度受上拉电阻和线容限制,较慢
典型应用I2C、SMBus、PMBus、部分中断线SPI、UART、普通GPIO
是否需要外部上拉必须不需要

看到这张表,I2C选开漏的理由就一目了然了:它要共享,就必须放弃主动拉高的能力,把拉高的活儿交给上拉电阻,从而保证任何时刻都不会出现两个器件对拉

5. 上拉电阻:不是随便焊一个就行

5.1 上拉电阻决定了上升沿

开漏输出的高电平是被动建立的,靠的是上拉电阻给总线电容充电。总线电容包括PCB走线电容、器件引脚电容、连接器电容,典型值在几十到几百皮法。上升时间由RC充电常数决定:

t_r ≈ 0.847 × R_pullup × C_bus

这个0.847来自RC充电从0.3VDD到0.7VDD的时间(I2C标准定义的上升沿测量区间)。举个例子,C_bus = 200pF,R_pullup = 4.7kΩ,那么t_r ≈ 0.847 × 4700 × 200e-12 ≈ 0.8μs。对于100kHz标准模式(周期10μs),这个上升时间完全够用;但如果跑400kHz快速模式(周期2.5μs),0.8μs的上升沿就偏大了,可能逼近时序极限。

5.2 上拉电阻的上下限怎么算

上拉电阻不是越小越好,也不是越大越好,它被两个约束夹在中间。

下限由灌电流决定。器件拉低时,电流从VCC经上拉电阻灌入器件的开漏管。I2C标准规定标准模式和快速模式下,器件的灌电流能力至少3mA,所以:

R_min = (VDD - V_OL_max) / I_OL_max

以VDD=3.3V、V_OL_max=0.4V、I_OL=3mA计算,R_min = (3.3-0.4)/0.003 ≈ 967Ω。也就是说上拉电阻不能小于约1kΩ,否则灌电流超标,器件可能拉不到足够低的电平。

上限由上升时间和漏电流决定。电阻太大,上升沿太慢,还没充到高电平下一个时钟就来了;同时总线漏电流在电阻上产生的压降也可能让高电平达不到VIH_min。经验上:

R_max ≈ t_r_max / (0.847 × C_bus)

还是以400kHz、C_bus=200pF、允许t_r=300ns为例,R_max ≈ 300e-9/(0.847×200e-12) ≈ 1771Ω。这就有点尴尬了,下限967Ω、上限1771Ω,可选范围很窄。

5.3 常用取值与场景对照

总线速率总线电容推荐上拉说明
100kHz<100pF4.7kΩ~10kΩ板内短走线,最常见
100kHz100~200pF2.2kΩ~4.7kΩ多器件或稍长走线
400kHz<100pF2.2kΩ~4.7kΩ快速模式
400kHz100~200pF1kΩ~2.2kΩ需权衡灌电流
1MHz+<50pF1kΩ左右高速模式,通常需专用缓冲

提示:很多开发板默认焊4.7kΩ,是因为板子小、器件少、电容小,这个值在100kHz下几乎万能。但一旦你把总线引到排线、接上多个模块,4.7kΩ可能就偏大了,波形上升沿会明显变缓。

5.4 上拉电阻小了不通信的反直觉现象

热词里有个"i2c上拉电阻小了不通信",这确实存在,而且很多人想不通——电阻小不是上升更快吗,怎么会不通信?

原因在于灌电流。电阻太小,器件拉低时灌入的电流超过它的额定能力,开漏管的V_OL被抬高,可能超过VIH_max(低电平上限,通常0.3VDD)。主设备读到的"低"不够低,就被判成高,数据全错。另外,如果总线上有多个器件同时拉低,电流叠加更严重。所以上拉电阻是"够用就好",不是越小越猛。

6. 从时序图看物理层的硬约束

6.1 起始与停止条件为什么这么定义

I2C的起始条件(START)是SCL为高时,SDA由高变低;停止条件(STOP)是SCL为高时,SDA由低变高。为什么这么定义?因为在正常数据传输期间,SDA只在SCL为低时变化,SCL为高时SDA必须稳定。所以"SCL高时SDA跳变"就成了一个不会在数据中出现的特殊模式,天然适合做帧边界标志。

这个定义对物理层有要求:SDA从高到低的跳变必须足够快、足够干净,否则主设备可能误判。而SDA的上升沿又受上拉电阻限制,所以起始条件之后紧接着的第一个时钟,SDA必须已经稳定在高电平——这就把上拉电阻的上限又收紧了一点。

6.2 建立时间与保持时间

I2C标准对时序有明确要求,以标准模式100kHz为例:

参数含义最小值单位
t_HD;STA起始条件保持时间4.0μs
t_LOWSCL低电平时间4.7μs
t_HIGHSCL高电平时间4.0μs
t_SU;DAT数据建立时间250ns
t_HD;DAT数据保持时间0ns
t_SU;STO停止条件建立时间4.0μs
t_r上升时间1000ns(最大)
t_f下降时间300ns(最大)

注意t_r最大1000ns,这就是上拉电阻和总线电容必须满足的硬指标。下降时间t_f由开漏管的导通能力决定,通常比上升快得多,因为拉低是主动的、低阻的。

6.3 时钟拉伸:从设备的"暂停键"

时钟拉伸是I2C一个很实用的机制。从设备如果处理不过来,可以在ACK之后把SCL拉低,主设备发现SCL没按预期变高,就知道从设备还没准备好,于是等待。这个拉低动作也是通过从设备的开漏管完成的——如果从设备是推挽输出,它拉低SCL时主设备可能正在拉高,又是对拉。

时钟拉伸对物理层的要求是:主设备的SCL输出也必须是开漏,并且主设备要能检测SCL的实际电平而不是只看自己输出什么。很多用GPIO模拟I2C的代码在这里翻车,就是因为只写不读,没做SCL回读。

7. 实战排查:I2C不通时先看物理层

7.1 用逻辑分析仪抓什么

逻辑分析仪是调I2C的标配。抓波形时重点看四件事:

  1. 空闲电平:SDA和SCL在不通信时是否都是高。如果有一条是低,说明有器件在死拉总线,或者上拉电阻没焊/虚焊。
  2. 上升沿形状:是不是明显的RC充电曲线。如果上升沿很缓,说明上拉电阻偏大或总线电容偏大。
  3. 起始条件:SCL高时SDA有没有干净的下降沿。
  4. ACK位:第九个时钟SDA是否被从设备拉低。如果一直是高,说明从设备没应答,可能是地址错、器件没上电、或者物理层电平不达标。

7.2 万用表能查出的物理层问题

逻辑分析仪看动态,万用表看静态。断电测电阻:

  • SDA对VCC的电阻应该等于上拉电阻值(如果只有一个上拉);
  • SDA对地的电阻应该是无穷大(除非有器件内部有下拉);
  • SDA和SCL之间不应该有低阻通路。

上电测电压:

  • 空闲时SDA、SCL都应该是VCC附近;
  • 如果某条线只有1V多,说明上拉电阻太大或有强下拉。

7.3 常见故障对照表

现象可能原因排查方向
空闲时SDA为低器件死拉、上拉缺失、短路逐个断开器件定位
上升沿极缓上拉电阻过大、电容过大减小上拉、缩短走线
低电平不够低上拉电阻过小、灌电流超标增大上拉
通信偶尔成功时序临界、干扰降速、优化上拉
完全无应答地址错、器件没上电、物理层断查地址、查供电、查连线
多器件时冲突有推挽器件混入确认所有器件都是开漏

7.4 一个容易被忽略的坑:电平不匹配

3.3V主控接5V从设备,或者反过来,是另一个高频坑。I2C的开漏结构其实给电平转换提供了便利:只要上拉电阻接到目标电压,开漏管拉低时两边都能拉到地,高电平由各自的上拉决定。但前提是器件的开漏管耐压要够,而且不能有内部钳位二极管把电压倒灌回去。稳妥做法是用专用的I2C电平转换芯片,或者用MOS管做双向电平转换,不要图省事直接上拉到了事。

8. 几个延伸问题的一次性说清

8.1 I2C、SMBus、PMBus的物理层关系

SMBus是I2C的一个子集,物理层几乎一样,都是开漏加两线,但SMBus对时序、电平、超时要求更严,比如它有35ms的超时机制,防止从设备死拉总线。PMBus又建立在SMBus之上,主要面向电源管理,物理层沿用。所以你在PMBus和I2C之间纠结时,物理层可以认为是一致的,差异在协议层。

8.2 为什么I2C速率上不去

I2C标准模式100kHz、快速模式400kHz、快速加模式1MHz、高速模式3.4MHz。相比SPI动辄几十MHz,I2C慢得明显。根子就在开漏加RC充电:上升沿是被动建立的,速率越高,允许的上升时间越短,对上拉电阻和总线电容的要求越苛刻。到了高速模式,往往需要专门的缓冲器或电流源上拉,普通电阻上拉已经力不从心。

8.3 多主设备的仲裁为什么不会损坏总线

因为所有主设备都是开漏。仲裁时,一个主设备输出高(释放),另一个输出低(拉低),线与结果是低,输出高的那个读回发现自己发的是高但线上是低,就知道自己输了,立刻退出并释放总线。整个过程没有任何对拉,因为谁都不会主动拉高。这就是开漏对多主架构的天然支持。

9. 我在实际项目里总结的几条经验

第一,画原理图时上拉电阻一定要画,而且标注清楚阻值和位置。我见过太多人以为芯片内部有上拉就不画,结果芯片内部上拉往往是弱上拉(几十kΩ),跑100kHz都勉强。外部上拉是必须的,内部上拉只能算锦上添花。

第二,上拉电阻尽量靠近主设备或总线中点,不要放在总线末端。放在末端的话,上拉电流要流过整条走线才能给远端器件充电,等效电容更大,上升沿更慢。

第三,调试时先降速。通信不稳时,先把速率从400kHz降到100kHz,如果降速后稳定了,基本可以确定是物理层时序余量不够,重点查上拉和电容。这个方法能帮你快速区分"物理层问题"和"协议层问题"。

第四,多器件总线要逐个上电验证。新板子第一次调试,不要一次把所有从设备都焊上。先焊一个,通了再加下一个,这样一旦出问题能立刻定位到是哪个器件引入的。我吃过一次亏,八个器件一起焊,结果一个器件的地址和另一个冲突,查了一下午。

第五,逻辑分析仪的解码功能要用起来。光看波形容易漏细节,开启I2C解码后,地址、数据、ACK一目了然。很多分析仪还能直接标出时序违规,比人眼靠谱。

第六,注意总线的分布电容。走线越长、挂的器件越多、连接器越多,电容越大。如果总线要引出板外,一定要把上拉电阻相应减小,或者加总线缓冲器。我有个项目总线引到排线接了四个模块,4.7kΩ上拉直接跑不动,换成1.5kΩ才稳。

把这些物理层的细节吃透,你会发现后面调协议、调驱动时心里特别有底——因为你知道问题大概率出在哪一层,也知道该拿什么工具去验证。I2C看着简单,但它的简单是建立在物理层精心设计之上的,开漏加线与就是这套设计的灵魂。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询