目录
本文是在上一篇文章的基础上进行的,对于IIC通信协议不懂的可以看上一篇博文MPU6050实验(一)IIC原理
由于要用到MPU6050的寄存器,所以要参考MPU6050的寄存器手册。
一、硬件资源
STM32F401,MPU6050,OLED显示屏
二、实验原理
初识MPU6050
MPU6050内部整合了三轴陀螺仪、三轴加速度计,而且还可以连接一个第三方数字传感器(如磁力
计),这样的话,就可以通过IIC接口输出一个9轴信号。陀螺仪的数据和加速度计的数据经过数据融合(互补滤波、卡尔曼滤波等等)才可以得到正确的⻆速度、加速度。
各轴采集到的数据经过ADC模数转换之后变为电平信号存入寄存器。
各方向确定如下:
MPU6050内的寄存器
寄存器的地址可以在MPU6050的说明书Register Map找到,下面对Map进行说明:
Addr(Hex)十六进制地址,Addr(Dec)十进制地址,Serial属性,R/W为可读可写,R为只读
在对寄存器进行写操作之前要解除芯片的睡眠模式,由PWR_MGMT_1电源管理寄存器的sleep位控制,写0解除睡眠。如下图
1.SMPLRT_DIV 分频寄存器
分频越小内部AD转换越快,数据寄存器刷新越快
Sample Rate = Gyroscope Output Rate陀螺仪时钟/(1+SMPLRT_DIV)
不使用低通滤波器时,陀螺仪时钟为8KHz,使用时为1KHz
2.CONFIG 配置寄存器
外部同步设置和低通滤波器配置,配置滤波器参数越大,输出数据抖动越小。
3.GYRO_CONFIG 陀螺仪配置寄存器
高3位是XYZ轴的自测使能位,
Self-test response = Sensor output with self-test enabled - Sensor output without self-
test enabled(自测响应=使能-失能)
中间两位是满量程选择位。
4.ACCEL_CONFIG 加速度计配置寄存器
高3位是XYZ轴的自测使能位,中间两位是满量程选择位,最后三位配置高通滤波器
ACCEL_XOUT高位左移8位,或上低位数据,存入一个int16_t的变量
5.PWR_MGMT_1
第一位设备复位,写1所有寄存器恢复到默认值
第二位,睡眠模式,3循环模式,唤醒频率由PWR_MGMT_2的高2位确定
Bit3温度传感器的使能和失能,最后三位选择系统时钟来源
6.PWR_MGMT_2
用来控制6个轴进入待机模式
7.WHO_AM_I
属性为只读,内容为ID号,默认值为0x68
8.ACCEL_XOUT_H和ACCEL_XOUT_L
每个寄存器只有八位,所以读到的数据分为高八位和低八位存放,最后将高八位进行移位操作或上低八位得到十六位的数据。
其他轴的和GYRO的寄存器也是同样的道理。
结果分析
x为测得的数据,y为实际加速度,由x计算得到y的公式为x/32768=y/16(g)
三、代码部分
MPU6050.c
这部分要包含头文件,其中有寄存器对应的地址。
由于函数MPU6050_GetData要返回6个轴的传感器数据,本实验用指针传递。
#include "stm32f4xx.h" // Device header
#include "MyI2C.h"
#include "MPU6050.h"
#define MPU6050_ADDRESS 0xD0 //为了方便修改参数,突出是从机
void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data){//指定地址写寄存器
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveAck();//从机收到后应答
MyI2C_SendByte(RegAddress);//指定寄存器
MyI2C_ReceiveAck();
MyI2C_SendByte(Data);//数据
MyI2C_ReceiveAck();
MyI2C_Stop();
}
uint8_t MPU6050_ReadReg(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_WriteReg(PWR_MGMT_1,0X01);//解除睡眠,选择陀螺仪时钟
MPU6050_WriteReg(PWR_MGMT_2,0X00);//6轴均不待机
MPU6050_WriteReg(SMPLRT_DIV,0X09);//陀螺仪采样率为1K/(1+0x09)=100Hz
MPU6050_WriteReg(CONFIG,0X06);//对应外部同步和低通滤波设置
MPU6050_WriteReg(GYRO_CONFIG,0X18);//自测使能,满量程选择
MPU6050_WriteReg(ACCEL_CONFIG,0X18);//自测使能,满量程选择,高通滤波器
}
uint8_t MPU6050_GetID(){//返回MPU6050的ID
return MPU6050_ReadReg(WHO_AM_I);
}
//用指针,进行变量的地址传递,实现多返回值
void MPU6050_GetData(int16_t *AccX,int16_t *AccY,int16_t *AccZ,
int16_t *GyroX,int16_t *GyroY,int16_t *GyroZ)
{
uint8_t DataH,DataL;//分别接收高八位和低八位的数据
DataH = MPU6050_ReadReg(ACCEL_XOUT_H);
DataL = MPU6050_ReadReg(ACCEL_XOUT_L);
*AccX = (DataH << 8) | DataL;//高八位左移八位或低八位得到十六位的数据
DataH = MPU6050_ReadReg(ACCEL_YOUT_H);
DataL = MPU6050_ReadReg(ACCEL_YOUT_L);
*AccY = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(ACCEL_ZOUT_H);
DataL = MPU6050_ReadReg(ACCEL_ZOUT_L);
*AccZ = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(GYRO_XOUT_H);
DataL = MPU6050_ReadReg(GYRO_XOUT_L);
*GyroX = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(GYRO_XOUT_H);
DataL = MPU6050_ReadReg(GYRO_XOUT_L);
*GyroY = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(GYRO_XOUT_H);
DataL = MPU6050_ReadReg(GYRO_XOUT_L);
*GyroZ = (DataH << 8) | DataL;
}
MPU605.h
其中有寄存器的地址宏定义,对寄存器用途不清楚可以参考手册
#ifndef MPU6050_H
#define MPU6050_H
#define SMPLRT_DIV 0x19
#define CONFIG 0x1A
#define GYRO_CONFIG 0x1B
#define ACCEL_CONFIG 0x1C
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48
#define PWR_MGMT_1 0x6B
#define PWR_MGMT_2 0x6C
#define WHO_AM_I 0x75
void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);
void MPU6050_Init(void);
uint8_t MPU6050_GetID();
void MPU6050_GetData(int16_t *AccX,int16_t *AccY,int16_t *AccZ,
int16_t *GyroX,int16_t *GyroY,int16_t *GyroZ);
#endif
main.c
将测得的数据分两列输出左边为加速度,右边为角速度
#include "stm32f4xx.h" // Device header
#include "oled.h"
#include "MPU6050.h"
int16_t AX,AY,AZ,GX,GY,GZ;
int main(void){
OLED_Init();
MPU6050_Init();
OLED_ShowString(1,1,"ID");
uint8_t ID;
OLED_ShowHexNum(1,4,ID,2);//显示MPU6050地址
while(1){
MPU6050_GetData(&AX,&AY,&AZ,&GX,&GY,&GZ);
OLED_ShowSignedNum(2,1,AX,5);
OLED_ShowSignedNum(3,1,AY,5);
OLED_ShowSignedNum(4,1,AZ,5);
OLED_ShowSignedNum(2,8,GX,5);
OLED_ShowSignedNum(3,8,GY,5);
OLED_ShowSignedNum(4,8,GZ,5);
}
}