I2C总线协议,物理层,协议层,M24C08读写实例
何为I2C总线
- I2C英文全称Inter-Integrated Circuit,字面意思是集成电路之间,也就是我们常说的I2C总线—I2C bus。它是一种串行通讯总线,使用多主从架构,但在同一时刻只允许有一个主控在线,由飞利浦公司(恩智浦NXP的母公司)在80年代开发,用于主板、嵌入式系统连接周边低速设备。
- I2C由两条双向开漏线组成,这是一个很大的优势,接线简单。两条线利用上拉电阻将电位上拉。典型电位为+3.3V或+5V。快速模式传输速率为400kbit/s,标准传输速率为100Kb/s,低速模式传输速率10Kb/s。
物理层
- 下图为I2C总线的物理拓扑图,大家可以看到一共只有两条总线,一条SDA(serial data)数据线用来承载数据、一条SCL(serial clock line)时钟线用来控制数据收发时序。所有I2C设备的SDA都接到了总线的SDA上,SCL都接到了总线的SCL上。每个设备都有自己的唯一地址,以保证设备之间访问的准确性。
- I2C在物理层的连接可以说是非常简单,这也是它最大的优势,原理就是通过控制SDA和SCL线的高低电平时序,来产生I2C总线协议所需要的信号进行数据传输。在总线处于空闲状态时SCL和SDA被上拉电阻拉高,保持高电平。
- 需要注意的是I2C的通讯方式为半双工,因为只有一条数据线,某一时刻只可能单向通讯。这也说明了I2C不适合大数据量的传输应用。
- 对于主机、从机的区分很简单,发布主要命令的就是主机,接受命令的就是从机,同一条I2C总线允许多个主机的存在。
协议层
- 下面对 I2C 总线通信过程中出现的几种信号状态和时序进行分析。
总线空闲状态
- I2C 总线总线的 SDA 和 SCL 两条信号线同时处于高电平时,规定为总线的空闲状态。此时各
个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平
拉高。
启动信号(Start)
- 在时钟线 SCL 保持高电平期间,数据线 SDA 上的电平被拉低(即负跳变),定义为 I2C 总线
总线的启动信号,它标志着一次数据传输的开始。启动信号是由主控器主动建立的,在建立该
信号之前 I2C 总线必须处于空闲状态。
停止信号(Stop)
- 在时钟线 SCL 保持高电平期间,数据线 SDA 被释放,使得 SDA 返回高电平(即正跳变),称
为 I2C 总线的停止信号,它标志着一次数据传输的终止。停止信号也是由主控器主动建立的,
建立该信号之后, I2C 总线将返回空闲状态。
数据位传送
- 在 I2C 总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在 SCL 串行
时钟的配合下,在 SDA 上逐位地串行传送每一位数据。进行数据传送时,在 SCL 呈现高电平期
间, SDA 上的电平必须保持稳定,低电平为数据 0,高电平为数据 1。只有在 SCL 为低电平期间,
才允许 SDA 上的电平改变状态。
应答信号(ACK 和 NACK)
- I2C 总线上的所有数据都是以 8 位字节传送的,发送器每发送一个字节,就在时钟脉冲 9 期
间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK 简
称应答位),表示接收器已经成功地接收了该字节; - 应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。对
于反馈有效应答位 ACK 的要求是,接收器在第 9 个时钟脉冲之前的低电平期间将 SDA 线拉低,
并且确保在该时钟的高电平期间为稳定的低电平。 - 如果接收器是主控器,则在它收到最后一个字节后,发送一个 NACK 信号,以通知被控发
送器结束数据发送,并释放 SDA 线,以便主控接收器发送一个停止信号。
读写实例
- 以M24C08 EEPROM的读写操作为例,讲解I2C协议的实际应用。
写操作
写入一个字节
- 向从机设备的某一个寄存器写一个字节数据:开始信号+设备地址(7位)+读/写(1位)+等待从机应答+寄存器地址(8位)+等待从机应答+要写的数据(8位)+等待从机应答+终止信号。
写入多个字节
- 向从机设备的某一个寄存器写多个字节数据:开始信号+设备地址(7位)+读/写(1位)+等待从机应答+寄存器地址(8位)+等待从机应答+要写的数据_1(8位)+等待从机应答+要写的数据_2(8位)+等待从机应答+······+要写的数据_N(8位)+等待从机应答+终止信号。
读操作
读取流程
- 读取操作的程序流程如下:
- 初始条件:一个写周期正在进行中。
- 步骤1:总线主机发出启动条件,后跟设备选择代码(新指令的第一个字节)。
- 步骤2:如果设备正忙于内部写周期,则不会返回任何确认,总线主控器将返回到步骤1。如果设备终止了内部写周期,它将以Ack响应,表明设备处于准备接收指令的第二部分(该指令的第一字节已在步骤1中发送)。
读取一个字节
- 从机设备的某一个寄存器读取一个字节数据:该过程基本类似于下文将要介绍到的从机设备的某一个寄存器读取多个字节数据,故在此处不再过多赘述,可参照多字节的读取方式。
读取多个字节
- 从从机设备的某一个寄存器读取多个字节数据:开始信号+设备地址(7位)+写(1位)+等待从机应答+数据地址(8位)+等待从机应答。
- 需要注意,此处的写为假写,主要目的是为了告诉M24C08,我将要从哪一个寄存器开始进行数据读取。
- 接着,再发送:开始信号+设备地址(7位)+读(1位)+等待从机应答+从机返回读取数据_1(8位)+主机(接收机)应答+从机返回读取数据_2(8位)+主机(接收机)应答+从机返回读取数据_N(8位)+主机(接收机)不再应答+终止信号
I2C补充知识
设备的地址
- I2C设备的地址为8位,但是时序操作时最后一位不属于地址,而是读or写状态位。这就是为什么arduino的SH1106库里操作的地址不是0x7-而是0x3-,因为有用的是前7位,地址整体右移一位处理了。再一个设备地址的前四位是固定死的,是厂家用来表示设备类型的,比如接口为I2C的温度传感器类设备地址前四位一般为1001即9X、EEPROM存储器地址前四位一般为1010即AX、oled屏地址前四位一般为0111即7X等等。
速度慢距离近
- I2C接口的致命缺点就是传输距离近同时速度慢。大家在使用I2C总线接口的时候切记不要长线传输,尽量只在PCB板内传输,不然偶尔丢数据甚至读不到数据会让人崩溃
电路开漏设计
- I2C总线被设计为开漏输出模式,主要有两个原因
- 防止短路:如果不设为开漏,而设为推挽,几个设备连在同一条总线上,这时某一设备的某个IO输出高电平,另有一台设备的某一个IO输出低电平,这时你会发现这两个IO的VCC和GND短路了;但是开漏就不会有这个问题。
- “线与”:如果总线上的一个A设备将SDA拉高,这时总线上另一个B设备已将SDA拉低,这时由于1&0=0,所以A设备检查SDA的时候会发现不是高电平而是低电平,这就表明总线上已经有其他设备占用总线了,A只好放弃,如果检测是高电平那就可以使用。如下图示为24C02芯片内部图,可以看到状态检测脚。