IIC协议(2)
a 数据有效性
在时钟的高电平周期内,SDA线上的数据必须保持稳定,数据线仅可以在时钟SCL为低电平时改变。
如图(2)所示:
图(2)
b.起始和结束条件
起始条件:当SCL为高电平的时候,SDA线上由高到低的跳变被定义为起始条件,
结束条件:当SCL为高电平的时候,SDA线上由低到高的跳变被定义为停止条件.
要注意起始和终止信号都是由主机发出的,连接到I2C总线上的器件,若具有I2C总线的硬件接口,则很容易检测到起始和终止信号。
总线在起始条件之后,视为忙状态,在停止条件之后被视为空闲状态,
每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。
对起始条件和结束条件的描述如下图(3)所示。
图(3)
c.应答
每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,
从机应答主机所需要的时钟仍是主机提供的,应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答,
如图(4)所示。
图(4)
d.数据帧格式
I2C总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。 在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。{这里小编在驱动MPU6050模块的时候,就犯过这样的错误,它写的MPU6050从机地址是0x68,因为发送从机地址的时候,要加一位读写方向位,因为刚开始应该是向这个MPU6050里写从机里某个寄存器的地址,所以应该是7位地址 0x68(1101000)+二进制位0=11010000)也就是0xD0,表示要向该IIC设备里写东西,然后再紧接着写入IIC设备里的寄存器地址,而我直接写入了0x68,导致出错},每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。 在总线的一次数据传输过程中,可以有以下几种组合方式: [1] 主机向从机发送数据,数据传送方向在整个传送过程中不变:[3]在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好反相:
一般情况下,[3]是比较常见的,比如MPU6050模块, 发送起始信号 等待从机应答 写一个从机地址+0(表示写), 等待从机应答 发送一个字节的MPU6050加速度存储寄存器地址, 等待从机应答 再发送一次起始信号 等待从机应答 写一个从机地址+1(表示读) 等待从机应答 读取MPU6050传感器数据 主机非应答 e.IIC信号的模拟 主机可以采用不带I2C总线接口的单片机,如80C51、AT89C2051等单片机,利用软件实现I2C总线的数据传送,即软件与硬件结合的信号模拟。即使是含有IIC硬件的单片机(如stm32 103系列)也有一定的缺陷,所以一般也会模拟IIC的时序。现将具体时间截图如下:
具体的程序代码如下:
//产生起始信号 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. }
针对于不同的IIC设备,IIC协议可能会有一定的区别,有的地址需要左移一位,把最低位腾出来做读写位。