21:I2C三:MPU6050的使用

news2025/1/10 2:18:26

MPU6050的使用

  • 1、MPU6050简介
    • 1.1:加速度与姿态测量
    • 1.2:陀螺仪与姿态测量
    • 1.3:MPU6050内部结构
    • 1.4:模块内部寄存器
  • 2、程序模拟I2C读写MPU6050
    • 2.1:数据的读取
    • 2.2:转换为角度并进行融合
  • 3、片上外设I2C2读写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的大小
	}
}

显示的结果和上图一样。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2138279.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

支付域——聚合支付设计

摘要 聚合支付是支付行业的一项重要创新&#xff0c;通过整合多种支付方式&#xff0c;极大简化了支付流程&#xff0c;提升了交易效率&#xff0c;为商户和消费者提供了更加便捷的支付体验。随着移动支付的普及&#xff0c;聚合支付在未来的支付场景中将继续发挥重要作用&…

STL之初始迭代器

迭代器 什么是迭代器? - 迭代器是一种检查容器内元素并且遍历容器内元素的数据类型 迭代器的作用: - 迭代器提供对一个容器中的对象的访问方法&#xff0c;并且定义了容器中对象的范围 为什么需要迭代器? - STL提供每种容器的实现原理各不相同&#xff0c;如果没有迭代器…

心觉:《潜意识显化实操营》首批体验者招募

你是否常常感到困惑&#xff0c;对未来感到迷茫&#xff1f; 你是否在事业、生活中遇到瓶颈&#xff0c;迫切希望突破&#xff1f; 你是否想要把自己3万倍的潜意识能量释放出来 &#xff0c;助力成功&#xff1f; 你是否想要解锁你财富显化的卡点&#xff0c;顺便实现财富自…

CISSP一站通关

依托轻速云维护了一个专注于CISSP备考通关的在线学习平台&#xff0c;提供知识串讲视频&#xff0c;配合大量针对性的习题和重难点习题解析&#xff0c;帮助备考学习者高效学习和巩固知识点。已经帮助100考友顺利通过考试。 知识串讲视频是我主讲的5天直播课程的录屏&#xff0…

C# 反射之动态生成dll/exe

这个可能应该属于反射的高级使用范围了&#xff0c;平常在项目中使用的人估计也不是很多。由于使用反射的话会降低性能&#xff0c;比如之前用到的GetValue、SetValue等之类&#xff0c;但是使用这种方式会大大提高效率&#xff0c;在这里我只想说&#xff0c;都直接写IL指令了…

Qt学习之旅 I

构建一个跨平台的应用(Create A Cross-Platform Application) 目录 构建一个跨平台的应用(Create A Cross-Platform Application) 设计模式 开始构建 Qt是跨平台的C框架&#xff0c;这里&#xff0c;我们将会构建一个简单的C跨平台项目来熟悉QT是如何实现简单的跨平台的。 …

HTB-MarkUp(XXE漏洞、SSH id_rsa密钥)

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天给大家讲解MarkUp靶机 渗透过程 信息搜集 服务器开放了22SSH端口、80HTTP端口 和 443HTTPS端口 弱口令登录后台 抓取http 数据包&#xff0c;进行加载 账号密码字典 账号: admin密码: password 利用XXE漏洞 捕…

超分辨率技术之插值算法

&#x1f31e;欢迎莅临我的个人主页&#x1f448;&#x1f3fb;这里是我专注于深度学习领域、用心分享知识精粹与智慧火花的独特角落&#xff01;&#x1f349; &#x1f308;如果大家喜欢文章&#xff0c;欢迎&#xff1a;关注&#x1f377;点赞&#x1f44d;&#x1f3fb;评论…

天融信把桌面explorer.exe删了,导致开机之后无windows桌面,只能看到鼠标解决方法

win10开机进入桌面&#xff0c;发现桌面无了&#xff0c;但是可以ctrlaltdelete调出任务管理器 用管理员权限打开cmd&#xff0c;输入&#xff1a; sfc /scanfilec:\windowslexplorer.exe 在运行C:\windows\Explorer.exe&#xff1b;可以进入桌面&#xff0c;但是隔离几秒钟…

链式二叉树的基本操作(C语言版)

目录 1.二叉树的定义 2.创建二叉树 3.递归遍历二叉树 1&#xff09;前序遍历 2&#xff09;中序遍历 3&#xff09;后序遍历 4.层序遍历 5.计算节点个数 6.计算叶子节点个数 7.计算第K层节点个数 8.计算树的最大深度 9.查找值为x的节点 10.二叉树的销毁 从二叉树…

物体识别之微特征识别任务综述

“深度人工智能”是成都深度智谷科技旗下的人工智能教育机构订阅号&#xff0c;主要分享人工智能的基础知识、技术发展、学习经验等。此外&#xff0c;订阅号还为大家提供了人工智能的培训学习服务和人工智能证书的报考服务&#xff0c;欢迎大家前来咨询&#xff0c;实现自己的…

一模--解题--71-80

文章目录 9.资源管理71、 [单选] 一个项目连续错过交付日期&#xff0c;项目团队评估完该情况后&#xff0c;项目经理意识到团队绩效差的原因在于团队成员对于自己的职责缺乏清晰认识。项目经理首先应该关注哪一项&#xff1f; 13.干系人管理72、 [单选] 项目团队刚刚完成一个新…

element-plus的面包屑组件el-breadcrumb

面包屑组件主要用来显示当页面路径&#xff0c;以及快速返回之前的页面。 涉及2个组件 el-breadcrumb 和el-breadcrumb-item, el-breadcrumb的spearator指定item的分隔符 el-breadcrumb-item的to和replace属性和vue-router的一致&#xff0c;需要结合vue_router一起使用 用法…

WSL安装Redis

前言 本来一直是在虚拟机的Ubuntu开发 但是 搞着搞着内存不足 导致我某些数据损坏了 然后目前迁移到Wsl开发 运行WSL的相较于虚拟机你不需要很多的性能开销&#xff01; 我只是代码开发和git交互&#xff0c;如果是搞逆向还是虚拟机。 记录一下redis 安装卸载 免得以后又忘了…

【中等】机试-滑动窗口(双指针)-例:无重复字符的最长子串

※高频、重点 字节&#xff08;飞书&#xff09;、百度等大厂测开高频面试题&#xff1a;最长不重复子串 . - 力扣&#xff08;LeetCode&#xff09;字节飞书面经里的高频题&#xff0c;没做出来&#xff0c;需要好好复习。 重点考察-滑动窗口这个概念&#xff0c;自学记录一…

攻击者如何在日常网络资源中隐藏恶意软件

近二十年来&#xff0c;安全 Web 网关 (SWG) 一直在监控网络流量&#xff0c;以检测恶意软件、阻止恶意网站并保护企业免受基于 Web 的威胁。 然而&#xff0c;攻击者已经找到了许多绕过这些防御措施的方法&#xff0c;SquareX的安全研究人员对此进行了记录。 最危险的策略之…

【Linux】调试和Git及进度条实现

这里是阿川的博客&#xff0c;祝您变得更强 ✨ 个人主页&#xff1a;在线OJ的阿川 &#x1f496;文章专栏&#xff1a;Linux入门到进阶 &#x1f30f;代码仓库&#xff1a; 写在开头 现在您看到的是我的结论或想法&#xff0c;但在这背后凝结了大量的思考、经验和讨论 目录 1.…

LinkedHashMap 如何实现排序

目录 一、LinkedHashMap二、排序实现三、代码片段分析 一、LinkedHashMap LinkedHashMap 是 Java 中的一个集合类&#xff0c;它是 HashMap 的一个子类&#xff0c;继承了 HashMap 的所有特性&#xff0c;并且在此基础上增加了一个双向链表来维护元素的插入顺序或者访问顺序。L…

java的内存分配和回收机制

Java 与 C之间有一堵由内存动态分配和垃圾收集技术所围成的高墙&#xff0c;墙外面的人想进去&#xff0c;墙里面的人却想出来。 概述 垃圾收集&#xff08;GC&#xff09;需要完成的三件事情&#xff1a; 哪些内存需要回收&#xff1f;什么时候回收&#xff1f;如何回收&am…

CloudXR 套件扩展 XR 工作流

NVIDIA为开发者提供了一个先进的平台&#xff0c;开发者可以在该平台上使用全新NVIDIA CloudXR 套件来创建可扩展、品牌化的定制扩展现实&#xff08;XR&#xff09;产品。 NVIDIA CloudXR 套件基于全新架构而打造&#xff0c;是扩展XR生态的重要工具。它为开发者、专业人士和…