当前位置:   article > 正文

【STM32F407学习笔记】MPU6050(一)原始数据获取_mpu6050数据采集

mpu6050数据采集

1. MPU6050简介

1.1 概述

MPU-60X0是全球首例9轴运动处理传感器。它集成了3轴MEMS陀螺仪、三轴MEMS加速度计,以及一个可扩展的数字运动处理器DMP,可用I2C接口连接一个第三方的数字传感器,比如磁力计。扩展之后就可以通过其I2C或SPI接口输出一个9轴的信号。
MPU60X0的陀螺仪和加速度计分别采用了三个16位的ADC,将其测量的模拟量转化为客户处的数字量。传感器的测量范围是用户可控的,陀螺仪可测范围为 ± 250 , ± 500 , ± 100 , ± 2000 ° / 秒 \pm250,\pm500,\pm100,\pm2000\degree/\text{秒} ±250,±500,±100,±2000°/,加速度计可测范围为 ± 2 , ± 4 , ± 8 , ± 16 g \pm2,\pm4,\pm8,\pm16g ±2,±4,±8,±16g
一个片上1024字节的FIFO,有助于降低系统功耗。
和所有设备寄存器间的通信均采用I2C接口(速度<=400KHz)。另外,片上还内嵌了一个温度传感器和在工作环境下仅有 ± 1 % \pm1\% ±1%变动的振荡器。

1.2 系统结构

在这里插入图片描述
MPU6050的系统结构如图中所示,可以清晰的观察到,MPU6050芯片中内置了三轴加速度传感器、三轴陀螺仪和一个温度传感器。右侧的INT引脚为MPU的中断输出脚,TCS为片选脚、AD0为IIC地址设置脚(AD0=0时0x68,AD0=1时0x69),SCL和SDA为主IIC接口,AUX_CL和AUX_DA为从IIC接口。主要用到的引脚就是AD0、SCL、SDA。

1.3 相关寄存器

  • 采样频率分频寄存器:SMPRT_DIV(0x19)
    在这里插入图片描述

    • SMPLRT_DIV[7:0]:配置MPU6050陀螺仪采样频率的分频值,从而配置陀螺仪的采样频率,计算公式为:
      KaTeX parse error: Expected 'EOF', got '_' at position 43: …\text{(1+SMPLRT_̲DIV)}
      其中陀螺仪的输出频率是1KHz或8KHz,与DLPF的设置相关
  • 配置寄存器:CONFIG(0x1A)
    在这里插入图片描述

    • DLPF_CFG[2:0],数字低通滤波器配置位,通过配置这些位,可以选择各种滤波参数,如下表所示:
      在这里插入图片描述
      当这三位为000时表示不使用数字低通滤波器,此时的陀螺仪时钟为8KHz;剩下的除了为111时,使用了数字低通滤波器后陀螺仪的时钟为1KHz。
  • 陀螺仪配置寄存器:GYRO_CONFIG(0x1B)
    在这里插入图片描述

    • XG_ST、YG_ST、ZG_ST:XYZ轴自测使能位
    • FS_SEL[1:0]:满量程选择位,这两位用于设置陀螺仪的满量程范围,如下所示:
      在这里插入图片描述
      一般我们设置为3,即 ± 2000 ° \pm2000\degree ±2000°,因为陀螺仪的ADC为16位,所以灵敏度为:65536/4000=16.4LSB。
    • Bit2~Bit1:未使用
  • 加速度计配置寄存器:ACCEL_CONFIG(0x1C)
    在这里插入图片描述

    • XA_ST、YA_ST、ZA_ST:XYZ轴自测使能位
    • AFS_SEL[1:0]:满量程选择位,用于设置加速度计的满量程范围,如下所示:
      在这里插入图片描述
      我们设置为3,即 ± 16 g \pm16g ±16g,因为加速度计的ADC也是16位,因此灵敏度为:65536/32=2048LSB
    • ACCEL_HPF[2:0]:高通滤波器配置位,是在内置功能的运动检测中用到,对数据输出没有影响,在这里暂时不用
  • 加速度计数据寄存器:ACCEL_XOUT_H 、ACCEL_XOUT_L、ACCEL_YOUT_H 、ACCEL_YOUT_L、ACCEL_ZOUT_H 、ACCEL_ZOUT_L(0x3B、0x3C、0x3D、0x3E、0x3F、0x40)
    在这里插入图片描述

    • ACCEL_XOUT:X轴加速度,16位的有符号数,二进制补码方式存储。通过读取ACCEL_XOUT_H左移8位或ACCEL_XOUT_L存入一个int16_t的数据即可
    • ACCEL_YOUT:Y轴加速度,16位有符号数
    • ACCEL_ZOUT:Z轴加速度,16位有符号数
  • 陀螺仪数据寄存器:GYRO_XOUT_H、GYRO_XOUT_L、GYRO_YOUT_H、GYRO_YOUT_L、GYRO_ZOUT_H、GYRO_ZOUT_L(0x43、0x44、0x45、0x46、0x47、0x48)
    在这里插入图片描述

    • GYRO_XOUT:X轴陀螺仪数据,16位的有符号数,二进制补码方式存储
    • GYRO_YOUT:Y轴陀螺仪数据,16位有符号数
    • GYRO_ZOUT:Z轴陀螺仪数据,16位有符号数
  • 温度数据寄存器:TEMP_OUT_H、TEMP_OUT_L(0x41、0x42)
    在这里插入图片描述

    • TEMP_OUT:传感器温度,16位的有符号数,以二进制补码方式存储。
  • 电源管理寄存器1:PWR_MGMT_1(0x6B)和电源管理寄存器2:PWR_MGMT_2
    在这里插入图片描述

    • DEVICE_RESET=1,复位MPU6050(所有寄存器恢复到默认值),复位完成后,该位自动清零。
    • SLEEP:睡眠模式。SLEEP=1,进入睡眠模式;SLEEP=0,正常工作模式。
    • CYCLE:循环模式。CYCLE=1,设备进入低功耗,过一段时间启动一次。唤醒频率由PWR_MGMT_2的LP_WAKE_CTRL[1:0]决定
    • TEMP_DIS:温度传感器失能。用于设置是否使能温度传感器,TEMP_DIS=0,则使能。
    • CLKSEL[2:0]:用于选择系统时钟源,如下所示:
      在这里插入图片描述
      非常建议选择陀螺仪晶振作为系统时钟源,因为它更加精确。
      在这里插入图片描述
    • LP_WAKE_CTRL[1:0]:设置CYCLE的唤醒间隔
      在这里插入图片描述
    • STBY_XA~STBY_ZG:控制六个轴进入待机模式,只需要部分轴的数据可以让别的轴待机(设置相应位=1),比较省电。
  • ID号寄存器:WHO AM I(0x75)
    在这里插入图片描述

  • WHO_AM_I[6:1]只读寄存器:寄存器内容固定为0x68,AD0引脚的值并不反映到这个寄存器中。

注意:当芯片上电复位后所有寄存器默认值均为:0x00,除了PWR_MGMT_1为:0x40(睡眠模式),以及WHO_AM_I为:0x68,因此在操作MPU6050前,需要先解除睡眠,否则操作MPU6050寄存器无效。

2. IIC通信时序实现

2.1 写操作实现

2.1.1 单字节写实现

查看数据手册可以找到I2C单字节写时序的为
在这里插入图片描述
其中S:起始信号,AD:六位IIC地址,W:写,ACK:应答信号,RA:寄存器地址,DATA:数据,P:停止信号
则根据博客【STM32F407学习笔记】模拟IIC协议可以实现单字节I2C写操作,实现如下:

/// @brief 写入指定设备,指定寄存器的一个字节
/// @param I2C_Addr 目标设备地址
/// @param reg 寄存器地址
/// @param data 写入的数据
/// @return 1失败 0成功
uint8_t IIC_WriteByteToSlave(uint8_t I2CAddr,uint8_t reg,uint8_t data)
{
	IIC_Start();
	IIC_SendByte((I2C_Addr<<1)|0); // 写入从机地址
	if(IIC_waitACK())
	{
		IIC_Stop(); 
		return 1; // 写入失败
	}
	IIC_SendByte(reg); // 发送寄存器地址
	if(IIC_waitACK())
	{
		IIC_Stop(); 
		return 1; // 写入失败
	}
	IIC_SendByte(data); // 发送数据
	if(IIC_waitACK())
	{
		IIC_Stop(); 
		return 1; // 写入失败
	}
	IIC_Stop();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

2.1.2 多字节写实现

查询数据手册,IIC多字节写入的时序为:
在这里插入图片描述
实现多字节I2C写操作,实现如下:

/// @brief 写入指定设备,指定寄存器的length个字节
/// @param I2C_Addr 目标设备地址
/// @param reg 寄存器地址
/// @param length 写入的数据长度
/// @param data 写入数据存放的指针
/// @return 1失败 0成功
uint8_t IIC_WriteByteToSlave(uint8_t I2CAddr,uint8_t reg,uint8_t length,uint8_t *data)
{
	uint8_t count=0;
	IIC_Start();
	IIC_SenByte((I2CAddr<<1)|0);// 写从机地址
	if(IIC_waitACK())
	{
		IIC_Stop(); 
		return 1; // 写入失败
	}
	IIC_SendByte(reg); // 寄存器地址
	if(IIC_waitACK())
	{
		IIC_Stop(); 
		return 1; // 写入失败
	}
	for(count=0;cout<length;count++)
	{
		IIC_SendByte(data[count]);
		if(IIC_waitACK())
		{
			IIC_Stop(); 
			return 1; // 写入失败
		}
	}
	IIC_Stop();
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

2.2 读操作实现

2.2.1 单字节读实现

查询数据手册,IIC单字节读的时序为:
在这里插入图片描述
实现单字节I2C读操作,实现如下:

/// @brief 读取指定设备,指定寄存器的一个字节
/// @param I2C_Addr 目标设备地址
/// @param reg 寄存器地址
/// @param buf 读取数据存放地址
/// @return 1失败 0成功
uint8_t ReadByteFromSlave(uint8_t I2CAddr,uint8_t reg,uint8_t *buf)
{
	IIC_Start();
	IIC_SendByte((I2CAddr<<1)|0); // I2CAddr+W 写入从机地址
	if(IIC_waitACK())
	{
		IIC_Stop(); 
		return 1; // 写入失败
	}
	IIC_SendByte(reg); // 写入寄存器地址
	if(IIC_waitACK())
	{
		IIC_Stop(); 
		return 1; // 写入失败
	}
	IIC_Start();
	IIC_SendByte((I2CAddr<<1)|1); // I2CAddr+R
	if(IIC_waitACK())
	{
		IIC_Stop(); 
		return 1; // 写入失败
	}
	*buf=IIC_ReadByte(0); // 仅读取一个字节,ReadByte(0)会返回NACK信号
	IIC_Stop();
	
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

2.2.2 多字节读实现

查询数据手册,IIC多字节读的时序为:
在这里插入图片描述实现多字节I2C读操作,实现如下:

/// @brief 读取指定设备,指定寄存器的一个字节
/// @param I2C_Addr 目标设备地址
/// @param reg 寄存器地址
/// @param length 读取的字节长度
/// @param buf 读取数据存放地址
/// @return 1失败 0成功
uint8_t ReadByteFromSlave(uint8_t I2CAddr,uint8_t reg,uint8_t length,uint8_t *buf)
{
	uint8_t count=0;
	uint8_t temp;
	IIC_Start();
	IIC_SendByte((I2CAddr<<1)|0); // I2CAddr+W 写入从机地址
	if(IIC_waitACK())
	{
		IIC_Stop(); 
		return 1; // 写入失败
	}
	IIC_SendByte(reg); // 写入寄存器地址
	if(IIC_waitACK())
	{
		IIC_Stop(); 
		return 1; // 写入失败
	}
	IIC_Start();
	IIC_SendByte((I2CAddr<<1)|1); // I2CAddr+R
	if(IIC_waitACK())
	{
		IIC_Stop(); 
		return 1; // 写入失败
	}
	for(count=0;count<length;count++)
	{
		if(count!=length-1) // 非最后一个字节
		{
			temp=IIC_ReadByte(1); // IIC_ReadByte(1) 会返回一个ACK
		}
		else // 最后一字节
		{
			temp=IIC_ReadByte(0); // IIC_ReadByte(0) 会返回一个NACK
		}
		buf[count]=temp; // 存储数据
	}
	IIC_Stop();
	
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

3. MPU6050原始数据获取

根据前述1,2小节将其综合可以实现如下的MPU6050操作

3.1 MPU6050初始化

/// @brief 检查MPU6050是否连接
/// @param  None
/// @return 0 连接 1 未连接
uint8_t MPU6050_Check(void)
{
    uint8_t ID;
    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_WHO_AM_I, &ID);
    if (ID == 0x68) // WHO AM I寄存器默认值
    {
        return 0;
    }
    return 1;
}

/// @brief MPU6050初始化
/// @param I2C_Addr MPU6050的从机IIC地址
void MPU6050_init(uint8_t I2C_Addr)
{
    MPU6050_Addr = I2C_Addr;
    // 检查MPU6050是否连接
    if (MPU6050_Check()) // 未连接
    {
        return;
    }

    // 设置MPU6050为复位状态
    IIC_WriteByteToSlave(MPU6050_Addr, MPU6050_PWR_MGMT_1, 0x80); // 设置为复位状态
    delay_ms(50);                                                 // 等待复位

    IIC_WriteByteToSlave(MPU6050_Addr, MPU6050_PWR_MGMT_1, 0x01); // 0000 0001 解除睡眠 使能温度传感器 选择时钟源使用X轴PLL作为时钟源
    IIC_WriteByteToSlave(MPU6050_Addr, MPU6050_PWR_MGMT_2, 0x00); // 输出三轴陀螺仪和三轴加速度计数据
    IIC_WriteByteToSlave(MPU6050_Addr, MPU6050_INT_ENBALE, 0x00); // 禁止中断

    IIC_WriteByteToSlave(MPU6050_Addr, MPU6050_SMPLRT_DIV, 0x09);   // 采样分频10
    IIC_WriteByteToSlave(MPU6050_Addr, MPU6050_CONFGIG, 0x06);      // 滤波参数最大
    IIC_WriteByteToSlave(MPU6050_Addr, MPU6050_GYRO_CONFIG, 0x18);  // 陀螺仪,最大量程
    IIC_WriteByteToSlave(MPU6050_Addr, MPU6050_ACCEL_CONFIG, 0x18); // 加速度计,满量程
    IIC_WriteByteToSlave(MPU6050_Addr, MPU6050_FIFO_EN, 0x00);      // 关闭FIFO
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

3.2 MPU6050 六轴数据获取

/// @brief 获取MPU6050 六轴数据
/// @param AccX X轴加速度数据
/// @param AccY Y轴加速度数据
/// @param AccZ Z轴加速度数据
/// @param GyroX X轴陀螺仪数据
/// @param GyroY Y轴陀螺仪数据
/// @param GyroZ Z轴陀螺仪数据
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;
    // 读取数据寄存器
    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_ACCEL_XOUT_H, &DataH);
    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_ACCEL_XOUT_L, &DataL);
    *AccX = (DataH << 8) | DataL;

    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_ACCEL_YOUT_H, &DataH);
    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_ACCEL_YOUT_L, &DataL);
    *AccY = (DataH << 8) | DataL;

    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_ACCEL_ZOUT_H, &DataH);
    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_ACCEL_ZOUT_L, &DataL);
    *AccZ = (DataH << 8) | DataL;

    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_GYRO_XOUT_H, &DataH);
    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_GYRO_XOUT_L, &DataL);
    *GyroX = (DataH << 8) | DataL;

    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_GYRO_YOUT_H, &DataH);
    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_GYRO_YOUT_L, &DataL);
    *GyroY = (DataH << 8) | DataL;

    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_GYRO_ZOUT_H, &DataH);
    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_GYRO_ZOUT_L, &DataL);
    *GyroZ = (DataH << 8) | DataL;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

3.3 温度获取

/// @brief 获取MPU6050当前温度
/// @param Tempdata 当前温度
void MPU6050_GetTemp(float *Tempdata)
{
    uint8_t TempH, TempL;
    short data;
    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_TEMP_OUT_H, &TempH);
    IIC_ReadByteFromSlave(MPU6050_Addr, MPU6050_TEMP_OUT_H, &TempL);

    data = (int16_t)((TempH << 8) | TempL);
    *Tempdata = (36.53f + (float)data / 340);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/666688
推荐阅读
相关标签
  

闽ICP备14008679号