IIC协议详解

软件模拟IIC程序代码详解

概述:
  通过stm32模拟IIC协议读取传感器86BSD压力传感器的压力值和温度值数据,利用通信波形来深入理解IIC协议。
  MCU-STM32F103,从设备地址0x28,利用逻辑分析仪进行波形分析。

IIC协议简介

I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
● IIC接口介绍
  SDA:串行数据线,总线空闲时为高电平
  SCL:串行时钟线,总线空闲时为高电平
  ★注意:SDA、SCL接口电路为开漏输出,需要接上拉电阻,本实验采用了4.7k的上拉电阻,电路图如下。
在这里插入图片描述
IIC总线特征
1)、IIC总线是真正的多主机总线,每个连接到总线上的器件都有唯一的地址,任何器件既可以作为主机也可以作为从机,但同时刻只允许有一个主机。
2)、连接到相同总线上的IC数量只受总线最大电容的限制,串行的8位双向数据传输位速率在标准模式下可达100Kbit/s,快速模式下可达400Kbit/s,高速模式下可达3.4Mbit/s。
3)、主设备和从设备之间以8bit为单位进行双向传输。

IIC总线协议详解

IIC总线协议的构成
  IIC总线协议构成主要构成如下图所示:
  在这里插入图片描述
  用逻辑分析仪捕捉的传输2Byte数据的IIC协议如下图所示:
在这里插入图片描述下面依次分解。
✍起始条件
  总线上必须以一个起始条件作为数据传输的起点,当SCL为高电平时,SDA由高电平变为低电平时为起始条件,如下图所示。
在这里插入图片描述
说明:起始信号和停止信号总是由主设备发出的。
✍从设备地址+R/W位  
  之前介绍过每个连接到IIC总线上的器件都有唯一的地址,主设备要想与从设备通信就必须先校验从设备地址,7bit从设备地址+1bit读/写位共同构成了1Byte的数据,这1Byte数据在起始条件之后,并在SCL的8个周期内将其放置在了SDA数据线上,如下图所示。
在这里插入图片描述
  ★★★注意:8bit地址数据的D0位位读/写位,D0为1时为主设备向从设备读模式,D0为0时为主设备向从设备写模式,上图中可以看出从设备地址为0x51(0101_0001),实际上该传感器设备的出厂地址为0x28(0010_1000),由此看出D7-D1为设备的出厂地址D0为1,所以此时的模式是向传感器取数据,同时观察逻辑分析仪第三通道同样识别为Read模式。
✍ACK应答  
  应答这个是为了后续通信能继续下去的一个机制,凡是接收数据或命令方,都i要给一个应答位,发送方只有收到应答位后才继续发送。只有一个特殊情况不用应答位,就是主机(给时钟信号的一方)作为接收方时,在收到最后一个字节信息时可以不用应答,所以应答分为主机应答和从机应答。
  从机应答:当一个字节的数据从高到低传输完毕后,从设备将SDA数据线拉低,这样主设备检测到SDA被拉低就相当于收到了一个来自从设备的应答,这样主设备才知道一个字节被正真传输完毕。如下图所示:在这里插入图片描述
  主机应答:主机向从机发送了读数据的指令后,从机必然要向主机返回数据,那么什么作为主机接收到从机数据的标志呢,也是一个应答位,看下图:
在这里插入图片描述✍数据传输:
  SDA的数据在SCL高电平期间被写入从机。所以SDA的数据变化要发生在SCL低电平期间。下图为1Byte数据传输的波形图:
在这里插入图片描述
✍停止条件
  总线上必须以一个停止条件作为数据传输的终点,当SCL为高电平时,SDA由低电平变为高电平时为停止条件,如下图所示。
在这里插入图片描述
说明:起始信号和停止信号总是由主设备发出的。

利用软件模拟IIC协议读取传感器内部的数据

基础函数和对应波形
●起始条件
代码:

void I2C_START(void)
{ 
	/* 当SCL高电平时,SDA出现一个下降沿表示I2C总线启动信号 */
	I2C_SDA_H;
	I2C_SCL_H;
	i2c_Delay();
	I2C_SDA_L;//SDA先产生下降沿
	i2c_Delay();
	I2C_SCL_L;
	i2c_Delay();
}

波形:
在这里插入图片描述
●停止条件

void I2C_STOP(void)
{	
	/* 当SCL高电平时,SDA出现一个上升沿表示I2C总线停止信号 */
	I2C_SDA_L;
	I2C_SCL_H;
	i2c_Delay();
	I2C_SDA_H;//SDA先产生上升沿
}

波形
在这里插入图片描述
●主设备等待来自从设备的应答

//返回值:1:接收应答失败,0:接收应答成功
uint8_t I2C_Wait_Ack(void)
{
	uint8_t re;

	I2C_SDA_H;	/* CPU释放SDA总线 */
	i2c_Delay();
	I2C_SCL_H;	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
	i2c_Delay();
	if (I2C_SDA_STATE !=0)	/* CPU读取SDA口线状态 */
	{
		re = 1;
	}
	else
	{
		re = 0;//从设备主动将SDA拉低,则接收到应答
	}
	I2C_SCL_L;
	i2c_Delay();
	return re;
} 

波形:
在这里插入图片描述
此处的ACK是由从设备产生的,从设备在接收到来自主设备8bit地址信息完毕后将SDA数据线拉低。观察图中SCL的前1-8个脉冲,这8个脉冲是由主设备产生的,主设备在这8个脉冲处完成了8bit地址信息的传输,同时从设备在这8个脉冲处对SDA数据线进行采样,采样的数据为0101_0001即0x51也就是主设备发送给从设备的地址信息。
●主设备应答
代码:

void I2C_ACK(void)
	
{
 	I2C_SDA_L;	/* CPU驱动SDA = 0 */
	i2c_Delay();
	I2C_SCL_H;	/* CPU产生1个时钟 */
	i2c_Delay();
	I2C_SCL_L;
	i2c_Delay();
	I2C_SDA_H;	/* CPU释放SDA总线 */
}

波形:
在这里插入图片描述
图中第二个ACK是由主设备产生的,主设备收到来自从设备1Byte数据后将SDA数据线拉低。观察图中SCL上的①-⑧个脉冲主设备就是在这8个脉冲处对SDA数据线采样,采样得到的8bit数据为0001_1110即0x1F也就是从机返回的数据。再看第⑨个脉冲,从机就是在这个脉冲的时候对SDA数据线采样的,采样得到SDA数据为低(说明我们在这个时钟之前主设备要把SDA拉低,这样从设备才能在正确的采样点得到应答信号),说明收到了主机的应答。要记住SCL、SDA可是两个设备共用的,只是两个设备对SDA数据线上的采样点不同(采样点由SCL时钟线控制),所以就可完成数据的传输。
●主设备非应答
代码:

void I2C_NACK(void)
{
	I2C_SDA_H;
	i2c_Delay();
	I2C_SCL_H;
	i2c_Delay();
	I2C_SCL_L; 
	i2c_Delay();
}

波形:
在这里插入图片描述
主设备在收到最后一个字节信息时就可以不用应答了,所以产生了非应答信号,在第⑨个脉冲之前将SDA拉高,便于从设备在第⑨个脉冲时得到来自主设备的非应答信号。
●时钟信号的产生
通过以上程序中的

i2c_Delay();

延时的使用产生了周期性的SCL时钟。

static void i2c_Delay(void)
{
	uint8_t i;
	//循环次数为10时,SCL频率 = 240KHz 
	for (i = 0; i < 10; i++);
}

通过逻辑分析仪观察IIC通信速率为240Khz。
在这里插入图片描述
●发送一个字节

void I2C_SendByte(uint8_t _ucByte)
{
 	uint8_t i;

	/* 先发送字节的高位bit7 */
	for (i = 0; i < 8; i++)
	{		
		if (_ucByte & 0x80)
		{
			I2C_SDA_H;
		}
		else
		{
			I2C_SDA_L;
		}
		i2c_Delay();
		I2C_SCL_H;
		i2c_Delay();	
		I2C_SCL_L;
		if (i == 7)
		{
			 I2C_SDA_H; // 释放总线
		}
		_ucByte <<= 1;	/* 左移一个bit */
		i2c_Delay();
	} 
}

●接收一个字节并作出应答

uint8_t I2C_ReceiveByte(u8 ack)
{
	uint8_t i;
	uint8_t value;

	/* 读到第1个bit为数据的bit7 */
	value = 0;
	for (i = 0; i < 8; i++)
	{
		value <<= 1;
		I2C_SCL_H;
		i2c_Delay();
		if (I2C_SDA_STATE !=0)
		{
			value++;
		}
		I2C_SCL_L;
		i2c_Delay();
	}
	if(ack==0)
		I2C_NACK();
	else
		I2C_ACK();
	return value;
}

--------------手动分割-------------------------以下为主设备对从设备一次数据的获取流程-------------这才是重点↓----------------------

查看本传感器手册,看中文就行。传感器手册一共给了4种提取数据的模式。可以直接跳过图片了 ↓
在这里插入图片描述
把四种模式总结了一下:
数据提取
模式1:
测试传感器是否应答,地址信息D0为1(主设备向从设备读数据)。
在这里插入图片描述
主机发送从设备地址后等待应答:

void _86BSD_ReadData(unsigned char*Read)
{
	unsigned char i;
	I2C_START();
	I2C_SendByte(0x51);//从机的地址	//0101 0001
	I2C_Wait_Ack();//等待从机响应
	I2C_STOP();
}

逻辑分析仪捕捉波形:
在这里插入图片描述
观察到波形,从机已经产生应答。
模式2: 主设备向从设备读取两个字节的数据。
在这里插入图片描述

void _86BSD_ReadData(unsigned char*Read)
{
	unsigned char i;
	I2C_START();
	I2C_SendByte(0x51);//从机的地址	//0101 0001
	I2C_Wait_Ack(); //等待从机响应
	/*气压*/
	*Read=I2C_ReceiveByte(1);//1  主机应答
	 Read++;
	*Read=I2C_ReceiveByte(0);//0  主机非应答
	I2C_STOP();
}

逻辑分析仪捕捉波形:
在这里插入图片描述
观察波形,从机收到地址信息后产生应答ACK,随之返回数据0x1E,主机收到数据后产生应答ACK,从机又返回数据0x1C,主机收到数据后不再需要从机的数据所以产生非应答NACK。
模式3: 主设备向从设备读取三个字节的数据。
在这里插入图片描述

I2C_START();
	I2C_SendByte(0x51);//从机的地址	//0101 0001
	I2C_Wait_Ack();//等待从机应答
	/*气压*/
	*Read=I2C_ReceiveByte(1);//1  主机应答
	 Read++;
	*Read=I2C_ReceiveByte(1);//1  主机应答
	 Read++;
	*Read=I2C_ReceiveByte(0);//0  主机非应答
	I2C_STOP();

逻辑分析仪捕捉波形:在这里插入图片描述
观察波形,从机收到地址信息后产生应答ACK,随之返回数据0x1E,主机收到数据后产生应答ACK,从机又返回数据0x1C,主机收到数据后产生应答ACK,从机又返回数据0x64,主机收到数据后不再需要从机的数据所以产生非应答NACK。
模式4: 主设备向从设备读取四个字节的数据。
在这里插入图片描述

void _86BSD_ReadData(unsigned char*Read)
{
	unsigned char i;
	
	I2C_START();
	I2C_SendByte(0x51);//从机的地址	//0101 0001
	I2C_Wait_Ack();
	/*气压*/
	*Read=I2C_ReceiveByte(1);//1  主机应答
	 Read++;
	*Read=I2C_ReceiveByte(1);//1  主机应答
	 Read++;
	/*温度*/
	*Read=I2C_ReceiveByte(1);//1  主机应答
	 Read++;
	*Read=I2C_ReceiveByte(0);//0  主机非应答
	I2C_STOP();
}

逻辑分析仪捕捉波形:
在这里插入图片描述
观察波形,从机收到地址信息后产生应答ACK,随之返回数据0x1E,主机收到数据后产生应答ACK,从机又返回数据0x1C,主机收到数据后产生应答ACK,从机又返回数据0x64,主机收到数据后产生应答ACK,从机又返回数据0xC3,主机收到数据后不再需要从机的数据所以产生非应答NACK。
以上就是举例该传感器的4种模式,便于深入理解IIC协议*–*

有兴趣可以看看
类似的SPI协议解析

★★★如有错误欢迎指导。

猜你喜欢

转载自blog.csdn.net/qq_40147893/article/details/106762340