I2C通信协议

本文详细解释了I2C总线接口的基本时序规则,包括起始和停止条件的具体实现,以及如何进行数据的发送和接收,包括I2C起始、停止、发送数据、接收数据、应答等操作的代码示例。以MPU6050为例,展示了完整的数据帧传输过程。
摘要由CSDN通过智能技术生成

I2C时序基本单元

I2C起始条件

起始条件:SCL高电平期间,SDA从高电平切换到低电平

代码示例

void MYI2C_Start(void)
{
	//空闲状态SDA和SCL均为高电平
	MYI2C_W_SDA (1);	
	MYI2C_W_SCL (1);
	//SCL高电平期间,拉低SDA。再拉低SCL
	MYI2C_W_SDA (0);
	MYI2C_W_SCL (0);
}

注意: 这里先拉高SDA再拉高SCL是防止读成停止信号(SCL高电平期间,SDA拉高)。

I2C停止条件

起始条件:SCL高电平期间,SDA从低电平切换到高电平

代码示例 

void MYI2C_Stop(void)
{
	
	MYI2C_W_SDA (0);	
	//SCL高电平期间拉高SDA再拉高SCL
	MYI2C_W_SCL (1);
	MYI2C_W_SDA (1);	
}

 注意: 这里先拉低SDA目的是在发送一个数据位时,SCL高电平期间从机读取SDA上的数据,发送完成后SCL拉低。在接收一个数据位时,主机在SCL高电平期间读取SDA上的数据,读取完成后拉低SCL。两个过程中SCL均被拉低,但是SDA上的电平未知,所以确保SDA为低电平,首先拉低SDA。

I2C发送一个字节

        I2C发送时,主机对SCL和SDA有控制权,SCL在低电平期间,主机将数据一位一位放置在SDA上(高位先行)。然后主机释放SCL,从机在SCL高电平期间读取一位数据,读取完成后主机再拉低SCL等待下一位数据放置在SDA上。将此过程重复8次,一个字节的数据就发送完成。

        简化过程就是,主机拉低SCL,把数据放置在SDA上。主机释放SCL,从机读取SDA上的数据,循环八次就完成了一个字节的发送。

注意:

        ①在从机读取时,SDA不允许有电平变化。

        ②主机释放SCL时,从机应该尽快读取数据。

        ③主机在发送过程中对SCL和SDA有控制权,从机只能被动读取。

代码示例 

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);
	}
}

I2C接收一个字节

        I2C接收数据时,主机在接收前应该释放SDA,从机拥有SDA控制权,主机仍有SCL的控制权。SCL低电平期间,从机将数据一位一位的放置在SDA线上(高位先行)。主机释放SCL,主机将在SCL高电平期间读取数据位,此过程循环八次即可接收一个字节。

注意:

        ①在主机读取时,SDA不允许有电平变化。

        ②从机数据电平变换要在SCL低电平期间尽快操作。

代码示例

uint8_t MYI2C_ReceiveByte(void)
{
	MYI2C_W_SDA (1);	//主机释放SDA
	uint8_t i,Byte=0x00;
	for(i=0;i<8;i++)
	{
		MYI2C_W_SCL (1);	//SCL高电平期间主机接收SDA上的数据
		
		if(MYI2C_R_SDA ()==1)
		{
			Byte |=(0x80>>i);
		}
		MYI2C_W_SCL (0);
	}
	return Byte ;			//返回接收到的数据
}

I2C发送应答 

        主机在接收完一个字节的数据之后,在下个时钟发送一位数据,数据0表示应答,数据1表示非应答。

代码示例 

void MYI2C_SendAck(uint8_t AckBit)
{
	MYI2C_W_SDA (AckBit);
	MYI2C_W_SCL (1);
	MYI2C_W_SCL (0);
}

I2C接收应答

        主机在发送一个字节数据后,在下个时钟发送一位数据,判断从机是否应答。主机在接收之前,应该把SDA释放,将SDA的控制权交给从机。从机在应答位对SDA进行操作(数据0表示应答,数据1表示非应答)。主机在SCL高电平期间读取SDA上的数据,来判断从机是否收到字节数据。同时主机在接收到应答位后,主机还需发送一个发送应答,目的是告诉从机是否要继续发送数据

代码示例

uint8_t MYI2C_ReceiveAck(void)
{
	uint8_t AckBit;
	MYI2C_W_SDA (1);	//主机释放SDA
	MYI2C_W_SCL (1);	//SCL高电平期间主机接收SDA上的数据
	AckBit=MYI2C_R_SDA ();	//将读取的数据赋值给AckBit
	MYI2C_W_SCL (0);
	return AckBit;
}

 

I2C完整数据帧

(这里以MPU6050为例)

指定地址写

        对指定设备的指定地址下写入数据。

        ①在SCL高电平期间拉低SDA产生起始条件。

        ②发送设备地址然后对指定设备开始读写用于确定通信对象和判断写入还是读出操作)。字节内容必须是从机地址+读/写位(从机地址是7位,读写位1位,读写位置0表示写入数据)。

        ③接收应答。主机释放SDA,SDA回弹置高电平,从机应答需要把SDA置低电平,应答结束后从机释放SDA(方便主机把下一位数据放置在SDA上),SDA置高电平。

        ④根据设备不同来发送寄存器地址/指令控制字。

        ⑤接收应答位

        ⑥在指定设备下的指定地址发送数据

         ⑦接收应答(接收应答结束后把SDA拉低,为后续SDA置高电平准备条件)

         ⑧停止条件       

代码示例

void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	MyI2C_Start();						//I2C起始
	MyI2C_SendByte(MPU6050_ADDRESS);	//发送从机地址,读写位为0,表示即将写入
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_SendByte(RegAddress);			//发送寄存器地址
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_SendByte(Data);				//发送要写入寄存器的数据
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_Stop();						//I2C终止
}

当前地址读 

        对指定设备,在当前地址

        ①在SCL高电平期间拉低SDA产生起始条件。

        ②发送设备地址(对指定设备开始读写用于确定通信对象和判断写入还是读出操作)。字节内容必须是从机地址+读/写位(从机地址是7位,读写位1位,读写位置1表示读取数据)。

        ③接收应答位

        ④主机开始接收一个字节。所以主机释放SDA的控制权,从机控制SDA,主机读取SDA上的数据。

        ⑤接收应答位。(接收应答结束后把SDA拉低,为后续SDA置高电平准备条件)

        ⑥停止条件 

 指定地址读

        对于指定设备,在指定地址下读取从机数据。(指定地址写+当前地址读)

         ①在SCL高电平期间拉低SDA产生起始条件。

        ②发送设备地址然后对指定设备开始读写用于确定通信对象和判断写入还是读出操作)。字节内容必须是从机地址+读/写位(从机地址是7位,读写位1位,读写位置0表示写入数据)。

        ③接收应答位。主机释放SDA,SDA回弹置高电平,从机应答需要把SDA置低电平,应答结束后从机释放SDA(方便主机把下一位数据放置在SDA上),SDA置高电平。

        ④根据设备不同来发送寄存器地址/指令控制字。

        ⑤接收应答位

        ⑥重新置起始条件。

        ⑦发送设备地址(对指定设备开始读写用于确定通信对象和判断写入还是读出操作)。字节内容必须是从机地址+读/写位(从机地址是7位,读写位1位,读写位置1表示读取数据)。

        ⑧接收应答位

        ⑨主机开始接收一个字节。所以主机释放SDA的控制权,从机控制SDA,主机读取SDA上的数据。

        发送应答

        ⑪停止条件。

代码示例

uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	MyI2C_Start();						//I2C起始
	MyI2C_SendByte(MPU6050_ADDRESS);	//发送从机地址,读写位为0,表示即将写入
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_SendByte(RegAddress);			//发送寄存器地址
	MyI2C_ReceiveAck();					//接收应答
	
	MyI2C_Start();						//I2C重复起始
	MyI2C_SendByte(MPU6050_ADDRESS | 0x01);	//发送从机地址,读写位为1,表示即将读取
	MyI2C_ReceiveAck();					//接收应答
	Data = MyI2C_ReceiveByte();			//接收指定寄存器的数据
	MyI2C_SendAck(1);					//发送应答,给从机非应答,终止从机的数据输出
	MyI2C_Stop();						//I2C终止
	
	return Data;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值