详解IIC的通信逻辑

由51仿制的IIC可以很方便的帮助我们学习IIC。

IIC有3种传输模式:

  • 标准模式:100K bit/s
  • 快速模式:400K bit/s
  • 高速模式:3.4M bit/s

主要的传输,通讯线SDA,时钟线SDL,由于时钟线的存在所以IIC协议为半双工协议。

全双工:允许A向B传输数据,同时允许B向A传输数据。

半双工:允许A向B传输数据,允许B向A传输数据,但两者不能同时进行。

单工:允许A向B传输数据,不允许B向A传输数据。

下面是他的起始和终止信号

首先时钟线和数据线默认情况下都是高电平,此时为空闲状态。

起始信号为在时钟信号高电平时,数据信号从高到低

在起始位发送完毕后,下面就开始发送数位了。

在数据为发送时我们首先要保证时钟线处于低电平。

思考为何时钟线一定是低电平呢,在上面的起始位和中止位中我们可以看到,都是在时钟线处于高电平时完成的,所以我们需要将发送的数据与起始位和终止位区分开,那让时钟线处于低电平时才允许SDA改变当前的状态就很好的解决了这个问题。

 以下为一帧标准的写数据帧:

 可以看到我们在传输完起始位后传输的是地址位,通常为7位,还有1位读写信号位,它是如何传输的呢,首先我们需要在SCLK(时钟线)为低电平的时候传输数据,在时钟线为高电平时让外设读取数据,传输8位数据后会得到一个应答信号。

 如上是在传输一个7位的地址位,都是在SCL为低电平时信号线SDA发生的跳变。

下面还需要一个读写数据位,通常情况下,我们需要读数据时需要给下一位置1,写数据时置0。

接下来就是应答信号的接受,当从机收到数据后会返回一个0,未收到或主机读取接受完成返回1。

之后就是寄存器地址位,一共8位的话可以访问256个地址,每个地址对应一个字节的存储空间,那我们就可以向256个字节的空间中写入数据。

 在写入地址或写入数据后从机都会返回一个应答信号,通常0代表写入成功,最后我们需要发送一个停止位,代表数据写入操作完成。

标准的读数据帧如上,首先到发送地址和写数据很相似,后面我们需要再次发送一次起始位之后再传输一次地址和读写位,之后从机便会主动将地址中的内容传输到单片机。关于这个部分的理解为:读取数据之前,要先由主机指派操作地址到从机。

需要注意,SDA在接受应答信号和向外发送地址或数据信号时都需要改变SDA引脚的输入输出设置。

以下为在使用单片机进行通信时的常用代码。


/*
	设置SDA总线为输出模式
	参数值:NULL
	返回值:NULL
*/

void IIC_setSDAMode_Out()
{
	GPIO_InitTypeDef GPIO_IIC;	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
	
	GPIO_IIC.GPIO_Mode  = GPIO_Mode_OUT;				//输出
	GPIO_IIC.GPIO_OType = GPIO_OType_PP;				//推挽
	GPIO_IIC.GPIO_Pin   = GPIO_Pin_15;		            //引脚
	GPIO_IIC.GPIO_PuPd  = GPIO_PuPd_UP;   				//上拉
	GPIO_IIC.GPIO_Speed = GPIO_Speed_25MHz;				//输出
	GPIO_Init(GPIOE, &GPIO_IIC);
}


/*
	设置SDA总线为输入模式
	参数值:NULL
	返回值:NULL
*/

void IIC_setSDAMode_In()
{

	GPIO_InitTypeDef GPIO_IIC;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
	
	GPIO_IIC.GPIO_Mode  = GPIO_Mode_IN;					 //输出
	GPIO_IIC.GPIO_Pin   = GPIO_Pin_15;		             //引脚
	GPIO_IIC.GPIO_PuPd  = GPIO_PuPd_UP;   				 //上拉
	GPIO_Init(GPIOE, &GPIO_IIC);

}

/*
	IIC开始信号
	参数值:NULL
	返回值:NULL
*/

void IIC_Start()
{
		IIC_setSDAMode_Out();
		
		IIC_SDA_OUT(1);                             //总线释放状态
		IIC_SCL_OUT(1);
		delay_us(5);
	
		IIC_SDA_OUT(0);                             //SDA跳变为低电平
		delay_us(5);
	
		IIC_SCL_OUT(0);        
	  delay_us(5);



}

/*
	IIC停止信号
	参数值:NULL
	返回值:NULL
*/

void IIC_Stop()
{
		IIC_setSDAMode_Out();
		
		IIC_SDA_OUT(0);          
		IIC_SCL_OUT(0);
		delay_us(5);
	
		IIC_SCL_OUT(1);                              //SDA跳变为高电平
		delay_us(5);
	
		IIC_SDA_OUT(1);        
	  delay_us(5);

}

/*
	主机写入数据到外设中
	参数值:
				 data  要写入的一个字节
	返回值:NULL
*/

void IIC_writeByte(u8 data)
{
	
	IIC_setSDAMode_Out();
	
	IIC_SCL_OUT(0);                                 //只有时钟线拉低,SDA上的数据才允许写入
	delay_us(5);
	
	//将数据一位一位的发出去
	for(int i =0;i<8;i++)
	{
	
		if(data&(0x1<<(7-i)))               //高位先入
			{
					IIC_SDA_OUT(1);
			}
			else
			{
					IIC_SDA_OUT(0);
			}
			
			 IIC_SCL_OUT(1);                 //让外设读取数据
			 delay_us(5);
		
			 IIC_SCL_OUT(0);                 //重新拉低,准备写入下一位数据
			 delay_us(5);
	}
}



/*
	主机从外设中读取一个字节的数据
	参数值:NULL
	返回值:NULL
*/

u8 IIC_readByte()
{

	
  u8 data = 0;
	IIC_setSDAMode_In();

	IIC_SCL_OUT(0);                  //先拉低,为读取数据做准备
	delay_us(5);

	for(int i=0;i<8;i++)
	{
	
			IIC_SCL_OUT(1);         // SCL为高期间才可以读取数据
			delay_us(5);
		
		if(IIC_SDA_IN)
		{
				data|=(0x01<<(7-i));
			
		}else{
			data &= ~(0x1<<(7-i));
		}	
		IIC_SCL_OUT(0);
		delay_us(5);
	}
	return data;


}



/*
	主机等待应答
	参数值:NULL
	返回值:ack     0  应答   1 不应答
*/


u8 IIC_waitAck()
{
	
	u8 ack =0;
	IIC_setSDAMode_In();

	IIC_SCL_OUT(0);             //准备时序
	delay_us(5);
	
	IIC_SCL_OUT(1);
	delay_us(5);
	

	if(IIC_SDA_IN)
	{
				ack =1;
	}
	else
	{
				ack =0;	
	}
	
	
	IIC_SCL_OUT(0);              //拉低,表示应答完成
	delay_us(5);
	
	
	return  ack;


}

/*
	主机主动应答
	参数值:
				ack  0 应答 1 不应答
	返回值:NULL
*/


void IIC_Ack(u8 ack)
{
	IIC_setSDAMode_Out();
	IIC_SCL_OUT(0);
	delay_us(5);
	
	if(ack)
	{
		IIC_SDA_OUT(1);
	}
  else
	{
	
		IIC_SCL_OUT(0);
	}
	
	
	IIC_SCL_OUT(1);
	delay_us(5);
	
	IIC_SCL_OUT(0);
	delay_us(5);	
	
}

以下为本内容的参考连接。

IIC协议超详细解释(适合小白入门)_iic通信协议_板栗阿叔的博客-CSDN博客

4分钟看懂!I2C通讯协议 最简单的总线通讯!_哔哩哔哩_bilibili

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值