1. I2C协议基础:物理层、协议层、时序与寻址机制详解

各位同学好,我是老周。做嵌入式这行十几年了,I2C这玩意儿几乎天天见。说实话,很多工程师写了几年I2C驱动,遇到问题还是抓瞎——波形看不懂,时序调不对,从机响应莫名其妙就挂了。这一章,咱们把I2C的底裤扒干净,从物理层一路干到寻址机制。

1.1 物理层:两条线怎么干活?

I2C总线就两条线:SCL(时钟线)和SDA(数据线)。别小看这两根线,里面的门道不少。

核心特征:

  • 开漏输出 + 上拉电阻 —— 这是I2C的灵魂
  • 线与逻辑 —— 多个设备可以同时拉低总线
  • 双向传输 —— SDA和SCL都是双向的

为什么用开漏?我刚开始学的时候也纳闷。你想想看,如果推挽输出,两个设备一个想拉高一个想拉低,那就短路了。开漏就不一样——谁想拉低谁拉低,想拉高就靠上拉电阻。这就是I2C能挂多个设备的根本原因。

参数 标准模式 快速模式 高速模式
最大速率 100 kbps 400 kbps 3.4 Mbps
上拉电阻 4.7kΩ ~ 10kΩ 1.5kΩ ~ 4.7kΩ ~1kΩ
总线电容 ≤ 400 pF ≤ 400 pF ≤ 100 pF
最大从机数 理论127个 理论127个 理论127个

实战经验:上拉电阻不是随便选的。总线电容大了,电阻就得小。我遇到过一块板子,I2C线走了30cm,用了10kΩ上拉,400kHz死活跑不起来。换成2.2kΩ,问题解决。记住:阻值 = 上升时间 / (0.8473 × 总线电容),算一下再选。

1.2 协议层:数据怎么传?

I2C的协议层其实不复杂,但细节多。咱们从最基本的传输单元说起。

1.2.1 起始条件和停止条件

这两个条件定义了传输的边界:

  • 起始条件(S):SCL高电平时,SDA从高到低跳变
  • 停止条件(P):SCL高电平时,SDA从低到高跳变

我当年调试一个传感器,发现它老是丢数据。抓波形一看,起始条件之后SDA没稳定就发数据了。嗯,时序没对齐。记住:起始条件之后,SCL的第一个下降沿才开始传数据位。

1.2.2 数据位传输

每个数据位传输遵循一个简单规则:

  1. SCL低电平时,SDA可以变化
  2. SCL高电平时,SDA必须稳定
  3. 每传输8位数据后,接收方发一个应答位

踩坑提醒:我曾经在从机模式下犯过一个低级错误——在SCL高电平时改变了SDA。主机直接NACK,整包数据重传。从机模式下,SDA的变化必须严格同步在SCL低电平期间。这是新手最容易翻车的地方。

1.2.3 应答机制

应答位(ACK/NACK)是I2C的握手信号:

  • ACK(0):接收方在第9个时钟周期拉低SDA —— 表示"收到,继续"
  • NACK(1):接收方在第9个时钟周期释放SDA —— 表示"收完了"或"出错了"

主机在收到NACK后,通常会发停止条件终止传输。从机也可以用NACK告诉主机"我忙,别发了"。

1.3 时序:波形里藏着什么?

时序是I2C调试的核心。说白了,就是看波形对不对。

I2C 完整传输时序图 SCL SDA S 起始 7位从机地址 R/W ACK 8位数据(MSB → LSB) P 停止 一帧完整传输:起始 + 地址 + R/W + ACK + 数据 + ACK + 停止

这张图我建议你存下来。调试的时候,拿逻辑分析仪抓波形,对着这张图一个一个对。我每次调I2C都是这么干的。

1.4 寻址机制:怎么找到对的设备?

I2C用地址来区分总线上的设备。地址分两种:

1.4.1 7位地址模式

这是最常用的模式。地址范围0x00~0x7F,但有些地址是保留的:

地址范围 用途
0x00 通用呼叫地址(广播)
0x01~0x03 保留
0x04~0x77 可用地址(实际可用112个)
0x78~0x7B 10位地址扩展前缀
0x7C~0x7F 保留

选地址的讲究:我习惯避开0x00和0x7F附近的地址。有些便宜传感器地址冲突严重,比如0x48~0x4F这片,温度传感器扎堆。选地址前先查一下,别跟别的设备撞车。

1.4.2 10位地址模式

当总线上设备超过112个时,用10位地址。传输分两阶段:

  1. 第一阶段:发11110xx + R/W(xx是10位地址的高2位)
  2. 第二阶段:发剩下的8位地址

说实话,我做了十几年嵌入式,10位地址几乎没用过。大部分场景7位地址够用了。但你要知道有这么个东西,万一遇到呢?

1.4.3 寻址流程

主机发一个字节,格式如下:

Bit 7~1: 7位从机地址
Bit 0:   R/W位(0=写,1=读)

示例:主机向地址0x50写数据
发送字节:0xA0 = 1010000 0
          ↑地址0x50  ↑写操作

地址左移的坑:很多数据手册给的地址是7位的,比如0x50。但实际传输时要左移1位,再加上R/W位。我见过有人直接拿0x50当8位地址发,结果从机根本不响应。记住:7位地址左移1位才是传输字节的高7位。

1.5 多主机仲裁

I2C支持多个主机同时发起传输。仲裁机制很简单:谁先拉低SDA谁赢。仲裁失败的自动转为从机模式。

仲裁发生在数据位级别。每个主机在发送数据位的同时,会监测SDA的实际电平。如果发现自己想发1但SDA被拉低了,就知道仲裁失败,立即释放总线。

我一般不建议在多主机模式下做复杂操作。仲裁虽然可靠,但调试起来很麻烦。能用单主机就别折腾。

1.6 本章小结

I2C协议看着简单,但每个细节都可能成为坑。物理层的上拉电阻、协议层的应答机制、时序的建立保持时间、寻址的地址左移——任何一个搞错,总线就罢工。