MPU6050
一、简介
MPU6050是一款6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景。
- 3轴加速度计(Accelerometer)
测量X、Y、Z轴的加速度 - 3轴陀螺仪传感器(Gyroscope)
测量X、Y、Z轴的角速度
详细信息可参考产品说明书
二、软件I2C
这里给出STM32F4XXX软件I2C代码
#include "stm32f4xx.h" // Device header
#include "Delay.h"
//软件I2C,SDA和SCL引脚根据需要配置
//GPIO全在AHB1总线,这里选择PD2作SCL和PD3作SDA
/*封装I2C引脚电平函数*/
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOD, GPIO_Pin_2, (BitAction)BitValue);
Delay_us(10);
}
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIOD, GPIO_Pin_3, (BitAction)BitValue);
Delay_us(10);
}
uint8_t MyI2C_R_SDA()
{
uint8_t BitValue = GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_3);//主机读取SDA引脚电平
Delay_us(10);
return BitValue;
}
/*I2C初始化函数,SDA和SCL同时为高电平*/
void MyI2C_Init()
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_SetBits(GPIOD, GPIO_Pin_2 | GPIO_Pin_3);//I2C处于空闲状态
}
/*I2C起始条件*/
void MyI2C_Start()
{
//在SCL高电平期间,SDA从高电平切换到低电平,先拉低SDA再拉低SCL
MyI2C_W_SDA(1);//先释放SDA
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
/*I2C终止条件*/
void MyI2C_End()
{
//在SCL高电平期间,SDA从低电平切换到高电平,先拉高SCL再拉高SDA
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
/*发送一个字节*/
void MyI2C_SendByte(uint8_t Byte)
{
//在SCL低电平期间,主机将数据依次放到SDA线上(高位先行)然后释放SCL
for(uint8_t i = 0; i < 8; i ++)
{
MyI2C_W_SDA(Byte & (0x80 >> i));//通过按位与将数据以高位先行的顺序写传输
MyI2C_W_SCL(1);//从机在SCL高电平期间读取数据位
MyI2C_W_SCL(0);//保证SCL以低电平结束确保下次数据传输
}
}
/*接收一个字节*/
uint8_t MyI2C_ReceiveByte()
{
//SCL低电平期间,从机将数据依次放到SDA线上(高位先行)然后释放SCL
uint8_t Byte = 0x00;
MyI2C_W_SDA(1);//为了防止主机干扰从机写入数据,需要主机释放SDA即转换为接收模式
for(uint8_t i = 0; i < 8; i ++)
{
MyI2C_W_SCL(1);//主机在SCL高电平期间读取数据位
if(MyI2C_R_SDA() == 1) Byte |= (0x80 >> i);
MyI2C_W_SCL(0);
}
return Byte;
}
/*发送应答*/
//主机在接收完一个字节之后,在下一个时钟发送一位数据,0表示应答,1表示非应答
void MyI2C_SendAck(uint8_t AckBit)
{
MyI2C_W_SDA(AckBit);
MyI2C_W_SCL(1);//从机在SCL高电平期间读取应答位
MyI2C_W_SCL(0);
}
/*接收应答*/
//主机在发送完一个字节之后,在下一个时钟接收一位数据以判断从机是否应答,0表示应答,1表示非应答
uint8_t MyI2C_ReceiveAck()
{
uint8_t AckBit;
MyI2C_W_SDA(1);//主机在接受之前要释放SDA,切换到接收状态
MyI2C_W_SCL(1);//主机在SCL高电平期间读取应答位
AckBit = MyI2C_R_SDA();
MyI2C_W_SCL(0);
return AckBit;
}
三、MPU6050主要寄存器
-
SMPLRT_DIV
采样频率分频器
用于配置采样频率的分频系数,分频越小内部AD转换越快,参照公式: 采样频率 = 陀螺仪输出时钟频率 1 + 分频值 采样频率 = \frac{陀螺仪输出时钟频率}{1+分频值} 采样频率=1+分频值陀螺仪输出时钟频率不使用低通滤波器时,陀螺仪时钟为8KHz。 -
CONFIG
配置寄存器
分两部分,外部同步设置和低通滤波器配置,一般只涉及到低通滤波器使数据更加平滑。 -
GYRO_CONFIG
陀螺仪配置寄存器
高三位是XYZ轴的自测使能位,中间两位是满量程选择位,低三位没用到。 -
ACCEL_CONFIG
加速度计配置寄存器
加速度计配置寄存器和上面一样,只不过第三位是配置高通滤波器进行运动检测。 -
数据寄存器
I2C从此处读取所需要的数据,是一个16位的有符号数,以二进制补码方式存储。读出高八位和第八位,高位左移八次或上低位数据即为原数据 -
电源管理寄存器
寄存器1可用于设备复位、睡眠模式、循环模式(省电,唤醒的频率由寄存器2决定)、温度传感器失能和选择系统时钟来源(陀螺仪的晶振更加准确)。
寄存器2可用于设置唤醒频率、分别控制6个轴进入待机模式 -
器件ID
这个寄存器只读,中间六位固定为1101 00,即I2C地址。
所有的寄存器上电默认值为0x00,除了107寄存器(0x40)和117寄存器(0x68)
四、MPU6050驱动
这里给出读一个字节的函数代码
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_End();
return Data;
}
对于在寄存器中写数据,需要根据寄存器地址进行操作,为方便对寄存器地址的调用这里用头文件包含所要用到的寄存器
#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H
//MPU6050主要寄存器地址
#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 //电源管理寄存器1
#define MPU6050_PWR_MGMT_2 0x6C //电源管理寄存器2
#define MPU6050_WHO_AM_I 0x75 //器件ID
#endif
要在MPU6050寄存器中写数据,首先要解除睡眠模式,这里给出MPU6050初始化代码
void MPU6050_Init()
{
MyI2C_Init();
MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//解除睡眠模式,同时配置x轴陀螺仪时钟
MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x01);//不需要循环模式同时不需要待机
MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//设置10分频
MPU6050_WriteReg(MPU6050_CONFIG, 0x06);//不需要外部同步,低通滤波设置为110
MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);//不需要自测,选择最大量程
MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);//不需要自测,选择最大量程,不需要高通滤波
}
初始化之后,陀螺仪就不断的产生数据存在寄存器中,不断读取数据寄存器数值即可
//读取寄存器数据值
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;
//寄存器中的值是16位有符号数,以二进制补码形式存储
DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);//高8位
DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);//低8位
*AccX = (DataH << 8) | DataL;//拼接为16位原数据
DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);
*AccY = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);
DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);
*AccZ = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);
*GyroX = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);
*GyroY = (DataH << 8) | DataL;
DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);
DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);
*GyroZ = (DataH << 8) | DataL;
}