STM32(9):I2C总线原理及应用编程

直接上实战。
I2C(IIC,Inter-Integrated Circuit),两线式串行总线,由PHILIPS公司开发用于连接微控制器及其外围设备。
它是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。

IIC是半双工通信方式。

IIC就是一种通信方式。
IIC是MCU里面存在,然后与之通信的器件也存在的。
STM32(9):I2C总线原理及应用编程_第1张图片SDA是数据线,
SCL则是时钟线之类的。

这里直接讲IIC的几种状态。

1.空闲状态
I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

2.起始信号
起始信号:当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。

代码如下

void Iic_Start(void)
{
	//模式输出
	Iic_Mode(GPIO_Mode_OUT);
	
	SCL		 = 1;
	SDA_OUT  = 1;
	delay_us(5);
	
	SDA_OUT = 0;
	delay_us(5);
	
	//SDA与SCL为低钳住总线
	SCL		= 0;

}

这里有个疑惑就是为什么最后SDA也要变成低电平。这里就要说到看图来编程了,嵌入式这一块不仅仅是看文字说明就行了的,还需要看一下波形图。
STM32(9):I2C总线原理及应用编程_第2张图片
看图就会发现,最后SDA会在SCL变低电平之后也变成低电平。

3.停止信号
停止信号:当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
有了上面的经验,这次是要想看波形图的。
STM32(9):I2C总线原理及应用编程_第3张图片

void Iic_Stop(void)
{
	//模式输出
	Iic_Mode(GPIO_Mode_OUT);	
	
	SCL		 = 0;
	SDA_OUT  = 0;
	delay_us(5);
	
	SCL		 = 1;
	delay_us(5);
	
	//SDA与SCL为高电平,总线空闲
	SDA_OUT  = 1;

}

所以可以看出来,其实文字说明是不够详细的,只是有助于理解。这里的话,直接看图,就可以很好的理解代码了。

4.数据有效性
说完了起始信号和停止信号,这比较简单的,就到了怎么传输数据了。这里就会复杂一些,不过同理就行。
2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定(引脚不能改变电平状态),只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化(引脚可以改变电平状态)。
即:数据在SCL的上升沿到来之前就需准备好。并在在下降沿到来之前必须稳定。
STM32(9):I2C总线原理及应用编程_第4张图片

//发送一位数据
void Iic_Send_Ack(u8 ack)
{
	//模式输出
	Iic_Mode(GPIO_Mode_OUT);
	
	SCL		 = 0;
	
	//数据准备
	if(ack == 1)
	{
		SDA_OUT = 1;	
	}
	else
	{
		SDA_OUT = 0;	
	}
	//时钟形成一个脉冲周期,发送一位数据
	delay_us(5);
	SCL = 1;
	delay_us(5);
	SCL = 0;
	

}

嵌入式其实都是根据硬件来写代码的,所以代码本身难度不会很大,主要就是把硬件原理弄懂就行了。这里的话依旧是先看波形图的解释,可以看出来,其实是这么一个解释。
只有在SCL为低电平的时候,SDA才可以改变电平,而改变电平,其实就是在传输数据了。
在SCL高电平的时候,SDA才可以改变电平。
举个例子,你想要传的数据是1,那么就把SCL变低电平,然后把SDA变高电平,如果SDA已经是高电平了,那么SDA就不需要改变。这也是代码前半段的意思。

后半段的意思就是把数据传出去了。形成一个脉冲,就把数据传出去了。

好的,既然一位数据传输已经知道了,那么怎么传输一个字节,也就是八位数据呢?道理都是一样的,触类旁通。

//发送一个字节数据 先传高位,再传低位
void Iic_Send_Byte(u8 data)
{
	u8 i;
	//模式输出
	Iic_Mode(GPIO_Mode_OUT);	
	
	
	SCL		 = 0;
	
	for(i=0; i<8; i++)
	{
		//数据准备
		if(	data   &  1<<(7-i) )
		{
			SDA_OUT = 1;	
		}
		else
		{
			SDA_OUT = 0;	
		}
		//时钟形成一个脉冲周期,发送一位数据
		delay_us(5);
		SCL = 1;
		delay_us(5);
		SCL = 0;	
	
	}
	
	
}

就是在传输一位数据的代码的基础上加上一个for循环就行了。
这里面有一个小细节就是用到了位操作。
data & 1<<(7-i)

嵌入式里面,对于位操作是需要非常熟练的,因为很多情况都是需要去改变电平的嘛。这里用到了与操作。与运算是两个都是1才是1.
1<<(7-i)
这个也很好理解,就是每次都左移一位。

好的,那么说完了怎么发送数据,那么就到了怎么接收数据了,原理都是类似的,还是那句话,触类旁通。
发送一位数据到发送一字节数据的原理是有小到大,
那么发送到接收是什么原理呢,是反过来。反复横跳就是高手了。

那么就是把发送的代码思想反过来就行了。

//接收一位数据
u8 Iic_Recv_Ack(void)
{
	u8 ack;
	
	//模式输出
	Iic_Mode(GPIO_Mode_IN);		
	
	SCL = 0;
	delay_us(5);
	SCL = 1;
	
	if(SDA_IN == 1)
	{
		ack = 1;
	}
	else
	{
		ack = 0;
	}
	delay_us(5);
	SCL = 0;
	
	return ack;
}

//接收一个字节数据 先接高位,再接低位
u8 Iic_Recv_Byte(void)
{
	u8 i, data = 0;  //0100 1010
	
	//模式输出
	Iic_Mode(GPIO_Mode_IN);		
	
	SCL = 0;
	
	for(i=0; i<8; i++)
	{
		delay_us(5);
		SCL = 1;
		
		if(SDA_IN == 1)
		{
			data |= 1<<(7-i);
		}

		delay_us(5);
		SCL = 0;
	}
	
	return data;

}

好的,讲解就到这里。思考的乐趣是最高级的,也是最不需要物质的。

你可能感兴趣的:(STM32)