这个传感器学起来还是挺费劲的,找的资料都是参差不齐的,最好的学习方法当然是读datasheet,当然由于都是英文,有人看的头大,所以我尽量将datasheet作详细解释
参考了几个帖子,大家可以去看看
阿西莫夫电子论坛:http://www.amobbs.com/thread-5581033-1-1.html?_dsign=5c5b5cde
第七实验室:https://wenku.baidu.com/view/89db87ac7375a417876f8f6b.html
博主ReCclay:https://blog.csdn.net/ReCclay/article/details/82829280
datasheet下载(分 产品介绍和 寄存器介绍两种)
MPU6050是什么
The MPU-60X0 Motion Processing Unit is the world’s first motion processing solution with integrated 9-Axis
sensor fusion using its field-proven and proprietary MotionFusion™ engine for handset and tablet
applications, game controllers, motion pointer remote controls, and other consumer devices. The MPU-60X0
has an embedded 3-axis MEMS gyroscope, a 3-axis MEMS accelerometer, and a Digital Motion
Processor™ (DMP™) hardware accelerator engine with an auxiliary I2C port that interfaces to 3rd party digital
sensors such as magnetometers. When connected to a 3-axis magnetometer, the MPU-60X0 delivers a
complete 9-axis MotionFusion output to its primary I2C or SPI port (SPI is available on MPU-6000 only).
(引用自datasheet)
具有三轴MEMS陀螺仪(Gyroscope)、三轴MEMS加速度计(Accelerometer)和数字运动处理器(DMP),也可以通过IIC接口接入第三方传感器如磁力计组成九轴传感器,利用iic或spi与主机进行通讯
具体特点
- 数字输出X、Y和Z轴角速率传感器(陀螺仪),用户可编程满刻度范围为±250、±500、±1000和±2000°/sec。
- 集成16位ADC可同时对陀螺仪进行采样。
- 数字输出三轴加速度计,可编程满刻度范围为±2g、±4g、±8g和±16g
- 集成16位ADC可在不需要外部设备的情况下对加速度计进行同步采样。
- 以数字形式输出 6 轴或 9 轴(需外接磁传感器)的旋转矩阵、四元数
(quaternion)、欧拉角格式的融合演算数据(需 DMP 支持)。 - 自带数字运动处理(DMP: Digital Motion Processing)引擎可减少 MCU 复杂
的融合演算数据、感测器同步化、姿势感应等的负荷
原理图和封装图
传感器原理
https://www.cnblogs.com/Jeanco/p/8966407.html
https://wenku.baidu.com/view/89db87ac7375a417876f8f6b.html
传感器参考坐标系(正向用右手螺旋定律)
MPU6050数据
对于这类传感器,我们需要了解的分别是它的数据组成和如何读取数据
数据组成
陀螺仪
陀螺仪直接输出的是角速度,角速度是指物体在单位时间内转过的角度大小,对角速度积分可以得到角度。
数字输出X、Y和Z轴角速率传感器(陀螺仪),用户可编程满刻度范围为±250、±500、±1000和±2000°/sec。
集成16位ADC可同时对陀螺仪进行采样。
以上信息可以得到,陀螺仪的量程有±250、±500、±1000和±2000°/sec这四个,同时用16位数据存储
因为16位数据就是2的十六次方也就是65536,由于最高位在这为符号位,所以相当于-7FFF,即-32767,最大的数是7FFF,即32767。
如果全量程(Full-Scale Range)是±250,对应着32767,也就是一个刻度就是32767/250=131,也就是上面的灵敏度比例因子(Sensitivity Scale Factor)
把从陀螺仪读出的数字除以131,就可以换算成陀螺仪的角速度数值。
加速度计
加速度计输出的是倾斜角。
分析方法同以上的陀螺仪,当AFS_SEL=0时,数字-32767对应-2g,32767对应2g。把32767除以2,就可以得到16384, 即灵敏度比例因子。把从加速度计读出的数字除以16384,就可以换算成加速度的数值。举个例子,如果我们从加速度计读到的数字是1000,那么对应的加速度数据是1000/16384=0.49g。g为加速度的单位,重力加速度定义为1g, 等于9.8米每平方秒。
读取原始数据
数据读取用的是iic协议
MPU6050有专门讲寄存器的datasheet
MPU系统框图
关于IIC
IIC基础
IIC协议是一种常见的通讯协议
分软件IIC和硬件IIC
软件IIC就是通过GPIO,软件模拟寄存器的工作方式,硬件(固件)I2C是直接调用内部寄存器进行配置。
这个网络学习资源一大把,随便找个看看就行了
贴个教程:https://blog.csdn.net/lingdongtianxia/article/details/81135456
这里给一个51常用的软件IIC程序
typedef unsigned char u8;
sbit SCL=P3^2; //IIC时钟引脚定义
sbit SDA=P3^3; //IIC数据引脚定义
//延时程序,根据单片机的不同调整
void Delay5us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 11;
while (--i);
}
//**************************************
//I2C起始信号
//**************************************
void I2C_Start()
{
SDA = 1; //拉高数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 0; //产生下降沿
Delay5us(); //延时
SCL = 0; //拉低时钟线
}
//**************************************
//I2C停止信号
//**************************************
void I2C_Stop()
{
SDA = 0; //拉低数据线
SCL = 1; //拉高时钟线
Delay5us(); //延时
SDA = 1; //产生上升沿
Delay5us(); //延时
}
//**************************************
//I2C发送应答信号
//入口参数:ack (0:ACK 1:NAK)
//**************************************
void I2C_SendACK(bit ack)
{
SDA = ack; //写应答信号
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
//**************************************
//I2C接收应答信号
//**************************************
bit I2C_RecvACK()
{
SCL = 1; //拉高时钟线
Delay5us(); //延时
CY = SDA; //读应答信号
SCL = 0; //拉低时钟线
Delay5us(); //延时
return CY;
}
//**************************************
//向I2C总线发送一个字节数据
//**************************************
void I2C_SendByte(u8 dat)
{
u8 i;
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1; //移出数据的最高位
SDA = CY; //送数据口
SCL = 1; //拉高时钟线
Delay5us(); //延时
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
I2C_RecvACK();
}
//**************************************
//从I2C总线接收一个字节数据
//**************************************
u8 I2C_RecvByte()
{
u8 i;
u8 dat = 0;
SDA = 1; //使能内部上拉,准备读取数据,
for (i=0; i<8; i++) //8位计数器
{
dat <<= 1;
SCL = 1; //拉高时钟线
Delay5us(); //延时
dat |= SDA; //读数据
SCL = 0; //拉低时钟线
Delay5us(); //延时
}
return dat;
}
MPU6050的IIC读写时序
1.1单个字节写
步骤:开始信号–>发送地址位+发送一个 R/W 位(0 写,1 读)–>等待应答–>发送寄存器地址–>等待应答–>发送数据–>等待应答–>结束信号
代码
#define SlaveAddress 0xD0 //IIC写入时的地址字节数据,+1为读取
//**************************************
//向I2C设备写入一个字节数据
//**************************************
void Single_WriteI2C(u8 REG_Address,u8 REG_data)
{
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress); //发送设备地址+写信号
I2C_SendByte(REG_Address); //内部寄存器地址,
I2C_SendByte(REG_data); //内部寄存器数据,
I2C_Stop(); //发送停止信号
}
1.2单个字节读
步骤:开始信号–>发送地址位+发送一个 R/W 位(0 写,1 读)–>等待应答–>发送寄存器地址–>等待应答–>开始信号–>发送地址位+发送一个 R/W 位(0 写,1 读)–>等待应答–>读取数据–>不应答–>结束信号
代码
#define SlaveAddress 0xD0 //IIC写入时的地址字节数据,+1为读取
//**************************************
//从I2C设备读取一个字节数据
//**************************************
u8 Single_ReadI2C(u8 REG_Address)
{
u8 REG_data;
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress); //发送设备地址+写信号
I2C_SendByte(REG_Address); //发送存储单元地址,从0开始
I2C_Start(); //起始信号
I2C_SendByte(SlaveAddress+1); //发送设备地址+读信号
REG_data=I2C_RecvByte(); //读出寄存器数据
I2C_SendACK(1); //接收应答信号
I2C_Stop(); //停止信号
return REG_data;
}
2.1 快速写
2.2 快速读
*重要寄存器
配置寄存器
寄存器107-电源管理寄存器1 地址:0x6B
宏定义
#define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用)
详细介绍:
DEVICE_RESET 位用来控制复位,设置为 1,复位 MPU6050,复位结束后, MPU硬件自动清零该位
SLEEEP 位用于控制 MPU6050 的工作模式,复位后,该位为 1,即进
入了睡眠模式(低功耗),所以我们要清零该位,以进入正常工作模式
TEMP_DIS 用于设置是否使能温度传感器,设置为 0,则使能
CLKSEL[2:0] 用于选择系统时钟源,选择关系如表
翻译以下
CLKSEL[2:0] | 时钟源 |
---|---|
000 | 内部 8M RC 晶振 |
001 | PLL,使用X轴陀螺仪作为参考 |
010 | PLL,使用Y轴陀螺仪作为参考 |
011 | PLL,使用Z轴陀螺仪作为参考 |
100 | PLL,使用外部32.768khz作为参考 |
101 | PLL,使用外部19.2Mhz作为参考 |
110 | 保留 |
111 | 关闭时钟,保持时序产生电路复位状态 |
默认是使用内部 8M RC 晶振的,精度不高,所以我们一般选择 X/Y/Z 轴陀螺作为参考的PLL 作为时钟源,一般设置 CLKSEL=001 即可
寄存器25-陀螺仪采样率分频寄存器 地址:0x19
宏定义
#define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz)
相关配置:
该寄存器用于设置 MPU6050 的陀螺仪采样频率,计算公式为:
采样频率 = 陀螺仪输出频率 / (1+SMPLRT_DIV)
这里陀螺仪的输出频率,是 1Khz 或者 8Khz,与数字低通滤波器( DLPF)的设置有关,当 DLPF_CFG=0/7 的时候,频率为 8Khz,其他情况是 1Khz。而且 DLPF 滤波频率一般设置 为采样率的一半。采样率,我们假定设置为 125Hz,那么 SMPLRT_DIV=1000/150-1=7
寄存器26- 配置寄存器 地址:0x1A
宏定义
#define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz)
相关配置:
数字低通滤波器( DLPF)的设置位,即: DLPF_CFG[2:0],加速
度计和陀螺仪,都是根据这三个位的配置进行过滤的。 DLPF_CFG 不同配置对应的过滤情况如表:
这里的加速度传感器,输出速率( Fs)固定是 1Khz,而角速度传感器的输出速率( Fs),
则根据 DLPF_CFG 的配置有所不同。一般我们设置角速度传感器的带宽为其采样率的一半,
如前面所说的,如果设置采样率为 50Hz,那么带宽就应该设置为 25Hz,取近似值 20Hz,
就应该设置 DLPF_CFG=100
寄存器27- 陀螺仪配置寄存器 地址:0x1B
宏定义
#define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
相关配置:
FS_SEL[1:0]这两个位,用于设置陀螺仪的满量程范围:
0,±250° /S
1,±500° /S
2,±1000° /S
3,±2000° /S
我们一般设置为 3,即±2000° /S,因
为陀螺仪的 ADC 为 16 位分辨率,所以得到灵敏度为: 65536/4000=16.4LSB/(° /S)
寄存器28- 加速度传感器配置寄存器 地址:0x1C
宏定义
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
相关配置:
AFS_SEL[1:0]这两个位,用于设置加速度传感器的满量程范围:
0, ±2g;
1,±4g;
2,±8g;
3,±16g;
我们一般设置为 0,即±2g,因为加速度传感器的
ADC 也是 16 位,所以得到灵敏度为: 65536/4=16384LSB/g
寄存器35-FIFO使能寄存器 地址:0x23
宏定义
#define FIFO_EN 0x23
相关配置:
该寄存器用于控制 FIFO 使能,在简单读取传感器数据的时候,可以不用 FIFO,设置
对应位为 0 即可禁止 FIFO,设置为 1,则使能 FIFO
加速度传感器的 3 个轴,全由 1 个位( ACCEL_FIFO_EN)控制,只要该位置 1,则加速度传感器的三个通道都开启 FIFO
读取数据地址寄存器(只读)
寄存器67to72-陀螺仪数据输出寄存器 地址:0x43 to 0x48
宏定义
#define GYRO_XOUT_H 0x43 //陀螺仪X轴高八位数据地址
#define GYRO_XOUT_L 0x44 //陀螺仪X轴低八位数据地址
#define GYRO_YOUT_H 0x45 //陀螺仪Y轴高八位数据地址
#define GYRO_YOUT_L 0x46 //陀螺仪Y轴低八位数据地址
#define GYRO_ZOUT_H 0x47 //陀螺仪Z轴高八位数据地址
#define GYRO_ZOUT_L 0x48 //陀螺仪Z轴低八位数据地址
通过读取这6个寄存器,就可以读到陀螺仪 x/y/z 轴的值,比如 x 轴的数据,可以通过读取
0X43(高 8 位)和 0X44(低 8 位)寄存器得到,其他轴以此类推
寄存器59to64-加速度传感器数据输出寄存器 地址:0x3B to 0x40
宏定义
#define ACCEL_XOUT_H 0x3B //加速度计X轴高八位数据地址
#define ACCEL_XOUT_L 0x3C //加速度计X轴低八位数据地址
#define ACCEL_YOUT_H 0x3D //加速度计Y轴高八位数据地址
#define ACCEL_YOUT_L 0x3E //加速度计Y轴低八位数据地址
#define ACCEL_ZOUT_H 0x3F //加速度计Z轴高八位数据地址
#define ACCEL_ZOUT_L 0x40 //加速度计Z轴地八位数据地址
通过读取这6个寄存器,就可以读到加速度传感器 x/y/z 轴的值,比如读 x 轴的数据,可以通过读取 0X3B(高 8 位)和0X3C(低8位)寄存器得到,其他轴以此类推
寄存器65to66-加速度传感器数据输出寄存器 地址:0x41 to 0x42
宏定义
#define TEMP_OUT_H 0x41 //温度传感器高八位数据
#define TEMP_OUT_L 0x42 //温度传感器低八位数据
温度传感器的值,可以通过读取 0X41(高 8 位)和 0X42(低 8 位)寄存器得到,
温度换算公式为:
Temperature = 36.53 + regval/340
其中, Temperature 为计算得到的温度值,单位为℃,regval 为从 0X41 和 0X42 读到的温度传感器值
以上就是寄存器的内容
总结一下所有的
宏定义
#define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用)
#define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz)
#define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz)
#define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
#define GYRO_XOUT_H 0x43 //陀螺仪X轴高八位数据地址
#define GYRO_XOUT_L 0x44 //陀螺仪X轴低八位数据地址
#define GYRO_YOUT_H 0x45 //陀螺仪Y轴高八位数据地址
#define GYRO_YOUT_L 0x46 //陀螺仪Y轴低八位数据地址
#define GYRO_ZOUT_H 0x47 //陀螺仪Z轴高八位数据地址
#define GYRO_ZOUT_L 0x48 //陀螺仪Z轴低八位数据地址
#define ACCEL_XOUT_H 0x3B //加速度计X轴高八位数据地址
#define ACCEL_XOUT_L 0x3C //加速度计X轴低八位数据地址
#define ACCEL_YOUT_H 0x3D //加速度计Y轴高八位数据地址
#define ACCEL_YOUT_L 0x3E //加速度计Y轴低八位数据地址
#define ACCEL_ZOUT_H 0x3F //加速度计Z轴高八位数据地址
#define ACCEL_ZOUT_L 0x40 //加速度计Z轴地八位数据地址
#define TEMP_OUT_H 0x41 //温度传感器高八位数据
#define TEMP_OUT_L 0x42 //温度传感器低八位数据
#define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读)
#define SlaveAddress 0xD0 //IIC写入时的地址字节数据,+1为读取
MPU读取原始数据程序
51版
//**************************************
//初始化MPU6050
//**************************************
void InitMPU6050()
{
Single_WriteI2C(PWR_MGMT_1, 0x00); //解除休眠状态
Single_WriteI2C(SMPLRT_DIV, 0x07); //配置陀螺仪采样率
Single_WriteI2C(CONFIG, 0x06); //配置低通滤波频率
Single_WriteI2C(GYRO_CONFIG, 0x18); //配置陀螺仪自检及测量范围
Single_WriteI2C(ACCEL_CONFIG, 0x01); //配置加速计自检、测量范围及高通滤波频率
}
//**************************************
//合成数据
//**************************************
int GetData(u8 REG_Address)
{
u8 H,L;
H=Single_ReadI2C(REG_Address);
L=Single_ReadI2C(REG_Address+1);
return (H<<8)+L; //合成数据
}
//显示函数
void Display10BitData(int value,u8 x,u8 y)
{ u8 i;
// value/=64; //转换为10位数据
lcd_printf(dis, value); //转换数据显示
// for(i=0;i<6;i++)
// {
// printf(dis);//发送到串口
// }
OLED_ShowString(x,y,dis,16);
// DisplayListChar(x,y,dis,4); //启始列,行,显示数组,显示长度
}
void main()
{
delay(500);
Config_UARTimer(9600);//串口配置
OLED_Init();//oled初始化
InitMPU6050();//mpu初始化
while(1)
{
OLED_Clear();//清屏
Display10BitData(GetData(ACCEL_XOUT_H),0,0); //显示X轴加速度
Display10BitData(GetData(ACCEL_YOUT_H),64,0); //显示Y轴加速度
Display10BitData(GetData(ACCEL_ZOUT_H),0,2); //显示Z轴加速度
Display10BitData(GetData(GYRO_XOUT_H),0,4); //显示X轴角速度
Display10BitData(GetData(GYRO_YOUT_H),64,4); //显示Y轴角速度
Display10BitData(GetData(GYRO_ZOUT_H),0,6); //显示Z轴角速度
}
显示效果
至此,原始数据就已经读出来了
stm32版程序
数据处理和姿态解算
原始数据于我们来说是毫无用途的,我们要进行转换,将原始数据转换成姿态数据
有两个方法,
一个是通过数学公式,将原始数据计算出姿态数据
第二个是用MPU6050内部的DMP,从这里读取数据出来
对于我当然是选择后者了,当然更推荐后者,因为DMP可以对数据进行滤波,同时节省主控芯片的算力
官方DMP移植
官方DMP程序
网上稍微百度一下应该就找到了
这里给一个链接提供下载
链接:https://pan.baidu.com/s/15eU8zVXgvFWeERb7KjgJNw 密码:rkw9
这里官方DMP给的是基于msp430的代码,对于我们要把它改成基于stm32的,本来我是打算自己改的,参照小马哥的,主要是改一些调用函数的接口,和一些函数的格式
https://www.moore8.com/courses/trainning_playback/1405
但是后来看到了正点原子官方有改过的,还改什么,直接拉过来用了。
接口程序下载
链接:https://pan.baidu.com/s/1m9J2JO3xnO2dbyihcfqIxg 密码:n1gx
由于程序比较多,所以这里就不全都贴出来了
只贴关键的代码
inv_mpu.c
这里将iic接口换成自己的接口,和正点原子的iic程序配套,所以不用动
在这个文件里加了两个函数,主要也就是用这两个函数
u8 mpu_dmp_init(void);//DMP初始化
u8 mpu_dmp_get_data(float *pitch,float *roll,float *yaw);//获得姿态数据
mpu6050.c
这个文件里就放着我上面说的那些,主要是初始化MPU6050,和获取原始数据用的
初始化
u8 MPU_Init(void)
{
u8 res;
MPU_IIC_Init();//初始化IIC总线
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X80); //复位mpu6050
delay_ms(100);
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X00); //唤醒MPU6050
MPU_Set_Gyro_Fsr(3); //陀螺仪传感器2000dps
MPU_Set_Accel_Fsr(0); //加速度传感器2g
MPU_Set_Rate(50); //设置采样率50hz
MPU_Write_Byte(MPU_INT_EN_REG,0X00); //关闭所有中断
MPU_Write_Byte(MPU_USER_CTRL_REG,0X00); //I2C主模式关闭
MPU_Write_Byte(MPU_FIFO_EN_REG,0X00); //关闭FIFO
MPU_Write_Byte(MPU_INTBP_CFG_REG,0X80); //INT引脚低电平有效
res=MPU_Read_Byte(MPU_DEVICE_ID_REG);
if(res==MPU_ADDR)//器件ID正确
{
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X01); //设置CLKSEL,PLL X轴作参考
MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X00); //加速度与陀螺仪都工作
MPU_Set_Rate(50); //设置采样率50hz
}else return 1;
return 0;
}
获取原始数据-角速度,角加速度,温度
//得到温度值(原始值)
short MPU_Get_Temperature(void)
{
u8 buf[2];
short raw;
float temp;
MPU_Read_Len(MPU_ADDR,MPU_TEMP_OUTH_REG,2,buf);
raw=((u16)buf[0]<<8)|buf[1];
temp=36.53+((double)raw)/340;
return temp*100;;
}
//得到陀螺仪原始值
u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz)
{
u8 buf[6],res;
res=MPU_Read_Len(MPU_ADDR,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;;
}
//得到加速度原始值
u8 MPU_Get_Accelerometer(short *ax,short *ay,short *az)
{
u8 buf[6],res;
res=MPU_Read_Len(MPU_ADDR,MPU_ACCEL_XOUTH_REG,6,buf);
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;;
}
测试
准备好OLED或者LCD接口代码
首先,测试OLED正常
第二步,确认串口没问题
注:不要嫌麻烦,一个模块一个模块的确认是最保险的方式
第三步:获取数据通过串口发出
现在获得的都是姿态数据了
然后显示到OLED上
姿态解算
姿态数据得到之后,就是对数据进行处理