1、IIC功能描述
IIC(Inter-Integrated Circuit,内部整合电路)是一种串行、半双工总线,主要用于近距离、低速的芯片之间的通信。它的主要功能描述如下:
- 基本通信:IIC总线使用两根信号线,一根为SCL(时钟线),另一根为SDA(数据线),实现双向的数据传输。主机有权发起和结束一次通信,而从机只能被呼叫。当总线上有多个主机同时启用总线时,IIC也具备冲突检测和仲裁的功能来防止错误产生。
- 多主机架构:IIC是一种多主机总线,连接在IIC总线上的器件分为主机和从机。每个连接到IIC总线上的器件都有唯一的地址(7bit),并且每个器件都可以作为主机和从机。
- 数据传输:IIC通信时每个字节为8位长度,数据传送时,先传送高位,后传送低位。发送器发送完一个字节后,接收器必须发送一位的响应信号。因此,每一帧数据为9位。
- 简单性和扩展性:IIC总线物理链路简单,硬件实现方便,扩展性非常好。1个主机控制器可以根据需求增加从机数量,同时删减从机数量也不会影响总线通信。
- 小型化封装:采用IIC总线的器件封装更容易实现小型化,对PCB空间的要求较小。同时,采用IIC总线的器件管脚数量得到明显的简化,可以减少一半的器件面积。
- 高速传输:高速IIC总线一般可达400kbps以上,适用于需要高速数据传输的应用场景。
2、IIC总线协议
2.1 数据的有效性
I2C 使用 SDA 信号线来传输数据,使用 SCL 信号线进行数据同步。见图数据有效性。 SDA 数据线在 SCL 的每个时钟周期传输一位数据。传输时, SCL 为高电平的时候 SDA 表示的数据有效,即此时的 SDA 为高电平时表示数据“1”,为低电平时表示数据“0”。当 SCL 为低电平时, SDA的数据无效,一般在这个时候 SDA 进行电平切换,为下一次表示数据做好准备。
起始信号与结束信号
当 SCL 线是高电平时 SDA 线从高电平向低电平切换,这个情况表示通讯的起始。
当 SCL 是高电平时 SDA 线由低电平向高电平切换,表示通讯的停止。
应答信号与非应答信号
每当主机向从机发送完一个字节的数据后,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据。应答信号是一个低电平信号(0),表示从机已经成功接收到了数据。
非应答信号是一个高电平信号(1),表示从机没有成功接收到数据或无法接收更多的数据。非应答信号也可以由主机发送,用于通知从机停止发送数据。
起始信号与结束信号、应答信号与非应答信号具体代码:
本文只展示IIC通讯协议代码,具体GPIO初始化自行了解
#define EEPROM_GPIO_PORT_I2C GPIOA /* GPIO端口 */
#define EEPROM_RCC_I2C_PORT RCC_APB2Periph_GPIOA /* GPIO端口时钟 */
#define EEPROM_I2C_SCL_PIN GPIO_Pin_2 /* 连接到SCL时钟线的GPIO */
#define EEPROM_I2C_SDA_PIN GPIO_Pin_3 /* 连接到SDA数据线的GPIO */
#define EEPROM_I2C_SCL_1() GPIO_SetBits(EEPROM_GPIO_PORT_I2C, EEPROM_I2C_SCL_PIN) /* SCL = 1 */
#define EEPROM_I2C_SCL_0() GPIO_ResetBits(EEPROM_GPIO_PORT_I2C, EEPROM_I2C_SCL_PIN) /* SCL = 0 */
#define EEPROM_I2C_SDA_1() GPIO_SetBits(EEPROM_GPIO_PORT_I2C, EEPROM_I2C_SDA_PIN) /* SDA = 1 */
#define EEPROM_I2C_SDA_0() GPIO_ResetBits(EEPROM_GPIO_PORT_I2C, EEPROM_I2C_SDA_PIN) /* SDA = 0 */
static void i2c_Delay(void)
{
/* 延时 */
uint8_t i;
for (i = 0; i < 10; i++);
}
void i2c_Start(void)
{
/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
EEPROM_I2C_SDA_1();
EEPROM_I2C_SCL_1();
i2c_Delay();
EEPROM_I2C_SDA_0();
i2c_Delay();
EEPROM_I2C_SCL_0();
i2c_Delay();
}
void i2c_Stop(void)
{
/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
EEPROM_I2C_SDA_0();
EEPROM_I2C_SCL_1();
i2c_Delay();
EEPROM_I2C_SDA_1();
}
void i2c_Ack(void)
{
EEPROM_I2C_SDA_0(); /* CPU驱动SDA = 0 */
i2c_Delay();
EEPROM_I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
EEPROM_I2C_SCL_0();
i2c_Delay();
EEPROM_I2C_SDA_1(); /* CPU释放SDA总线 */
}
void i2c_NAck(void)
{
EEPROM_I2C_SDA_1(); /* CPU驱动SDA = 1 */
i2c_Delay();
EEPROM_I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
EEPROM_I2C_SCL_0();
i2c_Delay();
}