IIC总线上的设备均只通过SDA数据线和SCL时钟线进行数据的传输,一条总线上只有一个主设备,但是可以有多个从设备。主设备发送数据,所有从设备都可以收得到。所以如果想要跟某一个设备通信,需要将从设备的地址发送到地址线上,从设备读地址后确认是否是和自己通信。
IIC总线的空闲态是SDA、SCL均为高电平时,通过Rp上拉电阻实现。同时可以看到,连接到IIC总线上的任一设备输出低电平,都会使总线变为低电平,SDA和SCL上连接的设备就是所谓的线“与”关系。
IIC总线有其判断数据有效的方式,当读取数据时,只有SDA线上的数据能够维持SCL的一个时钟时间内均为高电平,才可以确定此时数据有效。同时,SDA线上的数据只能在SCL线为0的时候才可以改变传输的数据。图片的不同步是由于延迟等原因造成的,是正常现象。
起始信号:检测到SCL为高,SDA发生1-0跳变。具体代码设计为起始时SDA和SCL均为高,当发送数据时,SDA电平拉低,延迟一段时间,SCL拉低,开始传输数据:
void IIC_Start(void)
{
IIC_SCL_1;
IIC_SDA_1;
IIC_Delay();//起始信号建立时间0.6us 400KHz
IIC_SDA_0;
IIC_Delay();//起始信号保持0.6us
IIC_SCL_0;
IIC_Delay();
}
终止信号:检测到SCL为高,SDA发生0-1跳变。具体设计为SDA为低,SCL置高,开始终止行动,延迟一段时间,SDA拉高,进入空闲态:
void IIC_Stop(void)
{
IIC_SDA_0;
IIC_SCL_1;
IIC_Delay();//终止信号建立时间0.6us
IIC_SDA_1;
IIC_Delay();//总线空闲建立时间1.3us
}
发送数据:IIC接口可以从最低位开始传输数据,可以从最高位开始传输数据,我们规定从最高位开始传输数据,每次传输一个字节,即8位数据。通过判断byte每一位0/1,来改变SDA线上数据为0/1。当发送完一位数据后,SCL置低,开始发送下一个数据。当8个数据发送结束后,发送设备需要通过第9位的ACK确认信号判断接收设备是否成功接收数据,0-成功,1-失败。本设计中直接读取SDA引脚的输入电平来判断ACK信号的值。最后,将SCL电平置低,等待下一次数据发送
void IIC_Send_Byte(uint8_t byte)
{
uint8_t i;
//从最高位开始发送数据
for(i=0;i<8;i++)
{
//最高位为1,则将SDA置1;反之,置0
if(byte & 0x80)
{
IIC_SDA_1;
}
else
{
IIC_SDA_0;
}
IIC_Delay();
IIC_SCL_1;
IIC_Delay();
IIC_SCL_0;
IIC_Delay();
byte<<=1;
}
IIC_SDA_1;
IIC_Delay();
IIC_SCL_1;
IIC_Delay();
if(IIC_SDA_READ)
{
ack=1;
}
else
{
ack=0;
}
IIC_SCL_0;
IIC_Delay();
}
接收数据:IIC接收数据需要SCL置高的情况下,才可以判断数据有效,每成功读取一位数据,需要将SCL电平置低。连续读取8次SDA线上的电平值来实现数据的读取。本设计中每次数据读取先将receive移位,是为了退出循环时可以保证receive的8位数据均存入了我们的数据。
uint8_t IIC_Receive_Byte(void)
{
uint8_t receive=0;
uint8_t i;
for(i=0;i<8;i++)
{
receive<<=1;
IIC_SCL_1;//置时钟数据线为高,使数据线上的数据有效
IIC_Delay();
if(IIC_SDA_READ)
{
receive++;//读数据
}
IIC_SCL_0;
IIC_Delay();
}
return receive;
}
至此,IIC接口的原理概述完毕,欢迎大佬指点哈~