I2C 总线上的通信通常发生在两个器件之间,其中一个作为主机,另一个为从机。同一总线上可以连接多个地址不同的器件。
I2C 总线由两条线路组成,SDA 线和 SCL 线,SDA 传送数据,SCL 提供时钟,所有数据以 8 位为一组通过 I2C 总线传送。为了在 I2C 总线上传送 1 位数据,须在SCL为低电平时驱动 SDA 线至适当的电平(SDA 为低则表明该位为 0,为高则表明该位为 1)。一旦 SDA 线稳定下来 SCL 线被拉高,然后变低,SCL 线上的脉冲以时钟将SDA位一位一位地移入接收器的移位寄存器中。
I2C 总线是双向的:SDA 线可用来发送和接收数据。当主机从从机中读取数据时,从机驱动数据线,当主机向从机发送数据时,主机驱动数据线,主机总是驱动时钟线。
通信时,总线被激活,只有主机才能发起一次通信,为了开始通信,主机
在总线上形成一个开始条件。通常,只有在时钟线为低电平时,数据线才允许改变状态,如果在时钟线为高电平时,数据线改变了状态,则形成一个开始条件或相反地形成一个停止条件。
总线空闲时:不发生通信时,时钟线和数据线都为高电平。
通常传输数据时:时钟线为低电平时,数据线才允许改变状态。
开始条件:时钟线为高电平时,数据线从高到低的跳变。
停止条件:时钟线为高电平时,数据线从低到高的跳变。
主机发送开始条件以后,它还会发送一个字节表明它想与哪一个从机通信,该字节称作地址字节。I2C 总线上的每个器件都有唯一的 7 位地址,以作出响应。主机以地址字节发送一个地址,并且还发出一位以表明是对从机读出还是写入。
当A2,A1,A0都为1时,写操作时最低位为1,则此时地址为10101110(即0xAE);读操作时最低位为0,则此时地址为10101111(即0xAF);
在 I2C 总线上发送的每个字节,无论是地址还是数据,均以一个应答位作为响应,在主机发送完一个字节(即 8 位)数据到从机后,它停止驱动 SDA 线,并等待从机对该字节的应答,从机将SDA 线拉低,以对该字节进行应答。然后主机发送一个时钟脉冲来对该应答位定时,类似地,当主机完成对一个字节的读取时,则将 SDA 线拉低以对从机作出应答,然后发送一个时钟脉冲对该位定时
(记住主机总是驱动时钟线)。在一个应答周期期间不作应答,只是保持 SDA 线为高电平。如果器件不在总线上,并且如果主机试图对其寻址,它不会接收到应答信号,因为该地址处没有器件将 SDA 线拉低。
主机完成与从机的通信后,它会发出一个停止条件。在发出停止条件后,总线再次空闲,主机也可发出另一个开始条件。
代码示例:
产生IIC起始信号
void IIC_Start(void)
{
SDA_OUT();
delay_us(4);
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;
delay_us(4);
IIC_SCL=0;
}
产生IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
IIC_SCL=1;
delay_us(1);
IIC_SDA=1;
delay_us(4);
}
等待应带信号。1:接收应答失败;0:接收应答成功
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN();
IIC_SDA=1;delay_us(1);
IIC_SCL=1;delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;
return 0;
}
产生ACK应答
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
发送一个字节
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;
for(t=0;t<8;t++)
{
if((txd&0x80)>>7)
IIC_SDA=1;
else
IIC_SDA=0;
txd<<=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
读取一个字节。Ack=1,发送ACK;ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();
else
IIC_Ack();
return receive;
}