MPU6050初始化的步骤:
1、 复位MPU6050,让MPU6050内部的所有寄存器恢复默认值(向0X6B写入0x80)
2、设置电源管理寄存器位0x00,以唤醒MPU6050,进入正常工作状态(向0x6B写入0x00)
3、 陀螺仪配置寄存器(0x1B)设置MPU6050陀螺仪传感器满量程范围,这里选择正负2000dps
4、加速度传感器配置寄存器(0x1C)这里选择正负2g
5、陀螺仪采样率,由采样率分频寄存器(0x19)控制;这里设置为50hz即输出频率=1KHz,SMPLRT_DIV=19
6、设置MPU6050的数字低通滤波器,因为配置为50hz,找一个接近值,所以配置为0x03,42hz
7、设置PLL,一般选择x轴陀螺PLL作为时钟源,以获得更高精度的时钟。(向0X6B写入0x01)
8、设置加速度与陀螺仪都工作(向0X6C写入0x00)
这里还有一个寄存器可以用来检测是否有mpu6050(当AD0接地时,向0x75读取数据则返回0x68;当AD0接VCC时,向0x75读取数据则返回0x69)
以上是初始化的部分,初始化完成之后开始读取数据:
读取温度的地址:
读取陀螺仪测量值(原始值)分别有X/Y/Z轴的数据
读取加速度计测量值(原始值)分别有X/Y/Z轴的数据
上代码:
mpu6050.c
#include "mpu6050.h"
//IIC连续写
//addr:器件地址
//reg:寄存器地址
//len:写入长度
//buf:数据区
//返回值:0,正常
// 其他,错误代码
u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
u8 i;
IIC_Start();
IIC_Write((addr<<1)|0);//发送器件地址+写命令
if(IIC_Whit_Ack()) //等待应答
{
IIC_Stop();
return 1;
}
IIC_Write(reg); //写寄存器地址
IIC_Whit_Ack(); //等待应答
for(i=0;i<len;i++)
{
IIC_Write(buf[i]); //发送数据
if(IIC_Whit_Ack()) //等待ACK
{
IIC_Stop();
return 1;
}
}
IIC_Stop();
return 0;
}
//IIC连续读
//addr:器件地址
//reg:要读取的寄存器地址
//len:要读取的长度
//buf:读取到的数据存储区
//返回值:0,正常
// 其他,错误代码
u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
IIC_Start();
IIC_Write((addr<<1)|0);//发送器件地址+写命令
if(IIC_Whit_Ack()) //等待应答
{
IIC_Stop();
return 1;
}
IIC_Write(reg); //写寄存器地址
IIC_Whit_Ack(); //等待应答
IIC_Start();
IIC_Write((addr<<1)|1);//发送器件地址+读命令
IIC_Whit_Ack(); //等待应答
while(len)
{
if(len==1)
{
*buf=IIC_Read();//读数据,发送nACK 0
IIC_Send_Ack(1);
}
else {
*buf=IIC_Read(); //1
IIC_Send_Ack(0);
//读数据,发送ACK
}
len--;
buf++;
}
IIC_Stop(); //产生一个停止条件
return 0;
}
//写
char MPU6050_WriteReg(u8 reg_add,u8 reg_dat)
{
IIC_Start();
IIC_Write((0x68<<1)|0);
if( IIC_Whit_Ack() == 1 ) return 1;
IIC_Write(reg_add);
if( IIC_Whit_Ack() == 1 ) return 2;
IIC_Write(reg_dat);
if( IIC_Whit_Ack() == 1 ) return 3;
IIC_Stop();
}
//读
char MPU6050_ReadData(u8 reg_add,unsigned char*Read,u8 num)
{
unsigned char i;
IIC_Start();
IIC_Write((0x68<<1)|0);
if( IIC_Whit_Ack() == 1 ) return 1;
IIC_Write(reg_add);
if( IIC_Whit_Ack() == 1 ) return 2;
IIC_Start();
IIC_Write((0x68<<1)|1);
if( IIC_Whit_Ack() == 1 ) return 3;
for(i=0;i<(num-1);i++){
*Read=IIC_Read();
Read++;
IIC_Send_Ack(0);
}
*Read=IIC_Read();
IIC_Send_Ack(0);
IIC_Stop();
return 0;
}
/**********************************************
函数名称:MPU_Set_Gyro_Fsr
函数功能:设置MPU6050陀螺仪传感器满量程范围
函数参数:fsr:0,±250dps;1,±500dps;2,±1000dps;3,±2000dps
函数返回值:0,设置成功 其他,设置失败
**********************************************/
u8 MPU_Set_Gyro_Fsr(u8 fsr)
{
return MPU6050_WriteReg(MPU_GYRO_CFG_REG,fsr<<3); //设置陀螺仪满量程范围
}
/**********************************************
函数名称:MPU_Set_Accel_Fsr
函数功能:设置MPU6050加速度传感器满量程范围
函数参数:fsr:0,±2g;1,±4g;2,±8g;3,±16g
函数返回值:0,设置成功 其他,设置失败
**********************************************/
u8 MPU_Set_Accel_Fsr(u8 fsr)
{
return MPU6050_WriteReg(MPU_ACCEL_CFG_REG,fsr<<3); //设置加速度传感器满量程范围
}
/**********************************************
函数名称:MPU_Set_LPF
函数功能:设置MPU6050的数字低通滤波器
函数参数:lpf:数字低通滤波频率(Hz)
函数返回值:0,设置成功 其他,设置失败
**********************************************/
u8 MPU_Set_LPF(u16 lpf)
{
u8 data=0;
if(lpf>=188)data=1;
else if(lpf>=98)data=2;
else if(lpf>=42)data=3;
else if(lpf>=20)data=4;
else if(lpf>=10)data=5;
else data=6;
return MPU6050_WriteReg(MPU_CFG_REG,data);//设置数字低通滤波器
}
/**********************************************
函数名称:MPU_Set_Rate
函数功能:设置MPU6050的采样率(假定Fs=1KHz)
函数参数:rate:4~1000(Hz) 初始化中rate取50
函数返回值:0,设置成功 其他,设置失败
**********************************************/
u8 MPU_Set_Rate(u16 rate)
{
u8 data;
if(rate>1000)rate=1000;
if(rate<4)rate=4;
data=1000/rate-1;
data=MPU6050_WriteReg(MPU_SAMPLE_RATE_REG,data); //设置数字低通滤波器 19
return MPU_Set_LPF(rate/2); //自动设置LPF为采样率的一半
}
/************************************************************
* 函数名称:MPU6050_Init
* 函数说明:MPU6050的初始化
* 型 参:无
* 返 回 值:0=失败没有检测到MPU6050 =1配置成功
* 备 注:无
*************************************************************/
char MPU6050_Init(void)
{
delay_ms(10);
//复位6050
MPU6050_WriteReg(MPU6050_RA_PWR_MGMT_1, 0x80);
delay_ms(100);
//电源管理寄存器
//选择X轴陀螺作为参考PLL的时钟源,设置CLKSEL=001
MPU6050_WriteReg(MPU6050_RA_PWR_MGMT_1, 0x00);
MPU_Set_Gyro_Fsr(3); //陀螺仪传感器,±2000dps
MPU_Set_Accel_Fsr(0); //加速度传感器,±2g
MPU_Set_Rate(50);
MPU6050_WriteReg(MPU_INT_EN_REG , 0x00); //关闭所有中断
MPU6050_WriteReg(MPU_USER_CTRL_REG,0x00); //I2C主模式关闭
MPU6050_WriteReg(MPU_FIFO_EN_REG, 0x00); //关闭FIFO
MPU6050_WriteReg(MPU_INTBP_CFG_REG,0X80); //INT引脚低电平有效
if( MPU6050ReadID() == 0 )//检查是否有6050
{
MPU6050_WriteReg(MPU6050_RA_PWR_MGMT_1, 0x01 );//设置CLKSEL,PLL X轴为参考
MPU6050_WriteReg(MPU_PWR_MGMT2_REG, 0x00 );//加速度与陀螺仪都工作
MPU_Set_Rate(50);
printf("MPU6050初始化成功\r\n");
return 1;
}
return 0;
}
//得到温度值
//返回值:温度值(扩大了100倍)
short MPU_Get_Temperature(void)
{
u8 buf[2];
short raw;
float temp;
MPU_Read_Len(0x68,MPU_TEMP_OUTH_REG,2,buf);
raw=((u16)buf[0]<<8)|buf[1];
//温度换算公式为:Temperature = 36.53 + regval/340
//单位为℃
temp=36.53+((double)raw)/340;
return temp*100;;
}
//得到陀螺仪值(原始值)
//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
//返回值:0,成功
// 其他,错误代码
u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz)
{
u8 buf[6],res;
//MPU_GYRO_XOUTH_REG = MPU6050陀螺仪数据寄存器地址
//陀螺仪数据输出寄存器总共由6个寄存器组成,
//输出X/Y/Z三个轴的陀螺仪传感器数据,高字节在前,低字节在后。
//每一个轴16位,按顺序为xyz
res=MPU_Read_Len(0x68,MPU_GYRO_XOUTH_REG,6,buf);
if(res==0)
{
*gx=((u16)buf[0]<<8)|buf[1];
*gy=((u16)buf[2]<<8)|buf[3];
*gz=((u16)buf[4]<<8)|buf[5];
}
return res;;
}
//得到加速度值(原始值)
//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
//返回值:0,成功
// 其他,错误代码
u8 MPU_Get_Accelerometer(short *ax,short *ay,short *az)
{
u8 buf[6],res;
res=MPU_Read_Len(0x68,MPU_ACCEL_XOUTH_REG,6,buf);
//MPU_ACCEL_XOUTH_REG = MPU6050加速度数据寄存器地址
//加速度传感器数据输出寄存器总共由6个寄存器组成,
//输出X/Y/Z三个轴的加速度传感器值,高字节在前,低字节在后。
if(res==0)
{
*ax=((u16)buf[0]<<8)|buf[1];
*ay=((u16)buf[2]<<8)|buf[3];
*az=((u16)buf[4]<<8)|buf[5];
}
return res;;
}
/************************************************************
* 函数名称:MPU6050ReadID
* 函数说明:读取MPU6050的器件地址
* 型 参:无
* 返 回 值:0=检测不到MPU6050 1=能检测到MPU6050
* 备 注:
*************************************************************/
uint8_t MPU6050ReadID(void)
{
unsigned char Re[2] = {0};
//器件ID寄存器 = 0x75
printf("mpu=%d\r\n",MPU6050_ReadData(0X75,Re,1)); //读器件地址
if (Re[0] != 0x68)
{
printf("检测不到 MPU6050 模块");
return 1;
}
else
{
printf("MPU6050 ID = %x\r\n",Re[0]);
return 0;
}
return 0;
}
mpu6050.h
#ifndef _MPU6050_H_
#define _MPU6050_H_
#include "stm32f4xx.h"
#include "delay.h"
#include "usart.h"
#include "iic.h"
//MPU6050的AD0是IIC地址引脚,接地则IIC地址为0x68,接VCC则IIC地址为0x69
#define MPU_INT_EN_REG 0X38 //中断使能寄存器
#define MPU_USER_CTRL_REG 0X6A //用户控制寄存器
#define MPU_FIFO_EN_REG 0X23 //FIFO使能寄存器
#define MPU6050_RA_PWR_MGMT_1 0x6B //电源管理寄存器
#define MPU_PWR_MGMT2_REG 0X6C //电源管理寄存器2
#define MPU_GYRO_CFG_REG 0X1B //陀螺仪配置寄存器
#define MPU_ACCEL_CFG_REG 0X1C //加速度计配置寄存器
#define MPU_CFG_REG 0X1A //配置寄存器
#define MPU_SAMPLE_RATE_REG 0X19 //采样频率分频器
#define MPU_INTBP_CFG_REG 0X37 //中断/旁路设置寄存器
#define MPU6050_RA_TEMP_OUT_H 0x41 //温度高位
#define MPU6050_RA_TEMP_OUT_L 0x42 //温度低位
#define MPU_ACCEL_XOUTH_REG 0X3B //加速度值,X轴高8位寄存器
#define MPU_ACCEL_XOUTL_REG 0X3C //加速度值,X轴低8位寄存器
#define MPU_ACCEL_YOUTH_REG 0X3D //加速度值,Y轴高8位寄存器
#define MPU_ACCEL_YOUTL_REG 0X3E //加速度值,Y轴低8位寄存器
#define MPU_ACCEL_ZOUTH_REG 0X3F //加速度值,Z轴高8位寄存器
#define MPU_ACCEL_ZOUTL_REG 0X40 //加速度值,Z轴低8位寄存器
#define MPU_TEMP_OUTH_REG 0X41 //温度值高八位寄存器
#define MPU_TEMP_OUTL_REG 0X42 //温度值低8位寄存器
#define MPU_GYRO_XOUTH_REG 0X43 //陀螺仪值,X轴高8位寄存器
#define MPU_GYRO_XOUTL_REG 0X44 //陀螺仪值,X轴低8位寄存器
#define MPU_GYRO_YOUTH_REG 0X45 //陀螺仪值,Y轴高8位寄存器
#define MPU_GYRO_YOUTL_REG 0X46 //陀螺仪值,Y轴低8位寄存器
#define MPU_GYRO_ZOUTH_REG 0X47 //陀螺仪值,Z轴高8位寄存器
#define MPU_GYRO_ZOUTL_REG 0X48 //陀螺仪值,Z轴低8位寄存器
char MPU6050_Init(void);
uint8_t MPU6050ReadID(void);
//
u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf);
u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf);
short MPU_Get_Temperature(void);
u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz);
u8 MPU_Get_Accelerometer(short *ax,short *ay,short *az);
#endif
IIC.c
#include "iic.h"
//PB6-SCL PB7-SDA
void IIC_GPIO_Init(void)
{
GPIO_InitTypeDef GPIOINIT={0};
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
GPIOINIT.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIOINIT.GPIO_OType = GPIO_OType_OD;
GPIOINIT.GPIO_PuPd = GPIO_PuPd_UP;
GPIOINIT.GPIO_Speed = GPIO_Speed_100MHz;
GPIOINIT.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(GPIOB,&GPIOINIT);
}
//IIC起始信号
void IIC_Start(void)
{
SDA_OUT();
SDA(1);
delay_us(5);
SCL(1);
delay_us(5);
SDA(0);
delay_us(5);
SCL(0);
delay_us(5);
}
//IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();
SCL(0);
SDA(0);
SCL(1);
delay_us(5);
SDA(1);
delay_us(5);
}
//主机发送应答
//0应答
//1非应答
char IIC_Send_Ack(unsigned char ack)
{
SDA_OUT();
SCL(0);
SDA(0);
delay_us(5);
if(!ack) SDA(0);
else SDA(1);
SCL(1);
delay_us(5);
SCL(0);
SDA(1);
return 0;
}
//等待从机应答
unsigned char IIC_Whit_Ack(void)
{
char ack = 0;
unsigned char ack_flag = 10;
// SCL(0);
// SDA(1);
SDA_IN();
delay_us(5);
SCL(1);
delay_us(5);
while( (GETSDA()==1) && ( ack_flag ) )
{
ack_flag--;
delay_us(5);
}
if( ack_flag <= 0 )
{
IIC_Stop();
return 1;
}
else
{
SCL(0);
SDA_OUT();
}
return ack;
}
//写一个字节
void IIC_Write(unsigned char dat)
{
int i = 0;
SDA_OUT();
SCL(0);//拉低时钟开始数据传输
for( i = 0; i < 8; i++ )
{
SDA( (dat & 0x80) >> 7 );
__nop();
SCL(1);
delay_us(5);
SCL(0);
delay_us(5);
dat<<=1;
}
}
//读1个字节
unsigned char IIC_Read(void)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
SCL(0);
delay_us(5);
SCL(1);
delay_us(5);
receive<<=1;
if( GETSDA() )
{
receive|=1;
}
delay_us(5);
}
SCL(0);
return receive;
}
IIC.h
#ifndef _IIC_H_
#define _IIC_H_
#include "stm32f4xx.h"
#include "delay.h"
#include "usart.h"
#define SDA_IN() {GPIOB->MODER &= ~(0X03<<14);} //设置SDA为输入模式
#define SDA_OUT() {GPIOB->MODER |= (0X01<<14);} //设置SDA为输出模式
#define SCL(BIT) GPIO_WriteBit( GPIOB, GPIO_Pin_6, BIT)
#define SDA(BIT) GPIO_WriteBit( GPIOB, GPIO_Pin_7, BIT)
#define GETSDA() GPIO_ReadInputDataBit( GPIOB, GPIO_Pin_7)
/////////////////// IIC ///////////////////
void IIC_GPIO_Init(void);
void IIC_Start(void);
void IIC_Stop(void);
char IIC_Send_Ack(unsigned char ack);
unsigned char IIC_Whit_Ack(void);
void IIC_Write(unsigned char dat);
unsigned char IIC_Read(void);
#endif
到这里可以尝试一下能不能通信成功了,我先测试了一下读取温度,是可以读出来的。
main.c
#include "stm32f4xx.h"
#include "usart.h"
#include "delay.h"
#include "iic.h"
#include "mpu6050.h"
#include "inv_mpu.h"
/***************************************
编 译 版 本:MDK-keil5.37--CC5
编 辑 时 间:2022-12-12
编 辑 人 员:老怪.
****************************************/
int main(void)
{
float pitch,roll,yaw; //欧拉角
short aacx,aacy,aacz; //加速度传感器原始数据
short gyrox,gyroy,gyroz; //陀螺仪原始数据
short temp; //温度
//中断分组 2组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//滴答定时器初始化
delay_init(168);
//串口1配置
USART1_Init();
//MPU6050的IIC引脚初始化
IIC_GPIO_Init();
//MPU6050的寄存器初始化
MPU6050_Init();
while(1)
{
printf("温度:%d\r\n",MPU_Get_Temperature()/100);
delay_ms(200);
}
}
如果想要输出角度等数据,可以使用正点原子提供的五个源码,MPU6050处理寄存器的相关数据时需要移植几个官方库,以便将数据处理为所需要的欧拉角。
将他们导入工程(记得.h也要导入!)我这里新建了一个分组导入.c
导入.h
如果使用的延时和导入的.c不一样子,可以在这里修改
完成之后直接修改一下主函数,再编译下载
main.c
#include "stm32f4xx.h"
#include "usart.h"
#include "delay.h"
#include "iic.h"
#include "mpu6050.h"
#include "inv_mpu.h"
/***************************************
编 译 版 本:MDK-keil5.37--CC5
编 辑 时 间:2022-12-12
编 辑 人 员:老怪.
****************************************/
int main(void)
{
float pitch,roll,yaw; //欧拉角
short aacx,aacy,aacz; //加速度传感器原始数据
short gyrox,gyroy,gyroz; //陀螺仪原始数据
short temp; //温度
//中断分组 2组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//滴答定时器初始化
delay_init(168);
//串口1配置
USART1_Init();
//MPU6050的IIC引脚初始化
IIC_GPIO_Init();
//MPU6050的寄存器初始化
MPU6050_Init();
//MPU6050的DMP初始化 成功返回0
while(mpu_dmp_init())
{
printf("DMP初始化失败\r\n");
delay_ms(200);
}
printf("开始\r\n");
while(1)
{
if(mpu_dmp_get_data(&pitch,&roll,&yaw)==0) //从DMP获取数据
{
temp=MPU_Get_Temperature(); //得到温度值
MPU_Get_Accelerometer(&aacx,&aacy,&aacz); //得到加速度传感器数据
MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz); //得到陀螺仪数据
printf("\r\n\r\n俯仰 =%.2f\r\n翻滚 =%.2f\r\n偏航 =%.2f\r\n温度 =%d\r\n\r\n",pitch*10,roll*10,yaw*10,temp/100);
}
delay_ms(200);
}
}
最终得到效果:
如果对其他的配置感兴趣就去正点原子吧
工程代码连接:
链接:https://pan.baidu.com/s/1Kfm0e-5cEKyiqh5pQRDtcw?pwd=1234
提取码:1234