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 kΩ 到 10 kΩ)在无人拉低时把线拉高。线与(wired-OR)行为由此而来——任何把线拉低的设备都会取胜,而高电平状态只是表示“没有人在说话”。
MCU 在其 SCL 和 SDA 引脚上的内部上拉通常不够强,无法单独充当总线上拉;通常需要在总线上加外部电阻。许多传感器扩展板已经自带了它们;在添加更多之前请查阅数据手册。
3.23.2. 事务¶
每一次 I2C 事务都遵循相同的形态:
一次 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 位地址空间涵盖 0x08 至 0x77;两端的值保留作特殊用途。每个设备的地址由芯片设计者设定;许多器件允许在板级层面更改低位中的若干位(通过把某个引脚接高或接低),以便两个相同的传感器能挂在同一总线上。
如果两个设备共用一个地址,就无法在不受另一个干扰的情况下与其中之一通信,所以在搭配器件前请查阅数据手册。i2c.scan()(在 I2C 代码实践 中介绍)会遍历地址空间并报告哪些地址有响应,这是查清总线上有什么的标准方法。
3.23.4. 优点和缺点¶
该总线的优点和缺点决定了它的定位:
两个引脚连接多个设备。单对 SCL/SDA 就能承载十几个传感器。SPI 则需要为每个设备增加一根 CS 引脚。
标准速度。
100 kHz(“标准模式”)和400 kHz(“快速模式”)几乎覆盖所有传感器。1 MHz是可达到的,但开始对总线电容和上拉取值提出更高要求。相对 SPI 较慢。任何传输速率超过几百千比特每秒的应用都应改用 SPI。
地址冲突。同一总线上两个地址相同的设备是协议无法绕过的硬件错误。