IIC简介
IIC由飞利浦公司开发的,有引脚少,硬件实现简单,扩展性强的特点,不需要有特殊的外部收发设备,目前也被广泛的应用于集成电路之间的通讯。IIC多应用于小数据场合,传输的距离短,在任意时刻只能有一个主机,主要适用于低速设备通信,其传输速率是比不上SPI的。
主要有以下特点:
两根通信线SCL(Serial Clock)、SDA(Serial Data)。
属于同步、半双工的通信。
支持总线挂载多设备(一主多从、多主多从)
带数据应答
从设备都挂载在SCL、SDA的总线上,因此IIC可以支持多个通讯主机及多个通讯从机。在IIC中是有地址的,每一个设备都有一个独立的地址,主机可以利用这个地址对不同的设备进行访问。
在物理层中可以见到,当IIC的设备空闲的时候,输出的为高阻态,这样做是为了避免总线冲突。可以这样理解,SDA线上有个弹簧,当他们要说话的时候,把线拉下来说话,不说话的时候,放开线SDA弹上去为电源电平,表示总线是空闲的。也就是说只要有人在说话,就需要拉下那根线,拉下后其他人也就不能说话了,这只是自己的一些理解。
因此在这里的GPIO输出要设置成为开漏输出的模式,具备线与的特性,当有很多个开漏模式连接在一起的时候,只有所有的引脚都输出高阻态,其上拉电阻才提供高电平。
对于IIC的高阻态
漏极开路即高阻态,适用于输入/输出,其可独立输入/输出低电平和高阻状态,若需要产生高电平,需要使用外部上拉电阻,以下是其他作者的介绍:
高阻状态:高阻状态是三态门电路的一种状态。逻辑门的输出除有高、低电平两种状态外,还有第三种状态——高阻状态的门电路。电路分析时高阻态可做开路理解。
我们知道IIC的所有设备是接在一根总线上的,那么我们进行通信的时候往往只是几个设备进行通信,那么这时候其余的空闲设备可能会受到总线干扰,或者干扰到总线,怎么办呢?
为了避免总线信号的混乱,IIC的空闲状态只能有外部上拉, 而此时空闲设备被拉到了高阻态,也就是相当于断路, 整个IIC总线只有开启了的设备才会正常进行通信,而不会干扰到其他设备
IIC的各个组成
IIC的通讯基本有三种,主机写数据到从机、主机由从机里读数据、IIC的复合格式。在程序的设计当中我们应该是先对各个部分进行实现,然后根据这三种模式的需要进行拼凑即可(这里是 指的软件模拟IIC)。
要完成软件模拟IIC,那么我们对其SCL、SDA应进行封装,函数如下,并且应加入一段时间的演示,是由于开漏输出的模式下,电平转换的时间是较慢的。所有应加入一段延时。
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
Delay_us(10);
}
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
Delay_us(10);
}
uint8_t MyI2C_R_SDA(void)
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
Delay_us(10);
return BitValue;
}
在起始信号和停止信号中,起始信号是SCL在高电平期间,SDA由高电平变化成为低电平,停止信号也是SCL在高电平情况下,SDA由低电平变化为高电平。起始信号和停止信号非常的有特点。
起始信号和停止信号都是在时钟线在高电平期间,SDA发生电平变化,恰好区别于在数据传输的过程中,SCL处于高电平时,SDA电平不能发送变化的特点。
void MyI2C_Start(void)
{
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
void MyI2C_Stop(void)
{
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
在数据传输的过程中,SCL在高电平情况下,SDA是不允许发送变化的,此时是读取SDA的电平。在SCL低电平情况下,SDA发送电平变化。也就是说在SCL电平为1的时候,数据线上SDA的任何电平变化都会看作为总线的起始信号或者停止信号。
应答信号
接收应答:每当主机向从机发送完一个字节的数据,主机总是要等到从机给出一个应答的信号,来确认从机是否成功的接收到了数据,数据0表示应答,数据1表示非应答,在主机接收前需要释放SDA,也就是主机SCL拉高,读取从机的SDA电平,为低电平的时候为应答。
发送应答:主机在接收一个字节数据后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答。
void MyIIC_SendAck(uint8_t AckBit) //1是非应答 0是应答
{
MyIIC_W_SDA(AckBit);
MyIIC_W_SCL(1);
MyIIC_W_SCL(0);
}
/*!
* @brief主机在发送完一个字节之后,在下一个时钟接收一位数据
判断从机是否应答 主机在接收前要释放SDA 0表示应答 1 表示非应答
* @param
* @param
* @param
* @since
* Sample usage:
*/
uint8_t MyIIC_ReceiveAck(void)
{
uint8_t AckBit;
MyIIC_W_SDA(1);
MyIIC_W_SCL(1);
AckBit = MyIIC_R_SDA();
MyIIC_W_SCL(0);
return AckBit;
}
IIC的数据传送
SDA线上的数据在SCL时钟“高”期间必须是稳定的,只有当SCL线上的时钟信号为低时,数据线上的“高”或“低”状态才可以改变。输出到SDA线上的每个字节必须是8位,数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)
void MyI2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i ++)
{
MyI2C_W_SDA(Byte & (0x80 >> i));
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
}
uint8_t MyI2C_ReceiveByte(void)
{
uint8_t i, Byte = 0x00;
MyI2C_W_SDA(1);
for (i = 0; i < 8; i ++)
{
MyI2C_W_SCL(1);
if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}
MyI2C_W_SCL(0);
}
return Byte;
}
多数从设备的地址为7位或者10位,一般都用七位,八位的设备地址=7位从机地址+读/写位
0表示主设备向从设备写数据
1表示主设备向从设备读数据