I2C 为什么必须用开漏输出?从推挽短路、线与,到上拉电阻取值

I2C 为什么必须用开漏输出?从推挽短路、线与,到上拉电阻取值

I2C 只有两根线,却要挂几十个设备,还能应答、拉伸时钟、多主机仲裁。这些能力全部来自同一个设计决定------输出级只准拉低,不准拉高。本文从推挽/开漏的电路结构讲起,说清 bus contention 为什么会烧芯片、三态门明明也能避免冲突为什么 I2C 不选它,最后给出上拉电阻的两个计算边界和实际取值。

1. 先把两种输出级分清楚

左边是推挽(push-pull):PMOS 在上、NMOS 在下,输入高就下管通、输出被强拉到地;输入低就上管通、输出被强拉到 VDD。高低两个方向都是"主动驱动",边沿陡、带载能力强,GPIO 推挽输出、SPI 的 MOSI/MISO 都是它。

右边是开漏(open-drain):只留下管 Q3,上管根本没有。

  • VIN = 1 → NMOS 导通 → VOUT ≈ 0,方向和推挽一样;
  • VIN = 0 → NMOS 截止 → 漏极悬空。

这里是最容易说错的地方:悬空不等于 1 。此刻这个引脚是高阻态,它对总线不提供任何电流,电平完全由外部决定。所以开漏输出必须配一个上拉电阻 Rp 到 VDD------"1" 是 Rp 给的,不是这个引脚给的。也正因为如此,开漏的下降沿由 NMOS 的导通电阻(几十 Ω)决定,很快;上升沿由 Rp × 总线电容 的充电过程决定,很慢。这个不对称是第 5 节的全部内容。

2. 推挽挂总线上会发生什么:bus contention

I2C 是多设备共享同一根 SDA/SCL。如果每个设备都用推挽输出,那么只要出现"A 出高、B 出低"这一瞬间,电流路径就是 VDD → Q2(PMOS) → 走线 → Q3(NMOS) → GND------整条回路上只有两个 MOS 的导通电阻,几百 mA 的电流直接灌过去。

后果不是"数据错一下"这么简单:轻则 VOH/VOL 被分压抬到判决门限附近造成误码、引脚发烫,重则直接烧毁输出级。而且这是概率事件------两个主机同时发起传输、从机在主机以为总线已空闲时回 ACK,都可能撞上。

推挽要安全共享总线,前提是"任何时刻只有一个设备在驱动"。这个前提在 I2C 上没法保证,因为 I2C 没有片选线,也不允许主机单方面决定什么时候结束。

3. 三态门也能避免冲突,为什么 I2C 还是选开漏?

!外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=figs%2Ffig3_tristate.png\&pos_id=img-OY6mvnpg-1793518175801

三态输出(高 / 低 / 高阻 Z)是解决共享总线的另一条路:不驱动时把两个 buffer 都 disable,引脚呈高阻,等于从总线上"断开"。SPI 就是这么干的------靠片选 CS 保证同一时刻只有一个从机驱动 MISO。

那 I2C 为什么不用三态,而用更"弱"的开漏?两个原因,这也是很多人学完这一章仍然没打通的一环:

第一,三态需要额外的协调信号。 谁被允许驱动,必须由 CS 之类的边带信号精确控制,时序一乱就还是冲突。I2C 只有两根线,不想再加第三根。开漏则从物理上取消了"主动拉高"这个能力,冲突不可能发生,不需要任何协调。

第二,开漏 + 上拉天然实现"线与"(wired-AND):任何设备拉低,总线就是低;所有设备都释放,总线才是高。I2C 的三件事全都建立在这上面,而三态方案一件也做不出来:

机制 怎么用线与实现
ACK / NACK 第 9 个时钟由接收方决定是否把 SDA 拉低,主机只需释放总线去读,双方不需要交换驱动权
时钟拉伸 clock stretching 从机不响应就按住 SCL 不放(SCL 也是开漏+上拉),主机自动等待,不需要握手协议
多主机仲裁 arbitration 大家同时发,谁发出高电平却读回低电平,就说明自己撞上了,立刻退出------边发边比,数据不丢

顺带一提,多设备共用一根中断线(INT)也是同一个道理:开漏 + 一个公共上拉,任何一个器件都能拉低报警,且不会互相短路。

4. 代价:上升沿是 RC 充电,慢

上升过程就是 Rp 给总线电容 Cb 充电,NXP 规范里取 10%→90% 的上升时间:

复制代码
tr = 0.8473 × Rp × Cb      →      Rp(max) = tr / (0.8473 × Cb)

Cb 包括走线、过孔和所有器件引脚的寄生电容,单块短走线的板子大约 50~200 pF,规范上限是 400 pF(Fast-mode)/ 550 pF(Fm+)。

注意:不是"不关心上升沿",而是"允许它慢"。 规范对 tr 有硬上限:标准模式 ≤1000 ns、快速模式 ≤300 ns、Fm+ ≤120 ns。之所以慢一点还能正常通信,是因为接收端在 SCL 为高的那半个周期里采样 SDA(见第 6 节图),只要采样窗口到来之前电平已经爬到判决门限之上就行,它根本不看边沿有多陡。下降沿是强下拉,几乎瞬时,所以 I2C 波形天生"下降快、上升慢",示波器上看到指数爬坡是正常现象,不是故障。

(另外提醒一句:外设中断是不是电平触发,和总线时序是两回事,不能拿来解释"为什么可以容忍慢边沿"。)

5. 上拉电阻到底怎么选:两个边界

下限看灌电流 ------保证输出低电平时 VOL ≤ 0.4 V,规范按 IOL = 3 mA 考核:

复制代码
Rp(min) = (VDD − VOL) / IOL
3.3 V:(3.3 − 0.4) / 3 mA ≈ 970 Ω
5 V  :(5 − 0.4) / 3 mA ≈ 1.53 kΩ

上限看上升时间------就是第 4 节那个式子。几个典型组合:

场景 Cb 模式(tr 上限) Rp(max) 实际取值
单板上 2~3 个器件,3.3 V 100 pF Fm 300 ns ≈ 3.5 kΩ 2.2 kΩ
单板上几个器件,3.3 V 50 pF Fm 300 ns ≈ 7.1 kΩ 4.7 kΩ 也行
短线,5 V,只跑 100 kHz 200 pF Sm 1000 ns ≈ 5.9 kΩ 4.7 kΩ
长总线 / 多器件,跑 400 kHz 400 pF Fm 300 ns ≈ 885 Ω 1 kΩ 都不够 → 降速,或加 I2C 总线缓冲器

所以"3.3 V 用 2.2 k/3 k、5 V 用 4.7 k"这组经验值是对的,但要清楚它的适用条件:板级、短走线、器件不多。一旦总线拉长、器件变多还要保持 400 kHz,靠改小 Rp 会撞到 3 mA 的灌电流上限,这时候该上 PCA951x / TCA9617 这类总线缓冲器做分段驱动,而不是硬压电阻。

还有两个容易踩的坑:上拉接谁的电源,就决定了这条总线的电平标准,3.3 V 器件挂到 5 V 上拉的总线上可能烧输入级;两个上拉电阻并联(板端一个、模块端一个)会让实际阻值减半,静态电流翻倍,扩展板上的上拉要么断开、要么确认不会和主板上拉冲突。

6. 一帧波形长什么样

规则只有三条,全部可以从"线与 + 采样窗口"推出来:

  1. 起始 S:SCL 为高期间,SDA 由高跳低;
  2. 停止 P:SCL 为高期间,SDA 由低跳高;
  3. 数据只在 SCL 为低时变化,SCL 为高期间 SDA 必须稳定------那半个周期就是对方的采样窗口。

一帧里第一个字节是 7 位从机地址(bit7→bit1,MSB 先行)+ 1 位方向(0 写 / 1 读),第 9 位是接收方拉的 ACK;之后每 8 位数据跟一个 ACK。地址位数的坑也在这:datasheet 写 0x50 是 7 位地址,某些库要传 0xA0 是已经左移一位并把 R/W 补成 0,读写函数对不上时先检查这个。

7. 三句话记住这一章

  1. 开漏只会拉低,"1" 由上拉电阻给,所以输入低电平时引脚是高阻而不是高电平;
  2. 用开漏不是为了省电或省事,是为了物理上消灭 bus contention ,并顺带得到线与------ACK、时钟拉伸、多主机仲裁全从这里长出来;
  3. 上拉电阻有两个边界:下限由 3 mA / 0.4 V 定,上限由 tr ≤ 0.8473 × Rp × Cb 定,经验值 2.2 k/4.7 k 只在短总线小电容下成立。

参考

  • NXP, I2C-bus specification and user manual (UM10204 Rev.4) --- tr 上限、400/550 pF、Rp(max) = tr / (0.8473 × Cb) 的出处
  • TI, I2C Bus Pullup Resistor Calculation (SLVA689) --- 上下限计算过程

本文是自己学 I2C 时的整理,。有表述不准的地方欢迎在评论区拍砖

相关推荐
无敌贵点大王1 小时前
从寄存器层面重学STM32(5)——串口USART理论部分
stm32·单片机·嵌入式硬件
女神下凡1 小时前
芯参谋(83):AliOS Things_驱动开发指南
arm开发·驱动开发·单片机·嵌入式硬件·设计规范·1024程序员节
女神下凡2 小时前
芯参谋(84):Linux eMMC 驱动开发指南
linux·运维·arm开发·驱动开发·单片机·嵌入式硬件
szxinmai主板定制专家13 小时前
X86 工控机 vs RK3588 异构主控:AMR 控制器 BOM、散热、长期运维对比
运维·stm32·单片机·嵌入式硬件·fpga开发·am62x
HQB202616 小时前
屹晶微电子 EG1130|2‑24V 输入 / 最高 28V 输出 内置 MOS 异步升压 DC‑DC|SOT23‑6|摄像头 / 车充 / 电池充电电源
嵌入式硬件·硬件工程
泛凡(Linyongui)17 小时前
基于 CW32L010 的智能厨卫调温旋钮方案--低成本 EC21 编码旋钮全解析
单片机·嵌入式硬件·c#
十年磨一剑~18 小时前
Linux 内核中断处理机制与软硬件协同流程
linux·嵌入式硬件
糖糖单片机设计19 小时前
基于单片机的热水器节能控制系统
单片机·嵌入式硬件
数字新视界19 小时前
数字孪生技术赋能智能化转型与创新实践
嵌入式硬件·物联网·系统架构·机房管理·动力与环境监控系统