01 先说概念
I2C 英文全称 Inter-Integrated Circuit,字面意思是集成电路之间,也就是我们常说的 I 方 C 总线—I2C bus。它是一种串行通讯总线,使用多主从架构,由飞利浦公司(恩智浦 NXP 的母公司)在 80 年代开发,用于主板、嵌入式系统连接周边低速设备。
I2C 由两条双向开漏线组成,这是一个很大的优势,接线简单。两条线利用上拉电阻将电位上拉。典型电位为 +3.3V 或 +5V。标准传输速率为 100Kb/s,低速模式 10Kb/s。
02 物理层
下图为 I2C 总线的物理拓扑图,大家可以看到一共只有两条总线,一条 SDA(serial data)数据线用来承载数据、一条 SCL(serial clock line)时钟线用来控制数据收发时序。所有 I2C 设备的 SDA 都接到了总线的 SDA 上,SCL 都接到了总线的 SCL 上。每个设备都有自己的唯一地址,以保证设备之间访问的准确性。
I2C 在物理层的连接可以说是非常简单,这也是它最大的优势,原理就是通过控制 SDA 和 SCL 线的高低电平时序,来产生 I2C 总线协议所需要的信号进行数据传输。在总线处于空闲状态时 SCL 和 SDA 被上拉电阻拉高,保持高电平。
需要注意的是 I2C 的通讯方式为半双工,因为只有一条数据线,某一时刻只可能单向通讯。这也说明了 I2C 不适合大数据量的传输应用。
对于主机、从机的区分很简单,发布主要命令的就是主机,接受命令的就是从机,同一条 I2C 总线允许多个主机的存在。
03 协议层
作为基础我们先来了解几个重要的小概念。
1、初始状态 (即空闲状态):SDA 与 SCL 均为上拉电阻所致的高电平时为初始状态;
2、 开始信号 :当 SCL 为高电平的时候,SDA 被拉低,此为开始信号,表明通讯开始。
3、 终止信号:当 SCL 为高电平的时候,SDA 被拉高,此为终止信号,表明本次通讯结束。
到这里大家有没有发现点什么?当 SCL 处于高电平的时候,SDA 电平一旦变化就会是一种信号,要么开始要么是终止。所以在 数据传输过程中,SCL 处于高电平时,SDA 必须保持状态稳定,只有 SCL 处于低电平时 SDA 才可以变化。
4、应答信号:当发送器向接收器发送完一个字节 / 8 位数据后,第 9 个时钟周期内,接收器必须给发送器一个应答信号,这样数据才算传输成功。高电平表非应答,低电平表应答。
我们了解这几个信号状态后,来一步一步看看数据是如何传输的。
1、向从机设备的某一个寄存器写一个字节数据:开始信号 + 设备地址(7 位)+ 读 / 写(1 位)+ 等待从机应答 + 寄存器地址(8 位)+ 等待从机应答 + 要写的数据(8 位)+ 等待从机应答 + 终止信号。下图为 24C02 EEPROM 存储器写数据的时序图。
2、写我们见识了,那读一个试试:下图为读取 24C02 当前地址一个字节数据的时序图,是不是一目了然了。值得注意的是当读的时候地址 7 位后的读写状态位为 1。这里说一下为什么最后是 NO ACK,在“读”这个操作下,主机为接收器,主机的 NO ACK 表示停止接收 24C02 的数据,不然 24C02 会继续发。
3、我们再读一个长一点的:下图为读取 24C02 任意地址一个字节数据的时序图。开始信号 + 设备地址 (7 位)+ 写(1 位)+ 等待从机应答 + 数据地址(8 位)+ 等待从机应答。前面这一步为假写,目的是告诉 24C02 要读哪个地址的数据。继续,开始信号 + 设备地址(7 位)+ 读(1 位)+ 等待从机应答 + 读到的数据(8 位)+ 等待主机(接收机) 应答 + 终止信号。
04 补点干货
1、设备的地址。I2C 设备的地址为 8 位,但是时序操作时最后一位不属于地址,而是读 or 写状态位。这就是为什么 arduino 的 SH1106 库里操作的地址不是 0x7- 而是 0x3-,因为有用的是前 7 位,地址整体右移一位处理了。再一个设备地址的前四位是固定死的,是厂家用来表示设备类型的,比如接口为 I2C 的温度传感器类设备地址前四位一般为 1001 即 9X、EEPROM 存储器地址前四位一般为 1010 即 AX、oled 屏地址前四位一般为 0111 即 7X 等等。
2、I2C 接口的致命缺点就是传输距离近同时速度慢 。 大家在使用 I2C 总线接口的时候切记不要长线传输,尽量只在 PCB 板内传输,不然偶尔丢数据甚至读不到数据会让人崩溃,不要问我是怎么知道的,问只有眼泪。
3、关于两线为什么设计成开漏,这个问题我记得我之前在写《STM32 单片机 I / O 的 8 种工作模式》时给大家埋下过伏笔。今天就来说一下具体原因。主要有两点①防止短路:大家想想如果不设为开漏,而设为推挽,几个设备连在同一条总线上,这时某一设备的某个 IO 输出高电平,另有一台设备的某一个 IO 输出低电平,这时你会发现这两个 IO 的 VCC 和 GND 短路了;但是开漏就不会有这个问题,如下图示:
第二个原因是“线与”,我们想个场景:如果总线上的一个 A 设备将 SDA 拉高,这时总线上另一个 B 设备已将 SDA 拉低,这时由于 1 &0=0,所以 A 设备检查 SDA 的时候会发现不是高电平而是低电平,这就表明总线上已经有其他设备占用总线了,A 只好放弃,如果检测是高电平那就可以使用。如下图示为 24C02 芯片内部图,可以看到状态检测脚。
05 总结
I2C 总线作为一个常见的总线协议,是非常值得我们来仔细研究琢磨的,通透以后我们再使用任意 I2C 接口的设备时就可以信手拈来了。我一直觉得在学习的过程中,“会使用”不一定就是我们追求的终点,会用的同时把一些更深的东西搞懂搞透会收获意想不到的喜悦。