STM32软件IIC配置及MPU6050演示教程详解

IIC说明:
IIC是一种通信协议,IIC 总线是一种用于IC器件之间连接的二线制总线,有主机和从机,二者可以互相通信,可以一主多从也可以多主多从,有时钟线(SCL)和数据线(SDA),SDA线既可以被主机控制也可以被从机控制,但SCL线只能由主机控制。
软件IIC配置:
总体操作:
1.初始化GPIO,包括打开时钟,配置结构体,初始化选用的引脚
2.配置IIC开始函数
3.配置IIC结束函数
4.配置IIC发送一个字节函数
5.配置IIC接收一个字节函数
6.配置IIC发送应答函数
7.配置IIC接收应答函数
具体操作:
1.初始化GPIO,例如,选用Pin10为SCL线,Pin11为SDA线,配置IIC的GPIO为开漏输出

void MyI2C_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11);
}

1.1IIC不论是SDA还是SCL都只有高低两种状态,所有使用时就是把GPIO电平配置为高或者低
例如:

	GPIO_WriteBit(GPIOB,GPIO_Pin_10,1);
	Delay_us(10);
	GPIO_WriteBit(GPIOB,GPIO_Pin_11,1);
	Delay_us(10);

延时10微秒为操作时间,实测不延时也可以
但这样,每次都配置GPIO不仅麻烦还不明了
所以就把GPIO封装起来

void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);
	Delay_us(10);
}//对SCL线封装,便于操作,控制时钟线
void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);
	Delay_us(10);
}//对SDA线封装,便于操作,发送主机值
uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
	Delay_us(10);
	return BitValue; 
}//对SDA线封装,便于操作,读取从机发送的值

封装完以后便可以很简单的配置后面的函数
2.配置开始函数
IIC开启需要在SCL为高的时候拉低SDA,这样为开启IIC信号,从机便知道,IIC开启,主机要发送或者接收数据了

void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);
	/*最好先拉高SDA确定,简要原因在最后面的读MPU6050的注释里面有简要说明,
	这样只是一个以防外一,个人感觉不是特别重要*/
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);//拉低SDA后再把SCL拉低,便可以进行数据传送

3.配置结束函数 IIC结束需要在SCL为高的时候拉高SDA,这样为关闭IIC信号,从机便知道,IIC关闭,主机要结束发送或者接收数据了,实际上,在配置的IIC函数里面,只有结束函数里面SCL以高结束,其他的都为低,这样方便两个函数衔接。

void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);//先拉低SDA确保待会可以产生上升沿
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}

4.配置主机发送函数
主机在SCL低的时候只会发送一位,从机在SCL为高的时候一次也只接收一位,每次都是一位一位进行的

void MyI2C_SentByte(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);
	}
}

一次发送八位数据也就是一个字节的数据,所以在for循环里面循环8次
在8次数据发送完以后,主机需要释放SDA,这时从机会自己占据SDA线给主机发送应答后面写的主机的接收应答就会主动去释放SDA(所有的发送和接收都是相对主机而言的)
5.配置主机接收函数

uint8_t MyI2C_ReceiveByte(void)//主机接收时,从机在时钟线拉低的时候只会发送一位数据
{
	uint8_t i,Byte=0x00;
	MyI2C_W_SDA(1);
	for(i=0;i<8;i++)
	{ 
		MyI2C_W_SCL(1);
		if(MyI2C_R_SDA()==1){Byte |= (0x80>>i);}//高位先行,所以右移
		MyI2C_W_SCL(0);
	}
	return Byte;
}

主机接收的数据要处理,所以要用变量存起来,发送的数据从机会自动处理
6.配置IIC发送应答

void MyI2C_SentAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);//当发送完一个数据以后,SCL本身就是低的,所以前面不需要再给SCL低了
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}

发送应答是主机接收了一个数据以后发送个主机的应答,SDA拉高,相当于主机发送1,为非应答,不需要再接收数据时就要发非应答,需要接收数据时,就在SDA拉低,为应答。从机发送完一个字节数据以后,会自动释放SDA,此时主机应占据SDA,从机便会去读取SDA的值,接收主机的应答(应答信号在第9个时钟周期出现,这时发送器必须在这一时钟位上释放数据线,由接收设备拉低SDA电平来产生应答信号或非应答信号)
7.配置IIC接收应答

uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;
	MyI2C_W_SDA(1);//主机主动空出SDA,从机会立刻占据,发送应答或者非应答信号
	MyI2C_W_SCL(1);//SCL拉高以后,主机便可以去读取从机给的信号
	AckBit=MyI2C_R_SDA();
	MyI2C_W_SCL(0);//放手SDA
	return AckBit;
}

在主机发送完数据以后,主机应空出SDA线,此时从机会产生应答或者非应答,因为是软件模拟IIC所以可以选择读取也可以不去读取,选择读取便可以根据读取的值判断下一步要不要再继续操作。因为一个时钟信号只进行一位传输,所以从机检测到电平变化以后如果接下来还是主机操作便不在占据SDA。

以上便是软件IIC的所有配置

以MPU6050为例,演示IIC的进行
MPU6050初始化即IIC初始化
如果要给MPU6050写一个字节的数据:

void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data)
{
	MyI2C_Start();//打开IIC通信
	MyI2C_SentByte(0xD0);//选中MPU6050,最后一位为0为写操作
	MyI2C_ReceiveAck();//从机发送应答,主机要接收应答
	MyI2C_SentByte(RegAddress);//主机继续发送要写的寄存器地址
	MyI2C_ReceiveAck();//主机接收从机的应答
	MyI2C_SentByte(Data);//主机发送要写的数据
	MyI2C_ReceiveAck();//主机接收从机的应答
	MyI2C_Stop();//停止IIC通信SDA与SCL都为高
}

IIC开始以后,第一次发送的是硬件的地址,每一个硬件都有一个地址,出厂的时候写好的,发送的数据前七位为地址,最后一位为读写位,0为写,1为读。
如果要读MPU6050一个字节的数据:

uint8_t MPU6050_ReadReg(uint8_t RegAddress)//读指定寄存器
{
	uint8_t Data;//接收读出数据的变量
	MyI2C_Start();
	MyI2C_SentByte(0xD0);
	MyI2C_ReceiveAck();
	MyI2C_SentByte(RegAddress);
	MyI2C_ReceiveAck();//前面几步确定地址
	MyI2C_Start();
	/*Start里面先SDA置1就是这里在上一步SCL为0的时候赶快为高然后重新开始,
	避免还没为高的时候SCL已经拉高了。
	如果SDA还没为高的时候SCL已经拉高了,这样再产生下降沿之前产生的就是上升沿了,就是停止的意思了。
	但是接收应答后,从机释放SDA,此时SDA就是高主机没有进行操作,SDA一直为高,所以个人感觉不重要*/
	MyI2C_SentByte(0xD1);//对指定地址进行读
	MyI2C_ReceiveAck();//从机要回应这个指令
	Data=MyI2C_ReceiveByte();//从机把指定地址的数据通过SDA线发出来
	MyI2C_SentAck(1);//主机回应1表示不给应答,从机便会结束发送
	MyI2C_Stop();//结束通信
	return Data;
}

因为无法直接指定寄存器读,但可以指定寄存器写,指定的地址指针在下一次操作前不变,所以指定地址写,然后什么都不写,重新开始读,便可以指定地址读

物联沃分享整理
物联沃-IOTWORD物联网 » STM32软件IIC配置及MPU6050演示教程详解

发表评论