2、从机模式硬件架构:开漏输出、SCL/SDA线、内部状态机

各位同学,今天我们来聊聊I2C从机模式的硬件基础。说实话,很多工程师写I2C驱动时只盯着寄存器,却忽略了物理层是怎么工作的。结果呢?遇到总线锁死、应答异常、时钟拉伸失败,就完全抓瞎了。

我个人习惯,搞懂一个协议,先看它的硬件架构。I2C从机看似简单,就两根线加一个状态机,但里面的门道不少。咱们一个一个拆开讲。

2.1 开漏输出:为什么非它不可?

I2C总线的输出级,用的是开漏(Open-Drain)结构。什么意思呢?说白了,就是MOS管的漏极直接接到引脚上,源极接地。输出高电平时,管子关断,引脚靠外部上拉电阻拉到VDD;输出低电平时,管子导通,引脚被拉到GND。

你可能会问:为什么不用推挽输出?推挽不是更快吗?

嗯,这里有个关键原因——多主机仲裁。I2C允许总线上挂多个主机,如果两个主机同时拉低SCL,推挽输出会直接短路,烧毁芯片。开漏结构天然支持“线与”:任何一个设备拉低,总线就是低电平。仲裁时,谁先释放总线,谁就输了。这个机制,推挽做不到。

核心要点:开漏输出 + 外部上拉电阻 = I2C总线的基础。上拉电阻的阻值选择很关键:太小则功耗大、边沿陡峭;太大则上升沿变缓,限制通信速率。我一般4.7kΩ起步,高速模式用2.2kΩ。

我在项目中遇到过一件事:某次调试,从机偶尔不响应,用示波器一看,SCL上升沿慢得像蜗牛。查了半天,发现板子上拉电阻用了100kΩ。换回4.7kΩ,问题立刻消失。所以,上拉电阻别乱选。

2.2 SCL/SDA线:时序与电平的博弈

SCL(时钟线)和SDA(数据线)是I2C的命脉。从机必须在这两根线上精确配合主机的时序。

先看几个关键时序参数:

参数 符号 标准模式 (100kHz) 快速模式 (400kHz)
SCL时钟频率 fSCL 100 kHz 400 kHz
保持时间(起始条件后) tHD;STA 4.0 μs 0.6 μs
数据建立时间 tSU;DAT 250 ns 100 ns
数据保持时间 tHD;DAT 0 ~ 3.45 μs 0 ~ 0.9 μs
SCL低电平时间 tLOW 4.7 μs 1.3 μs
SCL高电平时间 tHIGH 4.0 μs 0.6 μs

从机设计时,必须满足这些时序。尤其是数据建立时间和保持时间。我曾经调试一个从机,发现它在快速模式下偶尔丢数据。用逻辑分析仪抓波形,发现SDA在SCL下降沿之后才稳定——建立时间不够。后来在FPGA里加了一级寄存器延迟,才搞定。

调试技巧:用示波器同时抓SCL和SDA,触发在SCL下降沿。看SDA在下降沿前后是否稳定。如果SDA在下降沿附近跳变,说明建立或保持时间违规。这是最常见的I2C从机问题之一。

2.3 内部状态机:从机的“大脑”

I2C从机内部,本质上是一个有限状态机(FSM)。它根据SCL和SDA上的电平变化,在不同状态间跳转。

典型的从机状态机包含以下状态:

  • IDLE(空闲):等待起始条件。总线空闲时,SCL和SDA都是高电平。
  • ADDR(地址匹配):检测到起始条件后,接收7位或10位地址。如果匹配自己的地址,进入下一状态;否则回到IDLE。
  • ACK(应答):地址匹配后,在第9个SCL周期拉低SDA,发送应答信号。
  • DATA_RX(数据接收):主机写数据时,从机逐字节接收,每字节后发送应答。
  • DATA_TX(数据发送):主机读数据时,从机逐字节发送,每字节后等待主机应答。
  • STOP(停止):检测到停止条件,回到IDLE。

我画了一张状态机图,方便大家理解:

I2C从机内部状态机 I2C从机内部状态机 IDLE ADDR ACK DATA_RX DATA_TX STOP 起始条件 地址匹配 R/W=0 (写) R/W=1 (读) 每字节后应答 等待主机应答 停止条件 停止条件 回到空闲 状态机要点 • 每个状态由SCL边沿触发 • 地址匹配后决定读写方向 • 停止条件强制回到IDLE

状态机的实现,通常用硬件描述语言(Verilog/VHDL)或MCU的硬件外设。我个人更推荐用硬件实现,因为响应速度快,不受软件中断延迟影响。

避坑指南:我曾经在一个项目中,用GPIO模拟I2C从机状态机。结果主机时钟一快,软件来不及响应,状态机就乱了。后来改用硬件I2C外设,问题才解决。所以,如果通信速率超过100kHz,别用软件模拟,老老实实用硬件外设。

2.4 时钟拉伸:从机的“刹车”

I2C协议允许从机在需要时拉低SCL,迫使主机等待。这叫时钟拉伸(Clock Stretching)。从机在数据接收或发送前,如果没准备好,可以拉低SCL,告诉主机“慢点,我还没处理好”。

时钟拉伸的典型场景:

  • 数据接收后处理:从机收到一个字节,需要时间存入缓冲区或处理中断。
  • 数据发送前准备:从机需要从内存读取数据,才能放到SDA上。
  • 内部状态切换:从机状态机需要额外时钟周期完成跳转。

不过要注意,不是所有主机都支持时钟拉伸。有些主机的SCL驱动能力很强,从机拉不动。我遇到过一款MCU做主机,它的SCL引脚是推挽输出,从机根本拉不低。最后只能降低通信速率,或者让从机提前准备好数据。

经验之谈:设计从机时,尽量避免依赖时钟拉伸。如果必须用,确保主机支持,并且从机的拉低时间不超过主机超时阈值(通常10ms)。否则主机可能认为总线锁死,触发复位。

2.5 硬件设计检查清单

最后,我整理了一份从机硬件设计的检查清单,供大家参考:

  1. 上拉电阻:阻值是否合适?标准模式4.7kΩ,快速模式2.2kΩ。
  2. 引脚配置:SCL和SDA是否配置为开漏输出?内部上拉是否禁用?
  3. 电平匹配:从机VDD是否与主机一致?如果不一致,需要电平转换。
  4. 滤波电容:SCL和SDA上是否加了小电容(通常10pF)抑制噪声?
  5. 状态机验证:是否覆盖了所有异常情况?比如非应答、总线冲突、起始条件重复?
  6. 时钟拉伸:如果使用,是否测试过主机兼容性?