21:I2C三:MPU6050的使用

1、MPU6050简介

MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景。

1.1:加速度与姿态测量

主要是基于重力加速度g,进行测量X、Y、Z轴的的受力点的加速度。如下图所示:芯片的内部在X、Y、Z轴各有一个测力计。
在这里插入图片描述
在这里插入图片描述

如图:芯片内部就像是一个弹簧测力计。当芯片静止/匀速运动的时候,芯片内部的那个小滑块受重力G和弹簧的支撑力F1(Z轴方向G=F1),弹簧被压缩,而受力点5只受弹簧压缩的力F2,而此力方向向下,和滑块的重力相等F2 = G。而弹簧压缩就驱动电位器改变电阻大小,然后输出电压。而通过数模转换器将电压的大小代表受力点的受力大小F2。而F2/滑块的质量m = g(重力加速度)。所以得出了一个竖直向下的重力加速度g。

在这里插入图片描述

在这里插入图片描述

如上图所示:如果模块如图倾斜α角度倾斜(绕x轴旋转,其角度为翻滚角roll),那么受力点5和受力点3均受力,而受到的力为滑块的重力的分力。分力F1和F2除以滑块的质量那就得出了各个方向的加速度。那么怎么求出角度α喃?如上图所示:tanα = g2 / g1。那么α = arctan(g2/g1)。由此可得,我们可以判断那个受力点受力,进而判断是往那边倾斜,而通过计数出α的值,进而判断倾斜了多少度。进而得到了模块的姿态。

【注】①这种方法只能是模块静止或者匀速运动的时候才能使用,而当物体外部收到其他的作用力时,这个方法就会存在很大的误差。②如果物体绕z轴旋转产生的角度即偏航角yaw是没法用这个方法测量出来的

1.2:陀螺仪与姿态测量

为了避免上面那种方法的误差,所以人们发明了陀螺仪与姿态测量,通过测量X、Y、Z轴的角速度。

在这里插入图片描述
综上:
在这里插入图片描述
所以通过2种方法测到的结果进行数据的融合,进而得到更准确的结果。
在这里插入图片描述

1.3:MPU6050内部结构

在这里插入图片描述
如图所示:这个模块里面里面有加速度计、陀螺仪计,温度计(一般不用)。我们只需要通过I2C协议去读取gx,gy,gz(受力点的加速度),ax,ay,az(旋转方向的角速度)这些数据,然后在通过计数和数据融合,进而得到模块的姿态。

1.4:模块内部寄存器

模块的不同的寄存器有不同的地址值,且不同的寄存器功能不一样,我们通过I2C总线对模块内部的寄存器进行读/写,进而得到我们需要的数据。
在这里插入图片描述
下图为我们需要使用到的寄存器:

在这里插入图片描述采样频率分频寄存器:不使用低通滤波器时采样频率 = 8KHz / (1+x),使用低通滤波器时采样频率 = 1KHz / (1+x),而这个x就是我们写入寄存器的数据。
配置寄存器:主要是后面的低通滤波器的设置。
在这里插入图片描述

陀螺仪配置寄存器:设置测量角速度的量程

在这里插入图片描述

加速度配置寄存器:设置测量加速度g的量程

在这里插入图片描述
在这里插入图片描述

电池管理寄存器1:用于复位和唤醒

在这里插入图片描述

2、程序模拟I2C读写MPU6050

2.1:数据的读取

在这里插入图片描述
①I2C.c文件使用程序模拟I2C时序程序如下:

#include "stm32f10x.h"                 
#include "Delay.h"                 

//#define SCL(x)          GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)(x))
//#define SDA(x)          GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)(x))

void I2C_W_SCL(uint8_t BitValue)//拉低SCL/释放SCL(拉高)
{
	GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);
	Delay_us(10);
}

void I2C_W_SDA(uint8_t BitValue)//给SDA写数据(拉低SDA/释放SDA(拉高))
{
	GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);
	Delay_us(10);
}

uint8_t I2C_R_SDA(void)//主机读SDA数据,即判断引脚的电平
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
	Delay_us(10);
	return BitValue;
}

void MyI2C_Init(void)
{
	//1. 使能挂载在APB2总线上面的片上外设时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	//2. 对GPIO_PA10/PA11进行配置
	GPIO_InitTypeDef GPIOInitStruct;
	GPIOInitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_OD;//输出开漏模式,0才有驱动能力,开漏模式没有输出低电平时,也可以作为输入
	GPIOInitStruct.GPIO_Speed = GPIO_Speed_50MHz;//最大输出速度
	GPIO_Init(GPIOB,&GPIOInitStruct);
	
	GPIO_SetBits(GPIOB,GPIO_Pin_10 | GPIO_Pin_11);//先给输出高电平,释放总线
												//PB10连接SCL。PB11连接SDA
}

void MyI2C_Start(void)//起始信号
{
	I2C_W_SDA(1);//释放SDA
	I2C_W_SCL(1);//释放SCL
	I2C_W_SDA(0);//拉低SDA
	I2C_W_SCL(0);//拉低SDA
}

void MyI2C_Stop(void)
{
	I2C_W_SCL(0);
	I2C_W_SDA(0);
	I2C_W_SCL(1);
	I2C_W_SDA(1);
}

void MyI2C_SendByte(uint8_t Byte)//主机向从机发送一个字节,高位先行
{
	for(uint8_t i = 0;i<8;i++)
	{
		I2C_W_SDA(Byte & (0x80 >> i));//给SDA写入数据,只要Byte不是0,那么写入的就是1。因为BitAction
		I2C_W_SCL(1);//释放SCL,从机读取数据
		I2C_W_SCL(0);//拉低SCL,主机准备给SDA写入字节的次高位数据
	}
}

uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t Byte = 0x00;//接收数据的变量
	
	I2C_W_SDA(1);//主机释放SDA,让控制权给从机
	for(uint8_t i =0; i<8;i++)
	{
		I2C_W_SCL(1);//主机释放SCL,准备开始读取SDA上面的数据
		if(I2C_R_SDA() == 1)
		{
			Byte |= (0x80 >> i);
		}	
	I2C_W_SCL(0);//主机拉低SCL,让从机给SDA写入数据	
	}
	return Byte;
}


void MyI2C_SendACK(uint8_t ACKBit)//主机发送应答
{
/*
	从机发送完一个字节后,SCL为低电平,
	等待主机给SDA上面写数据,如果拉低代表接收成功。
*/
	
	I2C_W_SDA(ACKBit);
	I2C_W_SCL(1);
	I2C_W_SCL(0);
}

uint8_t MyI2C_ReceiveACK(void)//主机接收应答
{
/*
	主机发送完一个字节后,SCL为低电平,
	等待从机给SDA上面写入数据,如果拉低则代表接收成功
*/
	uint8_t ACKBit;
	I2C_W_SDA(1);//主机释放SDA,让控制权给从机
	I2C_W_SCL(1);//主机释放SCL,准备开始读取SDA上面的数据
	ACKBit = I2C_R_SDA();
	I2C_W_SCL(0);//主机拉低SCL,进入下一个时序单元,主机准备给SDA写入字节的数据
	return ACKBit;
}

②MPU6050Reg.h的代码如下:

/*
	将需要使用到的寄存器地址通过寄存器名宏定义
*/
#ifndef __MPU6050_H
#define __MPU6050_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文件的代码如下:

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

#define MPU6050_Address 0xD0//从机地址

/*
	给MPU6050的寄存器写入一个字节数据
*/
void MPU6050_WriteReg(uint8_t Address,uint8_t Data)//寄存器地址,需要发送的数据
{
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_Address);//从机地址 + 0,代表写入数据
	MyI2C_ReceiveACK();//接收应答,先不处理
	MyI2C_SendByte(Address);//发送MPU6050寄存器的地址
	MyI2C_ReceiveACK();//接收应答,先不处理
	MyI2C_SendByte(Data);
	MyI2C_ReceiveACK();//接收应答,先不处理
	MyI2C_Stop();
}

/*
	读取MPU6050的寄存器里面的一个字节数据
*/
uint8_t MPU6050_ReadReg(uint8_t Address)
{
	uint8_t Data;
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_Address);//从机地址
	MyI2C_ReceiveACK();//接收应答,先不处理
	MyI2C_SendByte(Address);//发送MPU6050寄存器的地址
	MyI2C_ReceiveACK();//接收应答,先不处理
	
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_Address | 0x01);//从机地址,从机地址 + 1,代表读取数据
	MyI2C_ReceiveACK();//接收应答,先不处理
	Data = MyI2C_ReceiveByte();//接收数据
	MyI2C_SendACK(1);
	MyI2C_Stop();
	return Data;
}

/*
	给MPU6050初始化配置
*/
void MPU6050_Init(void)/**/
{
	MyI2C_Init();
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x00);//给电源管理寄存器1发送0x00,解除睡眠模式
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00);//配置电源管理寄存器2
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);//采样率分频10分频
	MPU6050_WriteReg(MPU6050_CONFIG,0x06);//数字低通滤波
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);//角速度的量程,满量程
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);//加速度g的量程,满量程
}

/*
	获取数据寄存器里面的数据
	ACC代表加速度g的值
	Gyrox代表角数据的值
*/
void MPU6050_GetData(int16_t *ACCx,int16_t *ACCy,int16_t *ACCz,
										 int16_t *Gyrox,int16_t *Gyroy,int16_t *Gyroz)//读取寄存器里面的数据										
{
	uint16_t DataH , DataL;
	
	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);
	*ACCy = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);//读取Z轴加速度计的高8位
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	*ACCz = (DataH << 8) | DataL;

	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);//读取x轴陀螺仪的高8位
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*Gyrox = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);//读取y轴陀螺仪的高8位
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	*Gyroy = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);//读取z轴陀螺仪的高8位
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	*Gyroz = (DataH << 8) | DataL;
}

④主函数main.c的代码如下:

/*
	软件模拟I2C控制MPU6050
*/

#include "stm32f10x.h"                 
#include "OLED.h"
#include "MPU6050.h"

int main(void)
{
	OLED_Init();
	OLED_Clear();
	int16_t AX,AY,AZ,GX,GY,GZ;	
	
	MPU6050_Init();
	
	while(1)
	{
		MPU6050_GetData(&AX,&AY,&AZ,&GX,&GY,&GZ);
		OLED_ShowSignedNum(2,1,AX,5);//显示X轴的加速度g的大小
		OLED_ShowSignedNum(3,1,AY,5);//显示Y轴的加速度g的大小
		OLED_ShowSignedNum(4,1,AZ,5);//显示Z轴的加速度g的大小
		OLED_ShowSignedNum(2,8,GX,5);//显示X轴的角速度a的大小
		OLED_ShowSignedNum(3,8,GY,5);//显示Y轴的角速度a的大小
		OLED_ShowSignedNum(4,8,GZ,5);//显示Z轴的角速度a的大小
	}
}

下图为显示结果:

在这里插入图片描述
在这里插入图片描述

如上图所示:模块静止平放在桌面上面,z轴的加速度值为+1823,那么可以判断z轴的方向为垂直芯片向里。+1823换算为加速度g:(+1823)/(+2^15) = x/(+16g),那么算出x = +0.89g。

在这里插入图片描述
在这里插入图片描述

如上图所示:模块如图所放置,可见gx和gz都约为0,而gy = +2038。那么可以判断出Y轴的方向是横向向左。

在这里插入图片描述
在这里插入图片描述

如上图所示:模块如图所放置,可见gy和gz都约为0,而gx = -1962。那么可以判断出X轴的方向是纵向向上。

2.2:转换为角度并进行融合

/*
	通过数据寄存器里面的值计算出加速度g和角速度a
	通过g和a计算出欧拉角,然后在进行数据融合。
*/

void MPU6050_GatResult(float *yaw ,float *roll ,float *pitch)
{
	float gleX,gleY,gleZ,aleX,aleY,aleZ;//定义重力加速度和角速度
	int16_t AX,AY,AZ,GX,GY,GZ;	
	
	MPU6050_GetData(&AX,&AY,&AZ,&GX,&GY,&GZ);
	
	gleX = AX / 2048.0f;//x轴的重力加速度
	gleY = AY / 2048.0f;
	gleZ = AZ / 2048.0f;
	
	aleX = GX / 16.4f;//x轴的角速度
	aleY = GY / 16.4f;
	aleZ = GZ / 16.4f;
	
	/*通过重力加速度对欧拉角的计数*/
	float roll_a = atan2(gleY,gleZ) / 3.141593 * 180.0f;//翻滚角的角度
	float pitch_a = atan2(gleX,gleZ) / 3.141593 * 180.0f;//俯仰角的角度
	
	/*通过角速度计数欧拉角*/
	float yaw_g = *yaw + aleX * 0.005;//偏航角的角度
	float roll_g = *roll + aleY * 0.005;//翻滚角的角度
	float pitch_g = *pitch + aleZ * 0.005;//俯仰角的角度
	
	/*角度融合*/
	const float alpha = 0.95238;
	*yaw = yaw_g;//偏航角
	*roll = roll_a * alpha + (1 - alpha) * roll_g;//翻滚角
	*pitch = pitch_a * alpha + (1 - alpha) * pitch_g;//俯仰角
}

主函数main.c的代码如下:

/*
	软件模拟I2C控制MPU6050
*/

#include "stm32f10x.h"                 
#include "OLED.h"
#include "MPU6050.h"



int main(void)
{
	OLED_Init();
	OLED_Clear();
	MPU6050_Init();
	float yaw ,roll ,pitch;
	
	OLED_ShowString(1,1,"yaw: ");
	OLED_ShowString(2,1,"roll: ");
	OLED_ShowString(3,1,"pitch: ");
	while(1)
	{
		MPU6050_GatResult(&yaw ,&roll ,&pitch);
		OLED_ShowSignedNum(1,6,yaw,3);//偏航角
		OLED_ShowSignedNum(2,7,roll,3);//翻滚角
		OLED_ShowSignedNum(3,8,pitch,3);//俯仰角
	}
}

下图为显示结果:
在这里插入图片描述
在这里插入图片描述

如上图所示:模块如图所放置,所显示的俯仰角(绕Y轴旋转)为+84°
在这里插入图片描述
在这里插入图片描述

如上图所示:模块如图所放置,所显示的翻滚角(X轴旋转)为+81°

3、片上外设I2C2读写MPU6050

使用片上外设读写MPU6050只需要将程序模拟的I2C时序替换为硬件的I2C时序即可。
①MPU6050Reg.h的代码如下:

/*
	将需要使用到的寄存器地址通过寄存器名宏定义
*/
#ifndef __MPU6050_H
#define __MPU6050_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文件的代码如下:

#include "stm32f10x.h"                  
#include "MPU6050reg.h"
#include "MyI2C.h"

#define MPU6050_Address 0xD0//从机地址

/*
	给MPU6050的寄存器写入一个字节数据
*/
void MPU6050_WriteReg(uint8_t Address,uint8_t Data)//寄存器地址,需要发送的数据
{
	//MyI2C_Start();
	//MyI2C_SendByte(MPU6050_Address);//从机地址 + 0,代表写入数据
	//MyI2C_ReceiveACK();//接收应答,先不处理
	//MyI2C_SendByte(Address);//发送MPU6050寄存器的地址
	//MyI2C_ReceiveACK();//接收应答,先不处理
	//MyI2C_SendByte(Data);
	//MyI2C_ReceiveACK();//接收应答,先不处理
	//MyI2C_Stop();
	
	I2C_GenerateSTART(I2C2,ENABLE);//发送一个起始信号
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//如果起始信号没有发送成功则一直在这循环
	I2C_Send7bitAddress(I2C2,MPU6050_Address,I2C_Direction_Transmitter);//发送地址+写
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);
	I2C_SendData(I2C2,Address);//发送MPU6050寄存器的地址
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS);
	I2C_SendData(I2C2,Data);
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);
	I2C_GenerateSTOP(I2C2,ENABLE);
}


/*
	读取MPU6050的寄存器里面的一个字节数据
*/
uint8_t MPU6050_ReadReg(uint8_t Address)
{
	uint8_t Data;
	//MyI2C_Start();
	//MyI2C_SendByte(MPU6050_Address);//从机地址
	//MyI2C_ReceiveACK();//接收应答,先不处理
	//MyI2C_SendByte(Address);//发送MPU6050寄存器的地址
	//MyI2C_ReceiveACK();//接收应答,先不处理
	
	//MyI2C_Start();
	//MyI2C_SendByte(MPU6050_Address | 0x01);//从机地址,从机地址 + 1,代表读取数据
	//MyI2C_ReceiveACK();//接收应答,先不处理
	//Data = MyI2C_ReceiveByte();//接收数据
	//MyI2C_SendACK(1);
	//MyI2C_Stop();

	I2C_GenerateSTART(I2C2,ENABLE);//发送一个起始信号
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//如果起始信号没有发送成功则一直在这循环
	
	I2C_Send7bitAddress(I2C2,MPU6050_Address,I2C_Direction_Transmitter);//发送地址+写
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);
	
	I2C_SendData(I2C2,Address);//发送MPU6050寄存器的地址
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS);
	
	I2C_GenerateSTART(I2C2,ENABLE);//发送一个起始信号
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);
	
	I2C_Send7bitAddress(I2C2,MPU6050_Address,I2C_Direction_Receiver);//发送地址+读
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS);
	
	I2C_AcknowledgeConfig(I2C2,DISABLE);//非应答
	I2C_GenerateSTOP(I2C2,ENABLE);//在接收最后一个字节提前申请停止信号
	
	while(I2C_CheckEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);
	Data = I2C_ReceiveData(I2C2);
	
	return Data;
}

/*
	给MPU6050初始化配置
*/
void MPU6050_Init(void)/**/
{
	 //1 .初始化IO引脚
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitTypeDef GPIOInitStruct;
	GPIOInitStruct.GPIO_Mode = GPIO_Mode_AF_OD;//复用输出开漏模式
	GPIOInitStruct.GPIO_Pin = GPIO_Pin_10 |GPIO_Pin_11;
	GPIOInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIOInitStruct);
	
    //2 .开启I2C挂载APB1总线的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);//开启时钟
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C2,ENABLE);//对I2C1进行复位
	RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C2,DISABLE);//对I2C1进行释放
	
    //3 .配置I2C1的参数
	I2C_InitTypeDef I2CInitStruct;
	I2CInitStruct.I2C_ClockSpeed = 400000;//配置波特率,最高400K
	I2CInitStruct.I2C_Mode = I2C_Mode_I2C;//标准的I2C模式,一般不使用SMBus
	I2CInitStruct.I2C_DutyCycle = I2C_DutyCycle_2;//2:1
	I2C_Init(I2C2,&I2CInitStruct);
	
    //4 .使能I2C
	I2C_Cmd(I2C2,ENABLE);//闭合开关,PE
	
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x00);//给电源管理寄存器1发送0x00,解除睡眠模式
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00);//配置电源管理寄存器2
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);//采样率分频10分频
	MPU6050_WriteReg(MPU6050_CONFIG,0x06);//数字低通滤波
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);//角速度的量程,满量程
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);//加速度g的量程,满量程
}

/*
	获取数据寄存器里面的数据
	ACC代表加速度g的值
	Gyrox代表角数据的值
*/
void MPU6050_GetData(int16_t *ACCx,int16_t *ACCy,int16_t *ACCz,
										 int16_t *Gyrox,int16_t *Gyroy,int16_t *Gyroz)//读取寄存器里面的数据										
{
	uint16_t DataH , DataL;
	
	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);
	*ACCy = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);//读取Z轴加速度计的高8位
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
	*ACCz = (DataH << 8) | DataL;

	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);//读取x轴陀螺仪的高8位
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
	*Gyrox = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);//读取y轴陀螺仪的高8位
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
	*Gyroy = (DataH << 8) | DataL;
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);//读取z轴陀螺仪的高8位
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
	*Gyroz = (DataH << 8) | DataL;
}

④主函数main.c的代码如下:

/*
	软件模拟I2C控制MPU6050
*/

#include "stm32f10x.h"                 
#include "OLED.h"
#include "MPU6050.h"

int main(void)
{
	OLED_Init();
	OLED_Clear();
	int16_t AX,AY,AZ,GX,GY,GZ;	
	
	MPU6050_Init();
	
	while(1)
	{
		MPU6050_GetData(&AX,&AY,&AZ,&GX,&GY,&GZ);
		OLED_ShowSignedNum(2,1,AX,5);//显示X轴的加速度g的大小
		OLED_ShowSignedNum(3,1,AY,5);//显示Y轴的加速度g的大小
		OLED_ShowSignedNum(4,1,AZ,5);//显示Z轴的加速度g的大小
		OLED_ShowSignedNum(2,8,GX,5);//显示X轴的角速度a的大小
		OLED_ShowSignedNum(3,8,GY,5);//显示Y轴的角速度a的大小
		OLED_ShowSignedNum(4,8,GZ,5);//显示Z轴的角速度a的大小
	}
}

显示的结果和上图一样。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值