I2C通信协议
项目要求是,通过通信线,是实现单片机读写外挂模块寄存器的功能,至少实现,在指定位置写寄存器和在指定位置读寄存器,实现了读写寄存器,就实现对模块的控制。
MPU6050,OLED,AT24C02(存储器模块),DS3231(实时时钟模块)
一主多从指单片机作为主机主导I2C总线的运行,挂载在I2C总线上的所有设备都是从机,只有主机允许才可以控制I2C总线;多主多从是指多个主机,任何一个模块都可以作为主机,当冲突时对其进行仲裁,谁胜利谁成为主机。
作为一个通信协议,I2C必须在硬件和软件上作出规定。硬件上的规定包括电路的连接方式、端口的输入输出模式等;软件上的规定包括时序的定义、字节的传输方式、高位先行还是低位先行等。这些硬件和软件的规定结合起来构成了一个完整的通信协议。
硬件电路
主机永远拥有SCL时钟线的控制权,在空闲时可以主动操控SDA线,也可以将SDA线的控制权交给从机;从机权力较小,永远只能被动的读取SCL线,不允许控制,从机不允许主动发起对SDA的控制,只有在主机发送从机读取的命令后或者从机应答的时候从机才能短暂的取得SDA的控制权。这就是一主多从模型中协议的规定。
如果总线时序没有协调好,就极有可能发生两个引脚同时处于输出的状态。如果此时一个引脚输出高电平,一个引脚输出低电平,就会造成电源短路的情况,这是要极力避免的。
为了避免这种情况,I2C的设计规定所有设备不输出强上拉的高电平,而是采用外置弱上拉电阻加开漏输出的电路结构。这两点规定对应于前面提到的“设备的SCL和SDA均要配置成开漏输出模式”以及“SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右”。对应上面这个图。
引脚内部结构图
图左侧展示的是SCL的结构,其中SClk代表SCL;右侧则是SDA的结构,其中DATA代表SDA。
引脚的信号输入都可以通过一个数据缓冲器或施密特触发器进行,因为输入对电路无影响,所以任何设备在任何时刻都可以输入。然而,在输出部分,采用的是开漏输出的配置。
输出低电平,这个开关管导通,引脚直接接地,是强下拉,输出高电平,这个开关管断开,引脚什么都不接,处于浮空状态,这样的话,所有的设备都只能输出低电平而不能输出高电平,为了避免高电平造成的引脚浮空,这时就需要在总线外面,SCL和SDA各外置一个上拉电阻,这是通过一个电阻拉到高电平的,所以这是一个弱上拉。
- 第一,完全杜绝了电源短路现象,保证电路的安全。
第二,避免了引脚模式的频繁切换。开漏加弱上拉的模式,同时兼具了输入和输出的功能。因为开漏模式下,输出高电平就相当于断开引脚,所以在输入之前,可以直接输出高电平,不需要再切换成输入模式了。
第三,就是这个模式会有一个“线与”的现象。就是只要有任意一个或多个设备输出了低电平,总线就处于低电平,只有所有设备都输出高电平,总线才处于高电平。
I2C可以利用这个电路特性执行多主机模式下的时钟同步和总线仲裁,所以这里SCL虽然在一主多从模式下可以用推挽输出,但是它仍然采用了开漏加上拉输出的模式,因为在多主机模式下会利用到这个特征。
I2C时序基本单元
起始条件是指SCL高电平期间,SDA从高电平切换到低电平。在I2C总线处于空闲状态时,SCL和SDA都处于高电平状态,由外挂的上拉电阻保持。当主机需要数据收发时,会首先产生一个起始条件。这个起始条件是,SCL保持高电平,然后把SDA拉低,产生一个下降沿。当从机捕获到这个SCL高电平,SDA下降沿信号时,就会进行自身的复位,等待主机的召唤。之后,主机需要将SCL拉低。这样做一方面是占用这个总线,另一方面也是为了方便这些基本单元的拼接。这样,除了起始和终止条件,每个时序单元的SCL都是以低电平开始,低电平结束。
**终止条件是,SCL高电平期间,**SDA从低电平切换到高电平。SCL先放开并回弹到高电平,SDA再放开并回弹高电平,产生一个上升沿。这个上升沿触发终止条件,同时终止条件之后,SCL和SDA都是高电平,回归到最初的平静状态。这个起始条件和终止条件就类似串口时序里的起始位和停止位。一个完整的数据帧总是以起始条件开始、终止条件结束。另外,起始和终止都是由主机产生的。因此,从机必须始终保持双手放开,不允许主动跳出来去碰总线。如果允许从机这样做,那么就会变成多主机模型,不在本节的讨论范围之内。这就是起始条件和终止条件的含义。
发送一个字节
主机拉低SCL,把数据放在SDA上,主机松开SCL,从机读取SDA的数据。
在SCL的同步下,依次进行主机发送和从机接收,循环8次,就发送了8位数据,也就是一个字节,另外注意,这里是高位先行,所以第一位是一个字节的最高位B7,然后依次是次高位B6…最后是B0。(串口时序是低位先行,I2C是高位先行)
另外,由于这里有时钟线进行同步,所以如果主机一个字节发送一半,突然进中断,不操作SDA,SCL,那时序就会在中断不断拉长,SDA,SCL电平都暂停变化,传输也暂停,等中断结束后,主机继续回来操作,传输仍然不会出问题,这就是同步时序的好处。
接收一个字节
发送应答和接收应答
I2C的完整时序
主要有指定地址写,当前地址读和指定地址读这3种。
I2C总线上每个从机都确定一个唯一的设备地址,主机在起始条件之后会先发送一个字节确认相应的设备地址,相应的设备响应之后的读写操作,MPU6050地址为1101000
首先,SCL高电平期间,拉低SDA,产生起始条件(start,s),在起始条件后,紧跟着时序,必须是发送一个字节的时序,字节的内容,必须是从机地址+读写位,正好从机地址是7位,读写位是1位,加起来是1个字节,8位,发送从机地址,就是确定从机的对象。发送读写位,就是确认接下来要写入还是读出。在这里低电平期间,SDA变换数据,高电平期间,从机读取SDA。绿色的线,标明从机得到的数据。然后就是1101000,0表示之后的时序主机要进行写操作,1表示之后的时序主机要进行读出操作,这里是0说明之后要进行写操作。
那这里,目前主机是发送一个字节,字节的内容转换为16进制,高位先行,就是0xD0,然后根据协议规定,紧跟着单元是,就是接收从机的应答位,在这个时刻,主机要释放SDA,释放SDA后,引脚电平回弹到高电平,但是根据协议规定,从机要在这个位拉低SDA,所以从机的波形是这样的,该应答的时候,从机立刻拽住SDA,然后应答结束后,从机在放开SDA,现在综合两者的波形,结合线与的特性,在主机释放SDA后,由于SDA被从机拽住了,SDA没有回弹高电平,这个过程代表从机产生了应答。
首先,SCL高电平期间,拉低SDA,产生起始条件(start,s),起始条件开始后,主机必须首先调用发送一个字节,来进行从机的寻址和指定读写标志位。图示的波形,表示本次寻址的目标是1101 000的设备,同时最后一位,读写标志为1,表示主机接下来想要读取数据,紧跟着,发送一个字节后,接收一下从机的应答位。从机应答0,代表从机收到一个字节。
在从机应答后,从这里开始,数据的传输方向就要反过来了,因为主机发出了读的命令,所以之后,主机就不能继续发送了,要把SDA控制权交给从机,主机调用接收一个字节的操作,进行接收操作。
指定地址读=指定地址写+当前地址读
该时序下从中间红线分割,前半部分为指定地址写在写之前的时序,后半部分为当前地址读的时序,因为在前半部分中指定了地址,所以可以读出指定地址的数据。