I2C(Inter-IntegratedCircuit BUS) 集成电路总线,该总线由NXP(原PHILIPS)公司设计,多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。连接到总线的IC数量只是受到总线的最大负载电容400pf限制。
I2C支持0kHz~5MHz的设备:
普通模式(100kHz)、快速模式(400kHz)、快速模式+(1MHz)、高速模式(3.4MHz)和超高速模式(5MHz)。
两根数据线:SDA,SCL,均为漏级开路结构。一般接上拉电阻,形成“线与”逻辑(只要一方为低电平,则此线即为低电平)。当SDA,SCL为高电平时,表示总线空闲。一般来说,主机发起传输之前都要检查总线的电平状态(称为仲裁),以确定是否进行数据传输(当总线上只有一个主机时,可以不用)。如果I/O口既能输入也能输出,可以配置成开漏输出,但是必须外接拉电阻;如果I/O不能配置成开漏输出,则可以转换I/O输入输出方向,输出采用推挽输出,输入使用上拉输入即可。注意使用转换方向的方式时必须先转换方向之后才开始释放总线。
支持多主控,但是同一时间只能有一个主控。每个设备都有自己的设备地址(共7bit,有的是10bit),用于区分挂在在总线上的设备,广播地址0x00。最低位用于读写控制位,1表示读数据,0表示写数据。
(芯片数据手册上给出的地址需要左移1位,如果采用硬件驱动,则看具体的平台)
l 开始信号
当开始进行一次数据传输时,需要向从设备发送一个开始信号,表示数据传输开始。
SCL为高电平期间,SDA 由高到低表示开始信号。
l 结束信号
当结束一次传输时,需要发送结束信号
SCL为高电平期间,SDA由高到低表示结束信号。
(若主机在对一个从机操作之后,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址)
l 数据位
当传输数据位时,在SCL为低时改变SDA,在SCL为高时保持SDA稳定。高位先传输。
l 应答位
数据的第9位为应答位。
应答:第9个CLK时钟为高电平期间,如果SDA为为低电平,则为应答信号。
非应答:第9个CLK时钟为高电平期间,如果SDA为为高电平,则为非应答信号。
主机每向从机发送一个字节数据,从机都需要发送一个应答信号,而主机每接收一个字节都需要发送一个应答信号,当主机不准备接收下一个字节时,发送一个非应答信号,也就是说,非应答信号是由主机发送的,从机只能发送应答信号。应答位的数据状态则遵循“谁接收谁产生”的原则,即总是由接收器产生应答位
可以通过发送设备地址后由应答位确定该设备是否存在。
注意:任何在SCL为高电平期间的SDA上的电平改变都会被认为是起始或者停止信号,所以数据线SDA必须要在时钟线SCL为低电平时改变。
如下为传输一个字节的情况:
所有数据传输的发起者都是主设备,从设备只能被动接受主设备的请求。
实际上发送一个字节之后就马上发送停止信号一般是不能实现一次完整的数据传输的,那么正常传输流程应该是怎样的呢?
因为I2C总线上可能挂在了很多设备,所以首先需要在总线上发送一个设备地址,并且指明本次传输的方向。然后又因为一个设备里面有很多寄存器,所以还要再发送一个寄存器地址,最后才是发送寄存器的内容。
以上这些理论知识只是和I2C有关的,实际使用的时候根据驱动器件的不同又会有所不同。比如AT2402,只能连续发送8个字节的寄存器内容(类似8字节缓存),下次再发送的话需要重新发送开始信号,另外,进行下一次数据的传输时,需要延时一段时间,让器件将8字节缓存的内容实际写入EEPROM中才可,否则会将缓存内容覆盖,导致写入错误。
通过编写I2C驱动程序,并利用KEIL的仿真功能可以得到如下波形:
这是一个发送设备地址0xA0的波形,因为没有从机,所以在第9个CLK的高电平期间SDA为高。当总线上有设备地址为0xA0的从机时,SDA应被从机设置为低电平。
以上知识实际上是比较简单的,基础的,适合用于单主机的情况下,如果是多主机通信,远比单主机复杂的多,涉及到时钟同步和总线仲裁,有兴趣的同学可以自行研究。
更多关于I2C的问题,可以查阅《I2C总线规范》。
关于总线死锁问题:https://blog.csdn.net/g_salamander/article/details/8016698
总线死锁主要是因为主从设备中的主机异常复位造成从机始终处于应答状态(应答状态时SDA为低电平,只有在SCL变为低电平的时候,才会变为高电平,从而释放SDA),解决方案可在链接中找到。
STM32的硬件I2C有缺陷,但是可以通过一些方法避免。