I2C详解

一、概述

        I2C总线(Inter-Integrated Circuit),由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。常用于微控制器与外设之间的连接。

二、硬件连接

        I2C仅需两根线就可以支持一主多从或者多主连接:

  • SCL(Serial Clock Line):串行时钟线,时钟都是由master提供的。
  • SDA(Serial Data Line):串行数据线,发数据或者收数据(收发不能同时)。
    在这里插入图片描述

三、通信协议

        I2C是串行传输总线,传输是以8位为单元数据传输的,先传输最高位(MSB),主芯片发出start信号之后,然后发出9个时钟传输数据。

  • 空闲状态:SCL和SDA同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
  • 开始信号(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
  • 结束信号(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
  • 响应信号(ACK):发送器每发送一个字节,就会在时钟脉冲的第9个脉冲期间释放数据线SDA,然后由接收器反馈一个应答信号给发送器,如果这个应答信号为低电平,就为有效应答位(ACK简称应答位),表示接收器已经成功的接受了了这个字节;如果这个应答信号为高电平,就为非应答位(NACK),一般表示接收器没有成功接受这个字节。具体来说,对于反馈回来的有效应答位(ACK)的要求是:接收器在第9个时钟脉冲之前的低电平期间将SDA拉低,并且确保在该时钟的高电平期间为稳定的低电平。 如果接收器是主控器,则在他接受的最后一个字节之后,发送一个NACK信号,来通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。
            SDA上传输的数据必须在SCL为高电平期间保持稳定,SDA上的数据只能在SCL为低电平期间变化。如图:
    在这里插入图片描述

宏定义

#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 */
	
#define EEPROM_I2C_SDA_READ()  GPIO_ReadInputDataBit(EEPROM_GPIO_PORT_I2C, EEPROM_I2C_SDA_PIN)	/* 读SDA口线状态 */

1、起始、结束

在这里插入图片描述

        红框处分别为起始信号和停止信号。

(1)起始信号的产生过程

        保持SCL和SDA为高电平,SDA产生一个下降沿,然后SCL拉低,一个START信号产生。

void i2c_Start(void)
{
	EEPROM_I2C_SCL_1();
	EEPROM_I2C_SDA_1();
	i2c_Delay();
	EEPROM_I2C_SDA_0();
	i2c_Delay();
	EEPROM_I2C_SCL_0();
	i2c_Delay();
}

(3)停止信号的产生过程

        保持SCL为高电平,SDA为低电平,SDA产生一个上升沿,一个STOP信号产生。

void i2c_Stop(void)
{
	EEPROM_I2C_SCL_1();
	EEPROM_I2C_SDA_0();
	i2c_Delay();
	EEPROM_I2C_SDA_1();
}

2、发送

        刚开始主芯片要发出一个start信号,然后发出一个设备地址(用来确定是往哪一个芯片写数据),方向(读/写,0表示写,1表示读)。回应(用来确定这个设备是否存在),然后就可以传输数据,传输数据之后,要有一个回应信号(确定数据是否接受完成),然后再传输下一个数据。每传输一个数据,接受方都会有一个回应信号,数据发送完之后,主芯片就会发送一个停止信号。
在这里插入图片描述

在这里插入图片描述

(1)发送信号的产生过程

        一个CLK对应发送一个bit,先将SCL拉低,然后准备好要发送的数据,然后将SCL拉高,产生一个CLK时钟脉冲,数据就能直接发往从机;其中(ucByte & 0x80)表示从高位(MSB)开始发送直到最低位(LSB),每发送完一个位后ucByte左移一位,确保每一位的数据都能正确发送,最后将SCL重新拉低。

void i2c_SendByte(uint8_t ucByte)
{
	uint8_t i;
	EEPROM_I2C_SCL_0(); /* 首先拉低 */
	for (i = 0; i < 8; i++)
	{
		i2c_Delay();
		if ((ucByte & 0x80) >> 7) /* 准备数据 */
		{
			EEPROM_I2C_SDA_1();
		}
		else
		{
			EEPROM_I2C_SDA_0();
		}
		EEPROM_I2C_SCL_1(); /* 产生一个CLK时钟脉冲, 发送数据 */
		i2c_Delay();
		ucByte <<= 1;	/* 左移一个bit */
		EEPROM_I2C_SCL_0(); /* 重新拉低 */
		i2c_Delay();
	}
}

3、接收

        刚开始主芯片要发出一个start信号,然后发出一个设备地址(用来确定是从哪一个芯片读取数据),方向(读/写,0表示写,1表示读)。回应(用来确定这个设备是否存在),然后就可以传输数据,传输数据之后,要有一个回应信号(确定数据是否接受完成),然后在传输下一个数据。每传输一个数据,接受方都会有一个回应信号,数据发送完之后,主芯片就会发送一个停止信号。
在这里插入图片描述

在这里插入图片描述

(1)接收信号的产生过程

        一个CLK对应发送一个bit,先将SCL拉低,然后先进行数据为的左移,此时由从机发来的数据已经准备好发往主机了,或者说已经到主机家门口了,主机只要将SCL拉高,产生一个SCK信号即一个时钟脉冲,就可以接收到一个bit,然后用EEPROM_I2C_SDA_READ()读取这个bit,bit == 1则加1,bit ==0就不做操作。每次循环都是先左移再接收数据,这种顺序,在第一次左移时,对数据没有影响;在之后的每次循环,可以保证每个bit写入正确的位置,防止数据位错位。等循环结束后,将数据取出,就完成了一次8bit数据的读取。

uint8_t i2c_ReadByte(void)
{
	uint8_t i;
	uint8_t value = 0;
	EEPROM_I2C_SCL_0(); /* 首先拉低 */
	for (i = 0; i < 8; i++)
	{
		i2c_Delay();
		value <<= 1;
		EEPROM_I2C_SCL_1(); /* 产生一个SCK时钟脉冲, 准备接收数据 */
		i2c_Delay();
		if (EEPROM_I2C_SDA_READ())
		{
			value++;
		}
		EEPROM_I2C_SCL_0(); /* 重新拉低 */
		i2c_Delay();
	}
	return value;
}

4、ACK、NACK、读ACK

(1)ACK信号的产生过程

        在大于一个时钟脉冲的时间保持SDA为低电平。

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总线 */
}

(2)NACK信号的产生过程

        在大于一个时钟脉冲的时间保持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();	
}

(3)读取ACK信号的产生过程

        在一个时钟脉冲下完成的。在产生时钟脉冲前,要释放SDA总线,即将SDA拉高。(因为SDA返回0表示正确应答,返回1则表示无IIC设备响应,所以要先保证它在未响应状态),然后给一个时钟脉冲,并判断SDA的值,SDA返回0表示正确应答,返回1则表示无I2C设备响应。

uint8_t i2c_WaitAck(void)
{
	uint8_t re;
	EEPROM_I2C_SDA_1();	/* CPU释放SDA总线 */
	i2c_Delay();
	EEPROM_I2C_SCL_1();	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
	i2c_Delay();
	if (EEPROM_I2C_SDA_READ())	/* CPU读取SDA口线状态 */
	{
		re = 1;
	}
	else
	{
		re = 0;
	}
	EEPROM_I2C_SCL_0();
	i2c_Delay();
	return re;
}

5、检测I2C设备是否存在

        要检测I2C设备是否存在,只需要向设备发送设备地址,然后读取设备是否返回应答信号即可。返回值 0 表示正确即对应地址的设备存在, 返回1表示未探测到I2C设备。

uint8_t i2c_CheckDevice(uint8_t _Address)
{
	uint8_t ucAck;
	i2c_Start();		/* 发送启动信号 */
	/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
	i2c_SendByte(_Address | EEPROM_I2C_WR);
	ucAck = i2c_WaitAck();	/* 检测设备的ACK应答 */
	i2c_Stop();			/* 发送停止信号 */
	i2c_NAck();	/*若输入的是读地址,需要产生非应答信号*/
	return ucAck;
}

6、发送多个8bit的数据

在这里插入图片描述

总体的一个过程:

  1. 主机发送start信号
  2. 主机发送从机地址,高7bit是地址,bit0是读写控制位,0表示写,1表示读
  3. 从机返回ACK响应信号
  4. 主机发送要给从机写入数据的地址(有的设备不用)
  5. 从机返回ACK响应信号
  6. 主机发送数据
  7. 从机返回ACK响应信号
  8. 重复第6和7步,直到从机返回一个NACK非响应信号
  9. 主机发送停止信号,结束数据传输
void i2c_Send2Byte(uint8_t slaveAddr, uint8_t regAddr, uint16_t ucByte)
{
	uint8_t temp = 0;
	
	i2c_Start(); /* 1.发送start信号 */
	
	i2c_SendByte(slaveAddr); /* 2.发送从机地址 */
	i2c_WaitAck(); /* 3.等待从机返回ACK响应信号 */
	
	i2c_SendByte(regAddr); /* 4.发送从机写入数据的地址 */
	i2c_WaitAck(); /* 5.等待从机返回ACK响应信号 */
	
	temp = (uint8_t)(data >> 8);
	i2c_SendByte(temp); /* 6.发送数据 */
	i2c_WaitAck(); /* 7.等待从机返回ACK响应信号 */

	/* 重复第6和第7步 */
	temp = (uint8_t)(data & 0x00FF);
	i2c_SendByte(temp); /* 发送数据  */
	i2c_WaitAck(); /* 等待从机返回ACK响应信号 */

	i2c_Stop(); /* 9.最后发送停止信号, 结束数据传输 */
}

7、读取多个8bit的数据

在这里插入图片描述

总体的一个过程:

  • 主机发送start信号
  • 主机发送从机地址,高7bit是地址,bit0是读写控制位,0表示写,1表示读
  • 从机返回ACK响应信号
  • 主机接收数据
  • 向从机发送ACK响应信号
  • 重复第4和5步,最后向从机发送一个NACK非响应信号
  • 主机发送停止信号,结束数据传输
uint16_t i2c_Read2Byte(uint8_t slaveAddr)
{
	uint16_t temp = 0;
	
	i2c_Start(); /* 1.发送start信号 */
	
	i2c_SendByte(slaveAddr); /* 2.发送从机地址 */
	i2c_WaitAck(); /* 3.等待从机返回ACK响应信号 */
	
	temp = i2c_ReadByte(); /* 4.接收数据 */
	i2c_Ack(); /* 5.向从机发送响应信号 */
	
	/* 重复第4和第5步 */
	temp <<= 8;
	temp |= i2c_ReadByte(); /* 接收数据 */
	i2c_NAck(); /* 向从机发送非响应信号 */

	i2c_Stop(); /* 7.最后发送停止信号, 结束数据传输 */
}

四、总线速度

双向传输总线:

  • 标准模式(Standard-mode):速率高达 100 Kbit/s
  • 快速模式(Fast-mode):速率高达 400 Kbit/s
  • 快速模式+(Fast-mode Plus):速率高达 1 Mbit/s。
  • 高速模式(High-speed mode):速率高达 3.4 Mbit/s

单向传输总线:

  • 超快速模式(Ultra Fast-mode):速率高达 5 Mbit/s

五、链接

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值