I2C 主机设计:开漏线与、START/STOP/ACK、时钟拉伸与仲裁
更新时间:2026-09-19。本文配合 GPIO 开漏 理解为什么 I2C 必须开漏。寄存器接口通常挂 APB。I2C 慢、引脚少,适合挂 EEPROM、传感器、codec 等一堆配置型器件。
本文要回答的问题
- 为什么 SCL/SDA 必须是开漏 + 外部上拉?
- START/STOP/数据位/ACK 在波形上怎么定义?
- 一帧地址 + 读写怎么走,NACK 表示什么?
- 时钟拉伸和多主机仲裁是怎么实现的?
- 从机拉住 SDA 导致总线挂死怎么恢复?
一、物理层:开漏 + 上拉 = 天然线与
SCL、SDA 都只能被设备拉低或释放为高阻,高电平由外部上拉电阻提供。因此:
- 多个设备同时驱动不会短路对顶,总线电平 = 所有设备输出的「线与」;
- 谁拉低就是 0;只有大家都释放才是 1。
这同时是仲裁的物理基础。RTL 上输出侧只产生「输出使能(拉低)」,永远不主动驱高,见 GPIO 开漏代码。上拉电阻 + 总线电容决定上升沿是 RC 缓沿,速率越快要求阻值越小/电容越低。
二、位级时序:START、STOP、数据、ACK
空闲:SCL=1、SDA=1。所有变化都以 SCL 为参照:
- START(S):SCL=高电平期间,SDA 由高→低。
- STOP(P):SCL=高电平期间,SDA 由低→高(恢复空闲)。
- 数据位:SCL=低电平期间允许改变 SDA;SCL=高电平期间 SDA 必须稳定(接收器在 SCL 高时采样)。在 SDA 高电平期间变化就会被误认为 START/STOP——这是最常见的协议错误。
- ACK:每传完 8 位(1 字节,MSB 先发),发送方释放 SDA,接收方在第 9 个时钟把 SDA 拉低表示 ACK;保持高为 NACK。
SCL ┐┌─┐┌─┐┌─┐ ... 8 data bits ┐┌─┐(第9拍)
SDA: └START(高时SDA下降) bit7..bit0 ACK(接收方拉低)三、一帧:地址 + R/W + 数据
START 后第一字节高 7 位是从机地址、最低位是 R/W(0=写,1=读):
- 主机发 S + 地址字节;被寻址从机回 ACK。
- 写:主机逐字节发寄存器指针/数据,每个字节从机 ACK;主机以 P(或重复 START)结束。
- 读:通常先写寄存器偏移,再发 repeated START + 地址(R=1),然后从机驱动数据、主机逐字节 ACK;主机想结束时,对最后一字节回 NACK 再发 STOP(告诉从机别再发)。
10 位地址模式用保留前缀 11110xx 分两字节传地址;广播地址 0x00 为 general call。
四、时钟拉伸:从机让主机等
从机如果来不及处理,可以在某一位(常见于 ACK 后)拉住 SCL 为低。主机在释放 SCL 后必须先回读 SCL 确认它真的变高才能继续;只要从机拉着,主机就等。这就是 clock stretching——一种由从机发起的硬件流控。RTL 不能假设 SCL 是纯输出,必须把 SCL 当输入回采(开漏本就双向),否则会忽略拉伸、采样错位。
五、多主机仲裁:边发边听
多个主机同时 START 时,靠 SDA 线与逐位仲裁:每个主机在 SCL 高时回读 SDA,和自己发出的位比较。
- 自己发 1(释放)但采到 0:说明别人拉了 0,自己输掉仲裁,立即停止驱动、转为接收/等待,整套过程不丢数据(赢者的电平本来就在总线上)。
- SCL 也可在不同速率主机间被时钟同步(各设备拉低时长取最长)。
仲裁只发生在 SDA(地址/数据);SCL 的线与让最慢者决定时钟。单主机系统可省略仲裁逻辑,但开漏回读仍需保留以支持时钟拉伸。
六、速率与电气模式
- Standard 100 kHz、Fast 400 kHz、Fast-mode Plus 1 MHz、High-speed 3.4 MHz(HS 有特殊切换)。
- 上拉阻值/总线电容、设备输入电容需按模式核算;电平不匹配加电平转换器(见 电平转换)。
- SMBus/PMBus 是 I2C 子集,带超时、告警等更严格要求。
七、总线挂死与恢复
经典故障:主机在读取中途复位,从机还在等时钟、甚至拉住 SDA(以为自己要继续发数据),总线无法回到空闲,后续 START 发不出去。恢复办法:主机在保持 SDA 可操作的前提下,手动产生最多 9 个 SCL 时钟脉冲,让卡住的从机把剩余位移出、释放 SDA,再发 STOP 把总线复位到空闲;必要时给从机硬复位/电源循环。控制器设计应提供「手动时钟/SDA 控制」与总线 busy/超时检测。
八、常见的坑
- 用推挽驱 SDA/SCL:多设备对顶,破坏线与与仲裁,可能损坏引脚。
- SCL 高电平时改 SDA 数据:被当 START/STOP,帧错乱。
- 不回读 SCL:忽略时钟拉伸,在慢从机上采样错。
- 最后读字节回 ACK:从机继续驱动,主机无法 STOP;末字节必须 NACK。
- 上拉阻值不当:阻值过大上升沿太慢(高频误码),过小功耗大、拉不低。
- 复位后无总线恢复:SDA 被从机拉住永久 busy;需 9 脉冲恢复序列。
- START 毛刺:SDA 受干扰在 SCL 高时产生下降沿,误触发;布线/滤波与控制器毛刺抑制要考虑。
相关与延伸
开漏电气见 GPIO 设计、电平转换 IO;寄存器接口见 APB;对比同步全双工的 SPI 主机、异步的 UART;选型见 AMBA 总线选型。
总结
I2C 用 SCL/SDA 两根开漏+上拉线实现线与:SCL 高时 SDA 下降为 START、上升为 STOP、数据在 SCL 低时改高时稳,每 8 位后第 9 拍 ACK;开漏让从机能时钟拉伸、多主能边发边听逐位仲裁而不丢数据;读取末字节要 NACK 再 STOP,总线被拉死时用最多 9 个时钟脉冲恢复。