IIC通信详解
IIC通信详解
(1)概述
I2C(Inter-Integrated Circuit BUS) 集成电路总线,该总线由NXP(原PHILIPS)公司设计,多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。
经常IIC和SPI接口被认为指定是一种硬件设备,但其实这样的说法是不尽准确的,严格的说他们都是人们所定义的软硬结合体,分为物理层(四线结构)和协议层(主机,从机,时钟极性,时钟相位)。
IIC,SPI的区别不仅在与物理层,IIC比SPI有着一套更为复杂的协议层定义。下面来分别说明一下IIC的物理层和协议层。
(2)IIC的物理层
a.只要求两条总线线路,一条是串行数据线SDA,一条是串行时钟线SCL。(IIC是半双工,而不是全双工)。
b.每个连接到总线的器件都可以通过唯一的地址和其它器件通信,主机/从机角色和地址可配置,主机可以作为主机发送器和主机接收器。
c.IIC是真正的多主机总线,(而这个SPI在每次通信前都需要把主机定死,而IIC可以在通讯过程中,改变主机),如果两个或更多的主机同时请求总线,可以通过冲突检测和仲裁防止总线数据被破坏。
d.传输速率在标准模式下可以达到100kb/s,快速模式下可以达到400kb/s。
e.连接到总线的IC数量只是受到总线的最大负载电容400pf限制。
一个典型的IIC接口如下图(1)所示
图(1)
(3)IIC的协议层
IIC的协议层才是掌握IIC的关键。现在简单概括如下:
a.数据的有效性
在时钟的高电平周期内,SDA线上的数据必须保持稳定,数据线仅可以在时钟SCL为低电平时改变。
如图(2)所示:
图(2)
b.起始和结束条件
起始条件:当SCL为高电平的时候,SDA线上由高到低的跳变被定义为起始条件,结束条件:当SCL为高电平的时候,SDA线上由低到高的跳变被定义为停止条件,要注意起始和终止信号都是由主机发出的,连接到I2C总线上的器件,若具有I2C总线的硬件接口,则很容易检测到起始和终止信号。总线在起始条件之后,视为忙状态,在停止条件之后被视为空闲状态,对起始条件和结束条件的描述如下图(3)所示。
图(3)
c.应答
每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,从机应答主机所需要的时钟仍是主机提供的,应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答,如图(4)所示。
图(4)
d.数据帧格式
//产生起始信号 void I2C_Start(void) { I2C_SDA_OUT();//配置一下引脚,引脚设置为输出 I2C_SDA_H;//把数据线拉高 I2C_SCL_H;//把时钟线拉高 delay_us(5);//延时5微秒,要求大于4.7微秒 I2C_SDA_L; //拉低,产生下降沿 delay_us(6);//这个过程大于4微秒 I2C_SCL_L;//最后一定要把这个时钟线拉低,因为只有时钟线拉低的时候才允许数据变化。 } //产生停止信号 void I2C_Stop(void) { I2C_SDA_OUT(); I2C_SCL_L; I2C_SDA_L; I2C_SCL_H; delay_us(6); I2C_SDA_H; delay_us(6); } //主机产生应答信号ACK void I2C_Ack(void) { I2C_SCL_L; I2C_SDA_OUT(); I2C_SDA_L; delay_us(2); I2C_SCL_H; delay_us(5); I2C_SCL_L; } //主机不产生应答信号NACK void I2C_NAck(void) { I2C_SCL_L; I2C_SDA_OUT(); I2C_SDA_H; delay_us(2); I2C_SCL_H; delay_us(5); I2C_SCL_L; } //等待从机应答信号,我们只负责主机应答信号的产生,从机应答信号 //我们不控制。 //返回值:1 接收应答失败 // 0 接收应答成功 u8 I2C_Wait_Ack(void) { u8 tempTime=0; I2C_SDA_IN(); //配置为上拉输入。 I2C_SDA_H; //主机释放数据总线,等待从机产生应答信号 delay_us(1); I2C_SCL_H; delay_us(1); //等待从机对数据总线的操作。低电平代表应答 while(GPIO_ReadInputDataBit(GPIO_I2C,I2C_SDA)) { tempTime++; //这个属于软件延时,不一定准确。 if(tempTime>250) //如果时间超时,没有应答就停止。 { I2C_Stop(); return 1; //没有响应的话返回1. } } I2C_SCL_L; return 0; //如果有响应的话就返回0. }