3.23. I2C 基础

I2C(Inter-Integrated Circuit,读作“I-squared-C”或“I-two-C”)是一种两线串行总线,专为同一块板上芯片之间的短距离连接而设计。它在优先级上介于 SPI 和 UART 之间:比 SPI 慢但更节省引脚,并且支持寻址(同一两根线上可挂多个设备),而 SPI 则需要为每个设备配一根专用的 CS 线。

I2C 是低速率传感器的首选总线——加速度计、温度传感器、湿度传感器、磁力计、实时时钟、EEPROM——在这些场合,节省引脚和降低总线复杂度比原始吞吐量更重要。

3.23.1. 两根线,皆为开漏

一条 I2C 总线只有两个信号:

  • SCL(串行时钟)。(大多数时候)由控制器驱动。

  • SDA(串行数据)。由当前正在发言的设备驱动——地址和发出数据期间由控制器驱动,读取和 ACK 位期间由外设驱动。

两条线都是开漏的:总线上的每个设备都可以把线拉低到地,但永远不会把它驱动到高电平。总线上的两个上拉电阻(通常为接到供电轨的 2.2 10 )在无人拉低时把线拉高。线与(wired-OR)行为由此而来——任何把线拉低的设备都会取胜,而高电平状态只是表示“没有人在说话”。

MCU 在其 SCL 和 SDA 引脚上的内部上拉通常不够强,无法单独充当总线上拉;通常需要在总线上加外部电阻。许多传感器扩展板已经自带了它们;在添加更多之前请查阅数据手册。

3.23.2. 事务

每一次 I2C 事务都遵循相同的形态:

SCL 和 SDA 波形。SCL 为高时 SDA 下降 (START),然后 SCL 在 SDA 上为字节计时——一个带读/写位和 ACK 的 7 位 地址字节, 接着是一个寄存器字节和一个 ACK,然后是一个数据字节 和一个 NACK,最后 SCL 为高时 SDA 上升(STOP)。

一次 I2C 事务:START、7 位地址 + R/W、ACK、寄存器、ACK、数据、NACK、STOP。

这次交换逐位展开:

  • START。控制器在 SCL 仍为高时把 SDA 拉低。这个不寻常的边沿告知总线上的每个设备:一次事务即将开始。

  • 地址 + R/W。控制器时钟输出一个 7 位外设地址,后跟一个读/写位(0 表示写,1 表示读)。

  • ACK / NACK。在每个字节之后,接收方驱动 SDA 一个时钟周期以发出 ACK(低)或 NACK(高)。在地址字节上,外设若识别出自己的地址则发出 ACK;如果没有设备发出 ACK,控制器会看到 NACK,从而得知该地址不在总线上。

  • 数据字节。每个字节之后都跟着接收方发出的 ACK。在写操作中,外设对每个字节发出 ACK;在读操作中,控制器对每个它还想要更多的字节发出 ACK,并对最后一个字节发出 NACK,以告诉外设停止。

  • STOP。控制器在 SCL 为高时把 SDA 释放为高电平,结束事务。

重复起始(repeated start)是在中间不发 STOP 的情况下发出的第二个 START——控制器在同一外设上切换方向(写地址,然后读地址),而不放弃总线。

3.23.3. 寻址

7 位地址空间涵盖 0x080x77;两端的值保留作特殊用途。每个设备的地址由芯片设计者设定;许多器件允许在板级层面更改低位中的若干位(通过把某个引脚接高或接低),以便两个相同的传感器能挂在同一总线上。

如果两个设备共用一个地址,就无法在不受另一个干扰的情况下与其中之一通信,所以在搭配器件前请查阅数据手册。i2c.scan()(在 I2C 代码实践 中介绍)会遍历地址空间并报告哪些地址有响应,这是查清总线上有什么的标准方法。

3.23.4. 优点和缺点

该总线的优点和缺点决定了它的定位:

  • 两个引脚连接多个设备。单对 SCL/SDA 就能承载十几个传感器。SPI 则需要为每个设备增加一根 CS 引脚。

  • 标准速度。100 kHz(“标准模式”)和 400 kHz(“快速模式”)几乎覆盖所有传感器。1 MHz 是可达到的,但开始对总线电容和上拉取值提出更高要求。

  • 相对 SPI 较慢。任何传输速率超过几百千比特每秒的应用都应改用 SPI。

  • 地址冲突。同一总线上两个地址相同的设备是协议无法绕过的硬件错误。