一.序言
背景知识:I2C总线上是通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,当所有设备都空闲时,由上拉电阻把总线拉成高电平。所以默认总线上是高电平。
I2C设备之间常见的连接方式。
二.IIC读写过程
注:一般单片机是主机,各种模块是从机(eeprom,oled显示屏,以及各种传感器)
2.1主机向从机写入数据
首先给一个起始信号,然后发送从机地址(地址最后一位决定是写数据还是读数据)这里给的是 ‘0’,表示写入数据。写入从机地址后,等待从机给响应信号。主机接收到响应数据后才可以开始传输数据了,如果主机不想传输数据了,就给出停止信号。数据传输结束
2.2主机向从机读取数据
这边同写入数据一样,先给一个起始信号,再发送从机地址(地址最后一位表示读还是写)这里是 ‘1’,表示读取数据。等待从机响应。响应后就可以读取数据了。读取数据后,要发送一个应答信号(告诉从机数据已经被读取),从机收到应答信号后就可以继续发送数据给主机。如果主机不想再接收数据了,就发送一个非应答信号(告诉从机不要发送数据了),主机给出停止信号。数据读取完成。
2.3 I2C起始信号和停止信号
起始信号是SCL时钟线在高电平时,SDA由高拉低,产生一个下降沿。表示起始信号。
停止信号是SCL时钟线在高电平时,SDA由低电平拉到高电平,产生一个上升沿。表示停止信号
三. 数据的有效性
我们知道I2C是时钟线和数据线协同工作的。那么I2C是如何表示有效数据的呢?
其实就是在SCL保持高电平的时候,SDA的数据才是有效的,正确的。这时候才能去读取SDA的数据(同时这时候不能去改变SDA的数据)不然可能会读取到错误数据,那么SCL低电平时是在干嘛? SCL低电平时SDA进行数据变化(此时并不会读取SDA的数据),等SDA数据稳定后,在拉高SCL,进行数据读取。
四.时序要求
前面我们只是讲了大概流程,其实I2C对时序也是要求严格的。下面我就给大家讲解时序。
4.1 起始信号
起始产生时,SCL高电平时,SDA高电平至少维持4.7us,此时拉低SDA产生下降沿,至少维持4us.
4.2 终止信号
终止信号产生时,SCL高电平时,SDA低电平至少维持4us,再拉高SDA产生上升沿。维持SDA至少4.7us
4.3 应答信号
当SCL是高电平时,且至少维持4us,SDA数据线是0,此时表示应答信号
4.4 非应答信号
当SCL是高电平时,且至少维持4us,SDA数据线是1,此时表示非应答信号
4.5读取数据
我们可以参考下图,当读取数据时SCL保持时间为tHIGH最少4ms。当SDA变化数据是,SCL保持低电平的时间tLow至少保持4.7us,此时可以改变SDA上的数据。
五.代码实例
void I2C_Start(void) { IIC_SDA = 1; IIC_SCL = 1; //高电平 delay_us(5); //保持4.7us以上,这里5us IIC_SDA = 0; //拉低,下降沿 delay_us(5); //保持低电平 IIC_SCL = 0; //拉低 } void I2C_Stop(void) { IIC_SDA = 0; IIC_SCL = 1; delay_us(5);//保持高电平 IIC_SDA = 1; //拉高,上升沿 delay_us(5); } void I2C_SendACK(char ack) { IIC_SCL = 0; if(ack) IIC_SDA = 1; else IIC_SDA = 0; delay_us(2); //短延时等待SDA稳定 IIC_SCL = 1; //拉高SCL delay_us(5); //延时,从机读取SDA IIC_SCL = 0; char I2C_RecvACK(void) { unsigned char cnt=0; IIC_SDA=1; //释放数据线 delay_us(1); IIC_SCL=1; //时钟线拉高 delay_us(1); while(IIC_SDA) //等待从机把SDA拉低,产生应答信号 { cnt++; delay_us(1); if(cnt>=250) { I2C_Stop(); return 1; } } IIC_SCL=0; return 0; } void I2C_SendByte(unsigned char data) { //data高位先发。 unsigned char i; IIC_SCL = 0; //拉低SCL delay_us(2); for (i=0;i<8;i++) { if(data&0x80) IIC_SDA=1;//给SDA赋值 else IIC_SDA=0; data <<= 1; IIC_SCL = 1; //拉高时钟线 delay_us(2); //延时,从机读取SDA数据 IIC_SCL = 0; delay_us(2); } } unsigned char I2C_RecvByte(void) { //低位先接收 unsigned char i; unsigned char data = 0; for (i=0;i<8;i++) { IIC_SCL=0;//拉低时钟线 delay_us(2);//此时从机改变SDA数据 IIC_SCL=1; //拉高时钟线 data <<= 1; IIC_SCL = 1; if(IIC_SDA) data|=0x01; //读取数据线 delay_us(1); //因为默认是0,所以IIC_SDA为0是可以不赋值0 } IIC_SCL=0; return data; }
六.结语
本次也是结合I2C的时序图讲述了如何产生起始信号,停止信号,应答信号,非应答信号,向从机读取数据,向从机写入数据等。最后也是给出了代码实例,理论和实践结合用,帮助读者深入理解IIC。