I2C总线是一种同步、双向、半双工的串行总线,通信双方只需要两条线就可以实现通信,非常方便。
1. 硬件I2C和软件I2C
I2C有硬件I2C和软件I2C之分,简单介绍如下:
- 硬件I2C:即对应芯片上的I2C外设,速度比软件I2C快,并且可以使用DMA,但有些单片机的I2C
外设不太稳定,就例如STM32上的I2C就有bug。 - 软件I2C:用程序来控制IO口输出高低电平,模拟I2C协议的时序,从而实现I2C协议。相对于硬件I2C,软件I2C一般较稳定,但速度较慢,不过软件I2C可以模拟在任何管脚上,不像硬件I2C只有固定管脚。
2. I2C协议
在模拟I2C之前,得了解I2C总线的通讯过程,不然只会无从下手。
I2C总线的传输一个字节的过程概括如下:
在总线空闲(即SDA线和SCL线处于高电平,在I2C中要通过上拉电阻才能实现高电平)时,发送端产生起始信号,表示本次传输的开始,之后发送一个字节的数据(该数据总是高位在前的),然后等待接收端的应答信号或非应答信号,应答信号和非应答信号分别表示接受端接受到数据和没接收到数据,最后产生停止信号,表示本次传输的结束。
有几个要注意的点:
- 除了起始信号和停止信号,其他信号的电平状态切换只能在SCL线处于低电平时进行;而且在SCL线处于高电平时,SDA线必须保持稳定的电平状态。
- I2C协议支持的通讯速度在100kHz到400kHz之间,如果是模拟I2C,要保证所写的延时函数延时的时间尽量在频率对应的范围之内。
接下来,来看看各信号的定义:
-
起始信号和停止信号
起始信号:在SCL线高电平时,SDA线从高电平向低电平切换
停止信号:在SCL线高电平时,SDA线从低电平向高电平切换 -
应答信号和非应答信号
在传输一个byte数据后,在第9个bit对的脉冲高电平期间,SDA线若保持高电平,即为应答信号;SDA线若保持低电平,即为非应答信号。
3. 通过IO口模拟I2C
那么下来就是如何通过IO口来模拟I2C了,话就不多说,直接开冲!
既然模拟I2C是通过IO来模拟的,那首先肯定是要对相关GPIO进行初始化的,代码如下:
void I2C_GPIO_Init()
{
GPIO_InitTypeDef I2C_GPIO_InitStruct;
//打开数据信号线SDA和时钟信号线SCL的时钟
RCC_APB2PeriphClockCmd(I2C_SDA_CLK|I2C_SCL_CLK, ENABLE);
//SDA线对应IO口的配置
I2C_GPIO_InitStruct.GPIO_Pin = I2C_SDA_PIN;
I2C_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
I2C_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(I2C_SDA_PORT, &I2C_GPIO_InitStruct);
//SCL线对应IO口的配置
I2C_GPIO_InitStruct.GPIO_Pin = I2C_SCL_PIN;
I2C_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
I2C_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;