第1章:I2C协议基础

各位同学好,我是老张。做光模块固件开发十几年了,今天咱们来聊聊I2C协议。说实话,这玩意儿看着简单,但坑是真不少。我见过太多工程师在I2C上翻车,所以这一章咱们把基础打扎实。

1.1 I2C总线物理层

I2C总线就两条线:SCL(时钟线)和SDA(数据线)。别小看这两根线,它们承载了SFP模块里所有的控制通信。

物理层有几个关键点:

  • 开漏输出:SCL和SDA都是开漏结构。这意味着它们只能拉低,不能主动拉高。高电平靠上拉电阻实现。
  • 上拉电阻:一般选4.7kΩ左右。我做过一个项目,上拉电阻用了10kΩ,结果通信距离长了信号就变形了。后来换成2.2kΩ才稳定。
  • 总线电容:每根线的电容负载有限制,通常不超过400pF。SFP模块里走线短,问题不大。

重要提醒:SFP模块的I2C总线电压通常是3.3V,但有些老模块是5V的。连接前一定要确认电平匹配,否则可能烧芯片。

1.2 起始条件和停止条件

I2C通信的开始和结束,靠的是SCL和SDA的时序配合。

起始条件(S):SCL高电平时,SDA从高变低。

停止条件(P):SCL高电平时,SDA从低变高。

嗯,这里要注意:起始和停止条件都是由主机产生的。从机只能被动响应。

我的经验:调试时我习惯用逻辑分析仪抓起始条件。有一次发现模块不响应,抓波形一看,起始条件里SDA下降沿不够陡,原来是上拉电阻太大导致边沿变缓了。

1.3 数据帧格式

I2C的数据帧结构很规整。每个字节8位,高位在前。传输完一个字节后,从机要回复一个应答位。

标准的数据帧格式如下:

| 起始 | 7位地址 | R/W | 应答 | 8位数据 | 应答 | ... | 停止 |

举个例子,读取SFP模块的A0h地址(即0x50左移1位):

主机发送:S 0xA0 W A [寄存器地址] A
主机发送:S 0xA1 R A [读取数据] N P

这里S是起始条件,P是停止条件,A是应答,N是非应答。

1.4 地址与读写方向位

I2C设备地址是7位的,但实际发送时是8位。为什么?因为最低位被用来表示读写方向了。

方向位 含义 示例(地址0x50)
0(写) 主机向从机写数据 0xA0
1(读) 主机从从机读数据 0xA1

SFP模块里,常用的I2C地址是0x50(A0h写/A1h读)和0x51(A2h写/A3h读)。前者存数字诊断信息,后者存厂商信息。

避坑指南:我曾经在项目里把地址搞反了。写地址用了0xA1,读地址用了0xA0,结果模块死活不响应。查了半天才发现是地址搞混了。记住:写地址是偶数,读地址是奇数。

1.5 应答机制

应答机制是I2C可靠性的保障。每传输完一个字节,接收方要拉低SDA表示应答。

  • 应答(ACK):SDA在第9个时钟周期被拉低。表示「我收到了,继续发」。
  • 非应答(NACK):SDA在第9个时钟周期保持高。表示「我不想收了」或「出错了」。

读操作时,主机在最后一个字节发送NACK,然后发停止条件。这是告诉从机:「够了,别发了」。

关键点:应答信号是由接收方产生的。主机发送地址后,要释放SDA总线,等待从机拉低应答。如果从机没应答,说明地址不对或设备没准备好。

1.6 知识体系总览

下面这张图把I2C协议的核心逻辑串起来了。你看一遍就能明白整个通信流程。

I2C协议核心知识体系 物理层 SCL时钟线 SDA数据线 开漏输出 + 上拉电阻 时序控制 起始条件 S 停止条件 P SCL高电平采样 数据帧格式 7位地址 + R/W 8位数据 高位在前 地址与方向 7位地址 + 1位方向 写=0 / 读=1 应答机制 ACK = 拉低SDA NACK = 保持高电平 通信流程 S → 地址+R/W → ACK 数据 → ACK → ... → P I2C通信 = 物理层基础 + 时序控制 + 数据帧格式 + 地址方向 + 应答确认 SFP模块常用地址:0x50(诊断信息)和 0x51(厂商信息)

1.7 实战要点总结

说了这么多,我给大家总结几个实战中必须记住的点:

  1. 上拉电阻别乱选:SFP模块里建议用4.7kΩ,别图省事用10kΩ。
  2. 地址别搞混:写地址是偶数,读地址是奇数。0x50对应写0xA0、读0xA1。
  3. 应答必须检查:每次发送地址后,一定要检查从机是否回复ACK。没应答就重试。
  4. 停止条件要完整:通信结束一定要发停止条件,否则从机会一直等。
  5. 时序别太急:I2C标准模式100kHz,快速模式400kHz。SFP模块一般用100kHz,稳定第一。

个人习惯:我写I2C驱动时,喜欢在每次操作前后加个小延时(比如10μs)。虽然标准里没要求,但实际测试发现,加了这个延时后,兼容性好了很多。特别是跟不同厂家的SFP模块通信时,这个技巧帮我省了不少调试时间。

好了,I2C协议基础就讲到这里。这些内容看着简单,但都是后面写代码的基础。下一章咱们开始动手写代码,到时候你就知道这些基础有多重要了。


公众号:蓝海资料掘金营,微信deep3321