I2C通信协议

一、基本概念      

        由于串口通信属于点对点的通信,若通信设备过多会使得线路复杂,因此多设备的通信采用总线进行,如:usb,spi,can,i2c等。i2c通信 有SCL(时钟线),SDL(数据线)这两条通信线。同步,半双工模式。

注:

1.SCL和SDL上的设备都要设置成开漏输出模式

2.SCL和SDL都要加上一个上拉电阻,阻值为4.7KΩ左右。

3.主设备:控制SCL时钟线,产生时钟产生起始和终止信号

4.每个从设备都有一个唯一的设备地址,设备地址有7位和10位的地址,在设备的厂家手册里面可以找到。若一模一样的设备挂载在总线上,可以通过可变地址位进行区分。(一般情况,高位由厂商确定,低位可以通过控制引脚的高低电平改变)。

这样设置的理由:上拉电阻可以使得两条通信线上面的电平默认保持高电平,开漏输出使得两条通信线上的电平可以被拉低。这样做可以起到保护电源,避免引脚模式的频繁切换的作用。(协调主从设备之间的输入输出问题,由于采用半双工模式,若不这样做,容易造成设备之间的短路。)这里要注意,两条通信线上只要任意设备拉低了某条线的电平,这一整条线电平都会拉低。

二、数据帧

1.基本时序单元:

①起始信号:SCL-高电平,SDA-下降沿

②停止信号:SCL-高电平,SDA-上升沿

③逻辑1:SDA-高

④逻辑0:SDA-低

注:数据0/1的发送由SDA控制。

⑤发送一个字节:SCL低电平期间,主机将数据位依次放在SDA线上(高位先行),然后释放SCL,从机在拉高的上升沿瞬间接收SDA的数据位,依次循环8次即可发送一个字节。

注:在SCL高电平期间,不可以改变SDA的数据,在低电平期间可以改变。

⑥接收一个字节:SCL低电平期间,从机将数据位依次放在SDA线上(高位先行),然后释放SCL,主机在拉高的上升沿瞬间接收SDA的数据位,依次循环8次即可接收一个字节。

注:同上。此外,主机对SDA有绝对的控制权,在主机发送给从机数据时就让主机管着,但是当主机接收从机的数据时,由于总线“线与”的特点,为了判断从机发送的数据,需要主机释放SDA

【注:SCL低电平写,SCL高电平读】

⑦发送应答:主机在接收一个字节后,主机立马在下一个时钟发送一位数据给从机(在SCL高电平读取应答位),判断主机是否应答,若从机发送数据得到主机应答则继续发送,若没得到主机应答则停止发送。数据0表示应答,1表示不应答。

⑧接收应答:主机在发送完一个字节之后,主机立马在下一个时钟接收一位数据(在SCL高电平读取应答位),判断从机是否应答,若从机接收到主机的数据则应答,若没接收到则不应答。数据0表示应答,1表示不应答。(主机在接收应答位之前,要释放SDA)

通俗的讲:

从机接收主机发送来的数据时,要跟接收应答,告诉主机接收到了。->主发从收

主机接收从机发送来的数据时,要跟发送应答,告诉从机发送到了。->主收从发

2.读/写数据帧格式:

 (1)指定地址写:

定义:对指定设备,在指定的寄存器,写入指定数据。

格式:起始位 + 指定设备地址(Slave Address-7位)+ 读/写位(0写,1读) + 应答位RA(Receive ACK) + 指定寄存器地址(Reg Address-8位) + 指定数据(Data-8位) + 应答位RA(Receive ACK) + 停止位

 (2)当前地址读:

定义:对指定的设备,在当前地址指针下,读取从机数据。

格式:起始位 + 指定设备地址 + 读/写位 + 应答位 + 指定数据 + 应答位 + 停止位

注:这里的当前地址从0开始,每读一次地址自增1。

 (3)指定地址读:

定义:对指定设备,在指定寄存器,读取从机数据。

格式:起始位 + 指定设备地址 + 读/写位 + 应答位 + 指定寄存器地址 + 应答位 + 起始位 + 指定设备地址 + 读/写位 + 应答位 + 接受数据 + 停止位(指定地址写但还没来得及写数据+当前地址读的复合)

注:因为读不能指定寄存器的地址,利用读操作,将指针移至指定的寄存器位置,然后对当前指针进行读操作。此外,为了让读操作停止下来,最后的一个应答位要给1不应答!!!

 三、stm32利用I2C对MPU6050进行通信(软件)

【注:以下程序是通过软件编写写I2C通信,通过控制引脚的高低电平来控制SCL和SDA总线的数据。所以,SCL和SDA可以是任意引脚。并且引脚采用通用开漏输出模式。】

 1.编写I2C通讯:MyI2C.C

①.对SCL总线写字节:MyI2C_W_SCL(uint8_t BitValue)

                                   利用GPIO_WriteBit( )对SCL对应的引脚写电平

②.对SDA总线写字节:MyI2C_W_SDA(uint8_t BitValue)

                                   利用GPIO_WriteBit( )对SDA对应的引脚写电平

③.接收SDA总线的字节:uint8_t MyI2C_R_SDA(void)

                                   利用GPIO_ReadInputDataBit( )获取SDA上每一位的数据

④.初始化I2C总线:MyI2C_Init( )

                              将SCL和SDA总线对应的GPIO全部设置成开漏输出模式

⑤.起始数据帧: MyI2C_Start( )

⑥.停止数据帧:MyI2C_Stop( )

⑦.发送一个字节:MyI2C_SendByte(uint8_t Byte)

⑧.接收一个字节:uint8_t MyI2C_ReciveByte(void)

⑨.发送应答位:MyI2C_SendAck(uint8_t AckBit)

⑩.接收应答位:uint8_t MyI2C_ReceiveAck(void)

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

/*引脚配置层*/

/**
  * 函    数:I2C写SCL引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SCL的电平,范围0~1
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCL为低电平,当BitValue为1时,需要置SCL为高电平
  */
void MyI2C_W_SCL(uint8_t BitValue)
{
    GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);//对PB10进行引脚电平设置,也就是I2C_SCL
    Delay_us(10);
}

/**
  * 函    数:I2C写SDA引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SDA的电平,范围0~0xFF
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SDA为低电平,当BitValue非0时,需要置SDA为高电平
  */
void MyI2C_W_SDA(uint8_t BitValue)
{
    GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);
    Delay_us(10);
}

/**
  * 函    数:I2C读SDA引脚电平
  * 参    数:无
  * 返 回 值:协议层需要得到的当前SDA的电平,范围0~1
  * 注意事项:此函数需要用户实现内容,当前SDA为低电平时,返回0,当前SDA为高电平时,返回1
  */
uint8_t MyI2C_R_SDA(void)
{
    uint8_t BitValue;
    BitValue=GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
    Delay_us(10);
    return BitValue;
}

/**
* 函    数:I2C初始化->对SCL和SDA的引脚的GPIO口进行初始化,这里按照stm32的规则推荐,这是为开漏输出模式
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,实现SCL和SDA引脚的初始化
  */
void MyI2C_Init(void)
{
    //开启RCC时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    //初始化GPIOB
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB,&GPIO_InitStruct);
    //设置默认电平->都置高电平,释放总线
    GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11);
}

/*协议层*/

/**
  * 函    数:I2C起始
  * 参    数:无
  * 返 回 值:无
  */
void MyI2C_Start(void)
{
    MyI2C_W_SCL(1);
    MyI2C_W_SDA(1);
    MyI2C_W_SDA(0);
    MyI2C_W_SCL(0);
}

/**
  * 函    数:I2C终止
  * 参    数:无
  * 返 回 值:无
  */
void MyI2C_Stop(void)
{
    MyI2C_W_SDA(0);
    MyI2C_W_SCL(1);
    MyI2C_W_SDA(1);
    //MyI2C_W_SCL(0);//这里可以拉低也可以不拉低,因为停止位在应答位后面,应答位会拉低SCL
}

/**
  * 函    数:I2C发送一个字节
  * 参    数:Byte 要发送的一个字节数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void MyI2C_SendByte(uint8_t Byte)
{
    uint8_t i;
    for(i=0;i<8;i++)
    {
        MyI2C_W_SDA(Byte&(0x80>>i));//取出Byte的每一位并且依次写入
        MyI2C_W_SCL(1);
        MyI2C_W_SCL(0);
    }
}

/**
  * 函    数:I2C接收一个字节
  * 参    数:无
  * 返 回 值:接收到的一个字节数据,范围:0x00~0xFF
  */
uint8_t MyI2C_ReciveByte(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);}//如果接收到的数据位等于1就给Byte对于的位写1,不是就跳过保持0
        MyI2C_W_SCL(0);
    }
    return Byte;
}

/**
  * 函    数:I2C发送应答位
  * 参    数:Byte 要发送的应答位,范围:0~1,0表示应答,1表示非应答
  * 返 回 值:无
  */
void MyI2C_SendAck(uint8_t AckBit)
{
    MyI2C_W_SDA(AckBit);
    MyI2C_W_SCL(1);
    MyI2C_W_SCL(0);
}

/**
  * 函    数:I2C接收应答位
  * 参    数:无
  * 返 回 值:接收到的应答位,范围:0~1,0表示应答,1表示非应答
  */
uint8_t MyI2C_ReceiveAck(void)
{
    uint8_t AckBit;
    MyI2C_W_SCL(1);
    MyI2C_W_SDA(1);
    AckBit=MyI2C_R_SDA();
    MyI2C_W_SCL(0);
    return AckBit;
}

 2.利用I2C对MPU6050通讯:

MPU6050_Reg.h:对MPU6050相关寄存器重新宏定义地址

#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H

#define	MPU6050_SMPLRT_DIV		0x19
#define	MPU6050_CONFIG			0x1A
#define	MPU6050_GYRO_CONFIG		0x1B
#define	MPU6050_ACCEL_CONFIG	0x1C

#define	MPU6050_ACCEL_XOUT_H	0x3B
#define	MPU6050_ACCEL_XOUT_L	0x3C
#define	MPU6050_ACCEL_YOUT_H	0x3D
#define	MPU6050_ACCEL_YOUT_L	0x3E
#define	MPU6050_ACCEL_ZOUT_H	0x3F
#define	MPU6050_ACCEL_ZOUT_L	0x40
#define	MPU6050_TEMP_OUT_H		0x41
#define	MPU6050_TEMP_OUT_L		0x42
#define	MPU6050_GYRO_XOUT_H		0x43
#define	MPU6050_GYRO_XOUT_L		0x44
#define	MPU6050_GYRO_YOUT_H		0x45
#define	MPU6050_GYRO_YOUT_L		0x46
#define	MPU6050_GYRO_ZOUT_H		0x47
#define	MPU6050_GYRO_ZOUT_L		0x48

#define	MPU6050_PWR_MGMT_1		0x6B
#define	MPU6050_PWR_MGMT_2		0x6C
#define	MPU6050_WHO_AM_I		0x75

#endif

MPU6050.C 

①写MUPU6050的指定寄存器:MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)

②读MUPU6050的指定寄存器:uint8_t MPU6050_ReadReg(uint8_t RegAddress)

③初始化:MPU6050_Init( )

④MPU6050获取ID号:uint8_t MPU6050_GetID(void)

⑤MPU6050获取数据:MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
                        int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)

        这里要返回多个参数,是通过获取主函数当中变量的地址,传递给子函数,然后对子函数地址的内容进行写入。

#include "stm32f10x.h"                  // Device header
#include "MyI2C.h"
#include "MPU6050_Reg.h"

#define MPU6050_ADDRESS		0xD0		//MPU6050的I2C从机地址

/**
  * 函    数:MPU6050写寄存器
  * 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
  * 参    数:Data 要写入寄存器的数据,范围:0x00~0xFF
  * 返 回 值:无
  */
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终止
}

/**
  * 函    数:MPU6050读寄存器
  * 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
  * 返 回 值:读取寄存器的数据,范围:0x00~0xFF
  */
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;
}

/**
  * 函    数:MPU6050初始化
  * 参    数:无
  * 返 回 值:无
  */
void MPU6050_Init(void)
{
	MyI2C_Init();									//先初始化底层的I2C
	
	/*MPU6050寄存器初始化,需要对照MPU6050手册的寄存器描述配置,此处仅配置了部分重要的寄存器*/
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);		//电源管理寄存器1,取消睡眠模式,选择时钟源为X轴陀螺仪
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);		//电源管理寄存器2,保持默认值0,所有轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);		//采样率分频寄存器,配置采样率
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);			//配置寄存器,配置DLPF
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);	//陀螺仪配置寄存器,选择满量程为±2000°/s
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);	//加速度计配置寄存器,选择满量程为±16g
}

/**
  * 函    数:MPU6050获取ID号
  * 参    数:无
  * 返 回 值:MPU6050的ID号
  */
uint8_t MPU6050_GetID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);		//返回WHO_AM_I寄存器的值
}

/**
  * 函    数:MPU6050获取数据
  * 参    数:AccX AccY AccZ 加速度计X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  * 参    数:GyroX GyroY GyroZ 陀螺仪X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  * 返 回 值:无
  */
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t DataH, DataL;								//定义数据高8位和低8位的变量
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);		//读取加速度计X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);		//读取加速度计X轴的低8位数据
	*AccX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);		//读取加速度计Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);		//读取加速度计Y轴的低8位数据
	*AccY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);		//读取加速度计Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);		//读取加速度计Z轴的低8位数据
	*AccZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);		//读取陀螺仪X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);		//读取陀螺仪X轴的低8位数据
	*GyroX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);		//读取陀螺仪Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);		//读取陀螺仪Y轴的低8位数据
	*GyroY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);		//读取陀螺仪Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);		//读取陀螺仪Z轴的低8位数据
	*GyroZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
}

 四、stm32利用I2C对MPU6050进行通信(硬件)

注:这里是使用的stm32的I2C外设对MPU6050进行通信,引脚只能选择具有I2C复用功能的引脚,并且配置引脚时要选择复用开漏功能

① 等待MPU6050事件:MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)

        利用while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)等待事件的发生,并且给予等待时间,如果超时了就不等待了

② 写MPU6050寄存器:MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)

步骤:

step1:起始:I2C_GenerateSTART( )

           等待EV5:MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)

step2:发送从机地址:I2C_Send7bitAddress( )

           等待EV6:       MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)

step3:发送寄存器地址I2C_SendData( )

            等待发送: 

           EV8:MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING)

step4:发送数据:I2C_SendData( )

             等待EV8_2:

             MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED)

step5:停止:I2C_GenerateSTOP( )

 ③ 读MPU6050寄存器:uint8_t MPU6050_ReadReg(uint8_t RegAddress)

步骤:

step1:起始:I2C_GenerateSTART( )

           等待EV5:MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)

step2:发送从机地址:I2C_Send7bitAddress( )

           等待EV6:

  MPU6050_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)

step3:发送寄存器地址:I2C_SendData( )

            等待EV8_2:

            MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)

step4:重复起始位:I2C_GenerateSTART( )

             等待EV5事件:MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)

step5:发送从机地址,方向为接收:I2C_Send7bitAddress( )

             等待EV6:

        MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)

step6:在接收最后一个字节之前提前将应答失能:I2C_AcknowledgeConfig( )

            在接收最后一个字节之前提前申请停止条件:I2C_GenerateSTOP( )

step7:等待EV7:MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED)

            接收数据寄存器:Data = I2C_ReceiveData(I2C2)

step8:将应答恢复为使能,为了不影响后续可能产生的读取多字节操作 :  

                I2C_AcknowledgeConfig(I2C2, ENABLE)

④MPU6050初始化:MPU6050_Init( )

步骤:

step1:开始I2C和GPIOB的时钟

step2:配置I2C和GPIOB的结构体:

【注:i2c结构体配置:这里的占空比设置是为适应不同SCL传输频率而设定的,如果SCL的频率过大,在SCL低电平期间,可能还没有完成对SDA总线上数据的改变,就立马变高电平了。所以需要设置占空比使得低电平期间有一定的缓冲时间。在SCL标准速度下,占空比相当于1:1,在SCL快速模式下,占空比相当于2:1。此外,SCL总线是强下拉,弱上拉,所以下降沿波形是立马下降,上升沿波形是缓慢上升。】

typedef struct
{
  uint32_t I2C_ClockSpeed;             /*设置SCL时钟频,此值不低于40000*/
  uint16_t I2C_Mode;                   /* 指定工作模式,可选I2C模式和SMBUS模式*/
  uint16_t I2C_DutyCycle;              /*指定时钟占空比,可选 low/high=2:1以及16:9模式*/
  uint16_t I2C_OwnAddress1;            /*指定自身的I2C设备地址 */
  uint16_t I2C_Ack;                    /*使能或者关闭响应 (一般都是使能) */
  uint16_t I2C_AcknowledgedAddress;    /*指定地址的长度,可以位7位及10位 */
}I2C_InitTypeDef;

step3:使能I2C

step4:对MPU6050的寄存器初始化

以下为全部I2C通讯MPU6050的代码(硬件):

#include "stm32f10x.h"                  // Device header
#include "MPU6050_Reg.h"

#define MPU6050_ADDRESS		0xD0		//MPU6050的I2C从机地址

/**
  * 函    数:MPU6050等待事件
  * 参    数:同I2C_CheckEvent
  * 返 回 值:无
  */
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
	uint32_t Timeout;
	Timeout = 10000;									//给定超时计数时间
	while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)	//循环等待指定事件
	{
		Timeout --;										//等待时,计数值自减
		if (Timeout == 0)								//自减到0后,等待超时
		{
			/*超时的错误处理代码,可以添加到此处*/
			break;										//跳出等待,不等了
		}
	}
}

/**
  * 函    数:MPU6050写寄存器
  * 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
  * 参    数:Data 要写入寄存器的数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	I2C_GenerateSTART(I2C2, ENABLE);										//硬件I2C生成起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);					//等待EV5
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);	//硬件I2C发送从机地址,方向为发送
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);	//等待EV6
	
	I2C_SendData(I2C2, RegAddress);											//硬件I2C发送寄存器地址
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);			//等待EV8
	
	I2C_SendData(I2C2, Data);												//硬件I2C发送数据
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);				//等待EV8_2
	
	I2C_GenerateSTOP(I2C2, ENABLE);											//硬件I2C生成终止条件
}

/**
  * 函    数:MPU6050读寄存器
  * 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
  * 返 回 值:读取寄存器的数据,范围:0x00~0xFF
  */
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	I2C_GenerateSTART(I2C2, ENABLE);										//硬件I2C生成起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);					//等待EV5
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);	//硬件I2C发送从机地址,方向为发送
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);	//等待EV6
	
	I2C_SendData(I2C2, RegAddress);											//硬件I2C发送寄存器地址
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);				//等待EV8_2
	
	I2C_GenerateSTART(I2C2, ENABLE);										//硬件I2C生成重复起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);					//等待EV5
	
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);		//硬件I2C发送从机地址,方向为接收
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);		//等待EV6
	
	I2C_AcknowledgeConfig(I2C2, DISABLE);									//在接收最后一个字节之前提前将应答失能
	I2C_GenerateSTOP(I2C2, ENABLE);											//在接收最后一个字节之前提前申请停止条件
	
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);				//等待EV7
	Data = I2C_ReceiveData(I2C2);											//接收数据寄存器
	
	I2C_AcknowledgeConfig(I2C2, ENABLE);									//将应答恢复为使能,为了不影响后续可能产生的读取多字节操作
	
	return Data;
}

/**
  * 函    数:MPU6050初始化
  * 参    数:无
  * 返 回 值:无
  */
void MPU6050_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);		//开启I2C2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);					//将PB10和PB11引脚初始化为复用开漏输出
	
	/*I2C初始化*/
	I2C_InitTypeDef I2C_InitStructure;						//定义结构体变量
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;				//模式,选择为I2C模式
	I2C_InitStructure.I2C_ClockSpeed = 50000;				//时钟速度,选择为50KHz
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;		//时钟占空比,选择Tlow/Thigh = 2
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;				//应答,选择使能
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;	//应答地址,选择7位,从机模式下才有效
	I2C_InitStructure.I2C_OwnAddress1 = 0x00;				//自身地址,从机模式下才有效
	I2C_Init(I2C2, &I2C_InitStructure);						//将结构体变量交给I2C_Init,配置I2C2
	
	/*I2C使能*/
	I2C_Cmd(I2C2, ENABLE);									//使能I2C2,开始运行
	
	/*MPU6050寄存器初始化,需要对照MPU6050手册的寄存器描述配置,此处仅配置了部分重要的寄存器*/
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);				//电源管理寄存器1,取消睡眠模式,选择时钟源为X轴陀螺仪
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);				//电源管理寄存器2,保持默认值0,所有轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);				//采样率分频寄存器,配置采样率
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);					//配置寄存器,配置DLPF
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);			//陀螺仪配置寄存器,选择满量程为±2000°/s
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);			//加速度计配置寄存器,选择满量程为±16g
}


/**
  * 函    数:MPU6050获取ID号
  * 参    数:无
  * 返 回 值:MPU6050的ID号
  */
uint8_t MPU6050_GetID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);		//返回WHO_AM_I寄存器的值
}

/**
  * 函    数:MPU6050获取数据
  * 参    数:AccX AccY AccZ 加速度计X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  * 参    数:GyroX GyroY GyroZ 陀螺仪X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  * 返 回 值:无
  */
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t DataH, DataL;								//定义数据高8位和低8位的变量
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);		//读取加速度计X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);		//读取加速度计X轴的低8位数据
	*AccX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);		//读取加速度计Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);		//读取加速度计Y轴的低8位数据
	*AccY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);		//读取加速度计Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);		//读取加速度计Z轴的低8位数据
	*AccZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);		//读取陀螺仪X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);		//读取陀螺仪X轴的低8位数据
	*GyroX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);		//读取陀螺仪Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);		//读取陀螺仪Y轴的低8位数据
	*GyroY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);		//读取陀螺仪Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);		//读取陀螺仪Z轴的低8位数据
	*GyroZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值