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

本文详细介绍了如何使用STM32F407通过IIC协议与MPU6050运动传感器进行通信,包括MPU6050的硬件特性、寄存器配置、IIC通信时序的单字节和多字节读写实现,以及MPU6050的初始化、六轴数据和温度的获取方法。
摘要由CSDN通过智能技术生成

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;
}

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;
}

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;
}

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;
}

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
}

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;
}

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);
}
  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
使用STM32F407读取MPU6050的数据可以通过以下例程进行实现: 首先,在STM32CubeIDE中创建一个新工程,并选择好对应的硬件配置。 然后,将MPU6050连接到STM32F407的I2C接口上,确保硬件连接正确无误。 接着,打开I2C总线并配置为所需的速率和参数。例如: ```c // 打开I2C总线 HAL_I2C_Init(&hi2c1); // 配置I2C速率和参数 hi2c1.Instance->CR2 = I2C_SPEED_STANDARD | I2C_CLOCK_FREQ; // 使能I2C总线 HAL_I2C_Enable(&hi2c1); ``` 然后,通过I2C总线读取MPU6050的加速度、陀螺仪和温度数据。例如: ```c uint8_t buffer[14]; // 设置MPU6050的地址 uint8_t deviceAddress = MPU6050_ADDRESS << 1; // 设置读取加速度、陀螺仪和温度数据的寄存器地址 uint8_t registerAddress = MPU6050_ACCEL_XOUT_H; // 发送寄存器地址 HAL_I2C_Master_Transmit(&hi2c1, deviceAddress, &registerAddress, 1, HAL_MAX_DELAY); // 读取数据 HAL_I2C_Master_Receive(&hi2c1, deviceAddress, buffer, sizeof(buffer), HAL_MAX_DELAY); // 解析数据 int16_t accelX = (buffer[0] << 8) | buffer[1]; int16_t accelY = (buffer[2] << 8) | buffer[3]; int16_t accelZ = (buffer[4] << 8) | buffer[5]; int16_t gyroX = (buffer[8] << 8) | buffer[9]; int16_t gyroY = (buffer[10] << 8) | buffer[11]; int16_t gyroZ = (buffer[12] << 8) | buffer[13]; float temperature = (buffer[6] << 8) | buffer[7]; // 对数据进行处理或输出 ... ``` 最后,根据需求对读取到的数据进行处理或输出,例如将数据发送到串口或显示在LCD屏幕上,或者根据加速度和陀螺仪数据计算出相应的姿态信息。 需要注意的是,以上仅是一个简单的例程,实际应用中还需要考虑到数据的校验、滤波、数据的单位转换等问题,以及异常情况的处理。 希望以上的回答对您有帮助,祝您成功实现STM32F407读取MPU6050的数据!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值