一、MPU6050简介

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

2、3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度;

      3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度;

3、16位ADC采集传感器的模拟信号,量化范围:-32768~32767;

4、加速度即满量程选择:±2、±4、±8、±16;

5、陀螺仪满量程选择:±250、±500、±1000、±2000(°/sec);

6、可配置的数字低通滤波;

7、可配置的时钟源;

8、可配置的采样分频;

二、硬件与引脚

1、VCC:3.3V,模块板载LDO可以接5V,芯片内核只能3.3V。

2、GND:必须与STM32共地,否则数据乱跳;

3、SDA、SCL:I2C通信引脚,最高400KHz,

4、XCL、XDA:主机I2C通信引脚:是扩展功能用,常外接磁力计、气压计,将外接的数据读取到MPU6050里面,通过DMP数据融合和姿态解算,也可将气压计直接接SCL、SDA上。

5、AD0:地址选择

        AD0接地→I2C 7位地址0x68(0x1101000);

        AD0接VCC→I2C 7位地址0x69 (0x1101001);

6、INT:中断输出。

三、常用关键寄存器

1、WHO_AM_I 0x75(只读):读出应为0x68,读不到则I2C不通;

2、PWR_MGMT_1 0x6B,电源管理1,上电默认0x40;

名称 说明
7 DEVICE_RESET

写1硬件复位芯片,硬件自动清0;

复位后芯片进入睡眠

6 SLEEP 1=睡眠模式;0=正常工作,必须清零
5 CYCLE 循环低功耗模式:睡眠--唤醒间歇采集加速度,关闭陀螺仪。
4 RESERVED 保留
3 TEMP_DIS 1-关闭片内温度传感器,0-开启
2-0 CKLSEL 时钟源选择

CKLSEL推荐:0-内部8M RC,精度差,不推荐长期使用;

        1:PLL,X-Gyro时钟源,最常用2:PLL Y-Gyro;3:PLL Z-Gyro;

        标准唤醒配置:0x01--退出睡眠,使用X陀螺PLL时钟,稳定打开,建议不要写0x00;

3、PWR_MGMT_2 0x6C,电源管理2

bit[5-0]=分别控制AX、AY、AZ,GX,GY,GZ,写0开启该轴,写1关闭该轴;上电默认全部是0,六轴全开,全部传感器开启;注:这不是休眠,是单轴关闭,休眠是0x6B的bit6;

4、SMPLRT_DIV 0x19 采样率分频寄存器

陀螺仪输出数据率=陀螺仪内部输出速率/(1+SMPRT_DIV);

DLPF_CFG≠7,陀螺基础速率1000Hz(ADC);

DLPF_CFG=0或7,陀螺基础速率8000Hz(ADC);示例如下:

SMPRT_DIV=7→1000/(1+7) =125Hz,平衡车、倒立摆最常用;而加速度ADC固定1kHz不受SMPRT_DIV的限制。

5、CONFIG 0x1A 配置寄存器

DLPF(Digital Low Pass Filter):硬件数字低通滤波

bit[2-0] =DLPF_CFG,硬件数字低通滤波,直接决定滤波、延迟。

[7-3]:FSYNC外部同步输入,不用必须写0;Frame Synchronization(帧同步)

[2-0]:DLPF_CFG数字低通滤波配置;

示例:写0x06,即DLPF_CFG=6,带宽5Hz,强抑制振动噪声,但延迟大,适合平衡车,高速项目选3或4;

DLPF是硬件模拟之后数字滤波,优先用硬件DLPF,不能全靠软件滤波。

加速度计带宽好陀螺带宽不是严格相等,是2套独立的数字滤波器硬件。

DLPF带宽  VS   SMPLRT_DIV(0x19)输出采样频率

DLPF_CFG:硬件滤波器截止带宽,抗混叠、过滤噪声,发送在ADC之后,输出寄存器之前。

SMPLRT_DIV:输出分频,决定MCU读到数据的输出频率ODR(只改变输出多少Hz给MCU,不改变滤波器截止频率)。

经验设计原则:硬件DLPF带宽必须小于ODR/2,否则发发送信号混叠。比如:ODR=125Hz,ODR/2=62.5,那么DLPF_CFG只能选4(21Hz)、5(10Hz)、6(5Hz),不能选0/1/2/3。

6、GYRO_CONFIG 0x1B 陀螺仪配置寄存器

[7-5]是陀螺仪X、Y、Z的自检位;[4-3]是FS_SEL陀螺仪满量程选择;[2-0]保留,为0;

示例:0x18:FS_SEL=3,±2000°/s;最大量程,分辨率最低,适合高速旋转。普通平衡小车推荐0x08,±500°/s。

7、ACCEL_CONFIG 0x1C 加速度计配置寄存器

[7-5]是加速度计X、Y、Z的自检位;[4-3]是AFS_SEL加速度计量程选择;[2-0]写0;

示例:0x18:AFS_SEL=3,±16g;量程越大,分辨率越低。静止、倾角检测选±2g,冲击、剧烈运动选±8g。

8、数据寄存器组

只读,0x3B开始,连续14个字节,全部为16位二进制补码,高字节在前,低字节在后。读取后必须存入int16_t有符号16位整型,不能用uint16_t直接运算。

示例:0x3B-0x3C:ACCEL_XOUT_H/L,X加速度;以此类推……

0x41-0x42 :TEMP_OUT_H/L:片内温度原始值,温度计算公式:

int16_t Temp_raw;
float Temp_c =Temp_raw/340.0f+36.53f;

这是芯片硅片内部温度,不是环境温度,32-38℃属于正常,不能当环境温度计。

示例:0x43-0x44:ACCEL_XOUT_H/L,X陀螺仪角速度;以此类推……

四、代码部分

1、MPU6050.c文件

#include "stm32f10x.h"                  
#include "I2C.h"  
#include "MPU6050Req.h"  
#include "MPU6050.h"  

#define MPU6050_ADDRESS	0xD0
void MPU6050_WriteReq(uint8_t ReqAddress,uint8_t Data)
{
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS);
	MyI2C_ReceiveACK();
	MyI2C_SendByte(ReqAddress);
	MyI2C_ReceiveACK();
	MyI2C_SendByte(Data);
	MyI2C_ReceiveACK();
	MyI2C_Stop();
}	
uint8_t MPU6050_ReadReq(uint8_t RegAddress)
{
	uint8_t Data;
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS);
	MyI2C_ReceiveACK();
	MyI2C_SendByte(RegAddress);
	MyI2C_ReceiveACK();
	
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS|0x01);
	MyI2C_ReceiveACK();
	Data =MyI2C_ReceiveByte();
	MyI2C_SendACK(1);
	MyI2C_Stop();
	return Data;
}
void MPU6050_Init()
{
	MyI2C_Init();
	MPU6050_WriteReq(MPU6050_PWR_MGMT_1,0x01);
	MPU6050_WriteReq(MPU6050_PWR_MGMT_2,0x00);
	MPU6050_WriteReq(MPU6050_SMPLRT_DIV,0x09);
	MPU6050_WriteReq(MPU6050_CONFIG,0x06);
	MPU6050_WriteReq(MPU6050_GYRO_CONFIG,0x18);
	MPU6050_WriteReq(MPU6050_ACCEL_CONFIG,0x18);
	Delay_ms(100);
}
uint8_t GetID()
{
	return MPU6050_ReadReq(MPU6050_WHO_AM_I);
}

void MPU6050_GetData(MPU6050_StructureData* SensorValue)
{
	uint8_t DataH,DataL;
	DataH = MPU6050_ReadReq(MPU6050_ACCEL_XOUT_H);
	DataL = MPU6050_ReadReq(MPU6050_ACCEL_XOUT_L);
	SensorValue->AccX = (int16_t)DataH<<8| DataL;
	DataH = MPU6050_ReadReq(MPU6050_ACCEL_YOUT_H);
	DataL = MPU6050_ReadReq(MPU6050_ACCEL_YOUT_L);
	SensorValue->AccY = (int16_t)DataH<<8| DataL;
	DataH = MPU6050_ReadReq(MPU6050_ACCEL_ZOUT_H);
	DataL = MPU6050_ReadReq(MPU6050_ACCEL_ZOUT_L);
	SensorValue->AccZ = (int16_t)DataH<<8| DataL;	
}

2、MPU6050.h文件

#ifndef __MPU6050_H
#define __MPU6050_H
#include "Delay.h"
void MPU6050_WriteReq(uint8_t ReqAddress,uint8_t Data);
uint8_t MPU6050_ReadReq(uint8_t RegAddress);
void MPU6050_Init();
uint8_t GetID();

typedef struct
{	
	int16_t AccX;
	int16_t AccY;
	int16_t AccZ;	
	int16_t GroX;
	int16_t GroY;
	int16_t GroZ;	
}MPU6050_StructureData;

void MPU6050_GetData(MPU6050_StructureData* SensorValue);
#endif

3、MPU6050寄存器文件

#ifndef __MPU6050REQ_H
#define __MPU6050REQ_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	//加速度计X轴高位
#define	MPU6050_ACCEL_XOUT_L	0x3C	//加速度计X轴低位,以此类推……
#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	//电源管理1,配置睡眠模式和时钟源选择
#define	MPU6050_PWR_MGMT_2		0x6C	//电源管理2,配置X、Y、Z睡眠与否
#define	MPU6050_WHO_AM_I		0x75

#endif

4、main函数

#include "stm32f10x.h"                 
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
uint8_t ID;
MPU6050_StructureData Sensor_Value;
int main()
{
	OLED_Init();
	MPU6050_Init();
	ID= GetID();
	Test =TestValue();
	OLED_ShowHexNum(1,1,ID,2);	
	OLED_ShowHexNum(1,4,Test,2);		
	while(1)
	{
		MPU6050_GetData(&Sensor_Value);
		OLED_ShowSignedNum(2,1,Sensor_Value.AccX,5);
		OLED_ShowSignedNum(3,1,Sensor_Value.AccY,5);
		OLED_ShowSignedNum(4,1,Sensor_Value.AccZ,5);
		Delay_ms(100);
	}
}

练习2:连续读

1、I2C底层修改

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


void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);
	Delay_us(5);
}

void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);
	Delay_us(5);
}
uint8_t MyI2C_R_SDA()
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
	Delay_us(5);
	return BitValue;
}

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

void MyI2C_Start()
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);	
}

void MyI2C_Stop()
{
	MyI2C_W_SCL(0);
	MyI2C_W_SDA(0);
	
	MyI2C_W_SCL(1);	
	MyI2C_W_SDA(1);
}

void MyI2C_SendByte(uint8_t Byte)
{
	for(uint8_t i=0;i<8;i++)
	{
		MyI2C_W_SDA(Byte&(0x80>>i));
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
	}
}

uint8_t MyI2C_ReceiveByte()
{
	uint8_t Byte=0x00;
	uint8_t i=0;
	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(1);
		MyI2C_W_SCL(0);
	}
	return Byte;	
}

void MyI2C_SendACK(uint8_t ACKBit)
{	
	MyI2C_W_SDA(ACKBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}

uint8_t MyI2C_ReceiveACK()
{
	uint8_t ACKBit ;
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	ACKBit = MyI2C_R_SDA();
	MyI2C_W_SCL(0);
	return ACKBit;
}
//连续读
void MyI2C_ReadMulti(uint8_t Addr,uint8_t Reg,uint8_t*Buff,uint8_t Lenth)
{
	MyI2C_Start();
	MyI2C_SendByte(Addr);
	MyI2C_ReceiveACK();
	MyI2C_SendByte(Reg);
	MyI2C_ReceiveACK();
	
	MyI2C_Start();
	MyI2C_SendByte(Addr |0x01);
	MyI2C_ReceiveACK();
	for(uint8_t i=0;i<Lenth;i++)
	{
		Buff[i]= MyI2C_ReceiveByte();
		if(i ==Lenth-1)
		{
			MyI2C_SendACK(1);			
		}
		else
		{
			MyI2C_SendACK(0);
		}
		
	}
	MyI2C_Stop();
}

2、MPU6050.c文件

#include "stm32f10x.h"                  
#include "I2C.h"  
#include "MPU6050Req.h"  
#include "MPU6050.h"  

#define MPU6050_ADDRESS	0xD0
void MPU6050_WriteReq(uint8_t ReqAddress,uint8_t Data)
{
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS);
	MyI2C_ReceiveACK();
	MyI2C_SendByte(ReqAddress);
	MyI2C_ReceiveACK();
	MyI2C_SendByte(Data);
	MyI2C_ReceiveACK();
	MyI2C_Stop();
}	
uint8_t MPU6050_ReadReq(uint8_t RegAddress)
{
	uint8_t Data;
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS);
	MyI2C_ReceiveACK();
	MyI2C_SendByte(RegAddress);
	MyI2C_ReceiveACK();
	
	MyI2C_Start();
	MyI2C_SendByte(MPU6050_ADDRESS|0x01);
	MyI2C_ReceiveACK();
	Data =MyI2C_ReceiveByte();
	MyI2C_SendACK(1);
	MyI2C_Stop();
	return Data;
}
void MPU6050_Init()
{
	MyI2C_Init();
	MPU6050_WriteReq(MPU6050_PWR_MGMT_1,0x01);
	MPU6050_WriteReq(MPU6050_PWR_MGMT_2,0x00);
	MPU6050_WriteReq(MPU6050_SMPLRT_DIV,0x09);
	MPU6050_WriteReq(MPU6050_CONFIG,0x06);
	MPU6050_WriteReq(MPU6050_GYRO_CONFIG,0x18);
	MPU6050_WriteReq(MPU6050_ACCEL_CONFIG,0x18);
	Delay_ms(100);
}
uint8_t GetID()
{
	return MPU6050_ReadReq(MPU6050_WHO_AM_I);
}

uint8_t TestValue()
{
	return MPU6050_ReadReq(MPU6050_PWR_MGMT_1);
}
void MPU6050_GetData(MPU6050_StructureData* SensorValue)
{
	uint8_t Buff[14];
	MyI2C_ReadMulti(MPU6050_ADDRESS,MPU6050_ACCEL_XOUT_H,Buff,14);
	SensorValue->AccX =(int16_t)Buff[0]<<8 |Buff[1];
	SensorValue->AccY =(int16_t)Buff[2]<<8 |Buff[3];
	SensorValue->AccZ =(int16_t)Buff[4]<<8 |Buff[5];
	
	SensorValue->GroX=(int16_t)Buff[8]<<8 |Buff[9];
	SensorValue->GroY =(int16_t)Buff[10]<<8 |Buff[11];
	SensorValue->GroZ =(int16_t)Buff[12]<<8 |Buff[13];	
}	

3、main函数

#include "stm32f10x.h"                  
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
uint8_t ID;
int16_t AccX,AccY,AccZ,GroX,GroY,GroZ;
MPU6050_StructureData Sensor_Value;
int main()
{
	OLED_Init();
	MPU6050_Init();
	ID= GetID();
	Test =TestValue();
	OLED_ShowHexNum(1,1,ID,2);	
	while(1)
	{
		MPU6050_GetData(&Sensor_Value);
		OLED_ShowSignedNum(2,1,Sensor_Value.AccX,5);
		OLED_ShowSignedNum(3,1,Sensor_Value.AccY,5);
		OLED_ShowSignedNum(4,1,Sensor_Value.AccZ,5);
		
		OLED_ShowSignedNum(2,8,Sensor_Value.GroX,5);
		OLED_ShowSignedNum(3,8,Sensor_Value.GroY,5);
		OLED_ShowSignedNum(4,8,Sensor_Value.GroZ,5);	
		Delay_ms(100);
	}
}

结果:量程配置±16g,平放时,AccZ≈+2048,AccX、AccY在0附近小范围跳动。

陀螺仪静止全部接近0,手动转动模块,正负值来回变化。

本文完!

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐