基于STM32F103C8T6驱动MPU9250传感器读取九轴数据

一、MPU9250传感器模块概述

  MPU9250是一款高集成度的九轴惯性测量单元(IMU),它集成了三轴陀螺仪、三轴加速度计和三轴磁力计(通常集成的磁力计为AK8963)。由于其体积小、功耗低、性能稳定,MPU9250广泛应用于无人机、机器人、智能穿戴设备、虚拟现实以及运动追踪等领域。MPU9250采用MEMS(微机电系统)技术制造,能够同时采集角速度、线性加速度和地磁场数据,从而实现九自由度测量。模块内部还集成了数字运动处理器(DMP),用于执行复杂的传感器融合算法,从而降低了主控芯片的运算负担。

二、功能与特点

  MPU9250整合了3轴加速度计、3轴陀螺仪和3轴磁力计,能够提供九轴数据。这使得系统可以实时计算出姿态、航向和运动状态,为高精度定位和运动控制提供数据支持。

  1. 低功耗设计
      采用先进的低功耗设计,适合于便携式设备和电池供电的应用场景。其工作模式可根据应用需求进行灵活配置,如休眠、低功耗待机以及全速工作模式。
  2. 高动态范围和分辨率
      陀螺仪:支持±250、±500、±1000和±2000°/s等不同的量程选择,可适应从微小角速度到大幅度旋转的测量。
      加速度计:支持±2g、±4g、±8g和±16g的量程,适用于多种运动状态检测。
      磁力计:内置AK8963磁力计,具有高分辨率,可以进行精确的地磁测量,用于航向计算和磁场干扰补偿。
  3. 数字运动处理器(DMP)
      内置DMP可以实现基本的姿态计算和传感器融合,降低主控MCU的计算负担,并实现实时的运动检测与姿态估计。
  4. 多种通信接口
      MPU9250支持I2C和SPI两种通信方式,便于在不同系统中灵活应用。标准库或HAL库均可对其进行驱动开发。
  5. 内置温度传感器
      除了三轴数据,MPU9250还内置温度传感器,用于监控芯片内部温度。虽然该温度数据主要反映芯片工作时的温度状况,但在某些应用中也可作为温度补偿参考。

三、数据校准与转换

  传感器输出的原始数据通常为16位有符号数。为了获得物理意义上的数值,需要根据所选的量程进行转换。例如:

  1. 加速度计:如果选择±2g量程,则转换系数为16384 LSB/g,即原始数据除以16384后得到加速度值(单位g)。
  2. 陀螺仪:如果选择±250°/s量程,则转换系数为131 LSB/°/s,即原始数据除以131后得到角速度值(单位°/s)。
  3. 温度传感器:MPU9250的文档中未明确给出温度转换参数,温度转换公式一般参考MPU6050的公式:
    温度 ( ℃ ) = T E M P − O U T 340 + 36.53 温度(℃)=\frac{TEMP-OUT}{340}+36.53 温度()=340TEMPOUT+36.53

四、常用寄存器说明

1、WHO_AM_I:读取设备ID寄存器(只读不能写),MPU9250的ID默认为0x71。


  其中AK8963的设备ID固定为0x48。

2、PWR_MGMT_1:电源管理1, 此寄存器用于设置用户配置电源模式和时钟源。

  H_RESET:重置内部寄存器,并恢复默认设置。写一个1来设置重置位,该位将自动清除。即复位MPU9250。
  SLEEP:设置MPU9250工作模式为休眠模式。
  CYCLE:周期模式。当你在 PWR_MGMT_1 寄存器中设置了 CYCLE 位,同时确保 SLEEP 和 STANDBY 位没有被设置时,MPU9250 会进入一种低功耗工作模式。在这种模式下,芯片会不断地在“休眠”与“短暂唤醒采样”之间交替工作。每次唤醒时,它只会采集一个加速度计样本,而不是连续采集数据。采样的频率由 LP_ACCEL_ODR 寄存器来控制。注意:如果你通过 PWR_MGMT_2 寄存器禁用了所有加速度计的轴(也就是关闭了加速度计),那么即使启用了周期模式,芯片仍然会按照 LP_ACCEL_ODR 寄存器设定的时间间隔定时唤醒,但由于加速度计被禁用了,所以它不会采集任何数据。简单来说,就是启用周期模式时芯片周期性地从低功耗休眠状态中唤醒,采集一次数据,然后再回到休眠状态。如果禁用加速度计:芯片仍会周期性唤醒,但不会采集数据。
  GYRO_STANDBY:陀螺仪待机。设置后,陀螺仪驱动器和锁相环电路将启用,但感测路径将被禁用。这是一种低功耗模式,可快速启用陀螺仪。
  PD_PTAT:关闭内部 PTAT 电压发生器和 PTAT ADC
  CLKSEL[2:0]:时钟选择。可选择内部8M晶振、外部晶振或陀螺仪时钟作为时钟源。设备上电默认使用内部8M晶振作为时钟,但其精度不高。官方推荐使用陀螺仪时钟或外部晶振作为时钟源,以提高稳定性,一般设置CLKSEL=001,即选择陀螺仪X轴时钟作为时钟源即可。下表为时钟源选择。

3、PWR_MGMT_2:电源管理2,使能3轴加速度和3轴陀螺仪。

4、SMPLRT_DIV:采样率分频器,用于设置传感器数据采样的速率。将设置采样速率时钟通过这个寄存器进行分频,从而得到最终的数据采样率

分频计算公式如下

5、CONFIG:配置寄存器。

  FIFO_MODE:控制 FIFO(数据缓冲区)在装满后如何处理新的数据。当FIFO_MODE = 0时,一旦FIFO装满,则新数据覆盖旧数据。FIFO_MODE = 1时,一旦FIFO满,则停止存储数据。
  EXT_SYNC_SET[2:0]:配置外部同步,让芯片与外部触发信号同步采集数据。以下表为选择哪个传感器数据同步,其中000为禁用同步,001为温度数据同步,010-111分别为3轴陀螺仪、3轴加速度数据同步。

  DLPF_CFG[2:0]:设置数字低通滤波器,用于滤除传感器信号中的高频噪声。当 FCHOICE_B [1:0] = 00 时,DLPF 由 DLPF_CFG 配置。陀螺仪和温度传感器根据 DLPF_CFG 和 FCHOICE_B 的值进行过滤,如下表所示。

6、GYRO_CONFIG:配置陀螺仪自检和满量程


7、ACCEL_CONFIG:配置加速度自检和满量程


8、INT_PIN_CFG:中断配置寄存器,用于设置INT中断引脚的电平标准和驱动方式(推挽、开漏)等;

  BYPASS_EN:配置旁路模式。读取磁力计数据可以通过两种方式读取,一种是通过配置内部I2C主控模式读取,一种是直接通过旁路模式读取,一般是开启旁路模式来访问磁力计,比较简单方便。
9、CNTL1:配置磁力计工作模式。

以下为配置模式

五、接线说明

STM32F103C8T6 MPU9250 / USB转TTL
3V3 MPU9250_VCC
GND MPU9250和TTL的GND
GPIOB10 MPU9250_SCL
GPIOB11 MPU9250_SDA
GPIOA9 TTL_RX
GPIOA10 TTL_TX

六、读取MPU9250九轴数据

代码示例:

main.c

#include "stm32f10x.h"                  // Device header
#include "mpu9250.h"
#include "delay.h"
#include "usart.h"

int16_t ax, ay, az, gx, gy, gz, mx, my, mz;
float temperature;
char mpu9250ID[30], ak8963ID[30], buf[100];

int main(void)
{
    
    
	SystemInit();
	MPU9250_Init();

	USART1_Init();	// USART初始化函数
	
	sprintf(mpu9250ID, "mpu9250_id = 0x%02X\r\n", MPU9250_GetID());	
	USART_SendString(USART1, mpu9250ID);
	
	sprintf(ak8963ID, "ak8963_id = 0x%02X\r\n\r\n", AK8963_GetID());
	USART_SendString(USART1, ak8963ID);
	
	while(1)
	{
    
    
		//读取加速度和陀螺仪
		MPU9250_GetData(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);
		//读取温度
		temperature = MPU6050_GetTemperature();
		
		sprintf(buf, "ax:%d  ay:%d  az:%d    gx:%d  gy:%d  gz:%d\r\nmx:%d  my:%d  mz:%d\r\nTemp: %.2f℃\r\n\r\n",
                ax, ay, az, gx, gy, gz, mx, my, mz, temperature);
		
		USART_SendString(USART1, buf);

        Delay_ms(500);
	}
}

MPU9250.c

#include "MyI2C.h"
#include "MPU9250_Reg.h"
#include "delay.h"

void MPU9250_WriteReg(uint8_t DeviceAddr, uint8_t RegAddress, uint8_t Data)
{
    
    
	MyI2C_Start();
	MyI2C_SendByte(DeviceAddr);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(RegAddress);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(Data);
	MyI2C_ReceiveAck();
	MyI2C_Stop();
}

uint8_t MPU9250_ReadReg(uint8_t DeviceAddr, uint8_t RegAddress)
{
    
    
	uint8_t Data;
	
	MyI2C_Start();
	MyI2C_SendByte(DeviceAddr);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(RegAddress);
	MyI2C_ReceiveAck();
	
	MyI2C_Start();
	MyI2C_SendByte(DeviceAddr | 0x01); //指定地址读
	MyI2C_ReceiveAck();
	Data = MyI2C_ReceiveByte();
	MyI2C_SendAck(1);
	MyI2C_Stop();
	
	return Data;
}

uint8_t MPU9250_GetID(void)
{
    
    
	return MPU9250_ReadReg(MPU9250_ADDR_WRITE, MPU9250_WHO_AM_I);
}

uint8_t AK8963_GetID(void)
{
    
    
	return MPU9250_ReadReg(AK8963_ADDR_WRITE, AK8963_WHO_AM_I);
}

void MPU9250_GetData(int16_t *ax, int16_t *ay, int16_t *az, 
					    int16_t *gx, int16_t *gy, int16_t *gz,
						int16_t *mx, int16_t *my, int16_t *mz)
{
    
    
	uint8_t AccData[7], GyroData[7], MagData[7];  // 包括6字节数据及1字节状态(ST2)
    uint8_t i;
	
	//3轴加速度
	for(i = 0; i < 7; i++){
    
    
        AccData[i] = MPU9250_ReadReg(MPU9250_ADDR_WRITE, MPU9250_ACCEL_XOUT_H + i);
    }
	*ax = (int16_t)(((int16_t)AccData[0] << 8) | AccData[1]);
    *ay = (int16_t)(((int16_t)AccData[2] << 8) | AccData[3]);
    *az = (int16_t)(((int16_t)AccData[4] << 8) | AccData[5]);
	
	//3轴陀螺仪
	for(i = 0; i < 7; i++){
    
    
        GyroData[i] = MPU9250_ReadReg(MPU9250_ADDR_WRITE, MPU9250_GYRO_XOUT_H + i);
    }
	*gx = (int16_t)(((int16_t)GyroData[0] << 8) | GyroData[1]);
    *gy = (int16_t)(((int16_t)GyroData[2] << 8) | GyroData[3]);
    *gz = (int16_t)(((int16_t)GyroData[4] << 8) | GyroData[5]);
	
	//3轴磁力计
	for(i = 0; i < 7; i++){
    
    
        MagData[i] = MPU9250_ReadReg(AK8963_ADDR_WRITE, AK8963_HXL + i);
    }
    /* 注意:AK8963数据寄存器采用低字节在前的格式 */
    *mx = (int16_t)(((int16_t)MagData[1] << 8) | MagData[0]);
    *my = (int16_t)(((int16_t)MagData[3] << 8) | MagData[2]);
    *mz = (int16_t)(((int16_t)MagData[5] << 8) | MagData[4]);
	
	/* 可以在此检查ST2寄存器(Data[6])中的溢出标志 */
}

float MPU6050_GetTemperature(void)
{
    
    
	int16_t TempData;
	uint8_t tempH, tempL;
	
	tempH = MPU9250_ReadReg(MPU9250_ADDR_WRITE, MPU9250_TEMP_OUT_H);
	tempL = MPU9250_ReadReg(MPU9250_ADDR_WRITE, MPU9250_TEMP_OUT_L);
	
	TempData = (int16_t)(tempH << 8) | tempL;
	
	return((float)TempData) / 340.0f + 36.53f;
}

void MPU9250_Init()
{
    
    
	MyI2C_Init();
	MPU9250_WriteReg(MPU9250_ADDR_WRITE, MPU9250_PWR_MGMT_1, 0x01);
	MPU9250_WriteReg(MPU9250_ADDR_WRITE, MPU9250_PWR_MGMT_2, 0x00);
	MPU9250_WriteReg(MPU9250_ADDR_WRITE, MPU9250_SMPLRT_DIV, 0x09);	//采样分频器
	MPU9250_WriteReg(MPU9250_ADDR_WRITE, MPU9250_CONFIG, 0x06);	//配置寄存器,配置同步时钟和低通滤波
	MPU9250_WriteReg(MPU9250_ADDR_WRITE, MPU9250_GYRO_CONFIG, 0x18); //配置陀螺仪自测和满量程
	MPU9250_WriteReg(MPU9250_ADDR_WRITE, MPU9250_ACCEL_CONFIG, 0x18);
	MPU9250_WriteReg(MPU9250_ADDR_WRITE, MPU9250_INT_PIN_CFG, 0x02);  //开启旁路模式
	MPU9250_WriteReg(AK8963_ADDR_WRITE, AK8963_CNTL1, 0x16);  //设置连续测量模式
	Delay_ms(10);
}

效果展示:

七、读取不到磁力计数据的可能原因及解决方法

  获取磁力计是根据AK8963来测量,在读取磁力计数据前,先读取下AK8963设备的ID值是否返回正常。如果不正常,可能的原因有以下几点:

  1. 旁路模式未开启
      MPU9250内部连接了AK8963,只有在设置旁路模式后,外部I2C才能直接访问AK8963。如果旁路模式没有正确配置,I2C总线上可能读不到AK8963的数据。
      如果旁路模式已开启,常见原因是MPU9250内部I2C主控功能没有被关闭,导致旁路模式虽然设置了但仍然屏蔽了对AK8963的直接访问。通常在启用旁路模式之前,需要先清除MPU9250的I2C_MST_EN,以确保外部I2C能直接访问AK8963。否则,即便旁路模式配置正确,也可能读不到AK8963的寄存器,返回0x00。
      建议检查初始化流程,确保在设置INT_PIN_CFG寄存器为0x02以启用旁路模式之前,先在USER_CTRL寄存器中清除I2C主控使能位,然后再进行后续的AK8963初始化流程。
  2. I2C通信问题
      检查下I2C初始化和通讯时序是否正确。
      值得注意的是I2C地址的位宽上,AK8963的7位从地址是0x0C,如果你的I2C位操作中,传递的是8位地址。对于8位地址,写操作应使用(0x0C<<1)=0x18,而读操作应使用0x18|0x01=0x19。如果直接使用0x0C,就相当于地址错误,I2C总线读取时由于找不到设备,返回的自然就是上拉电平(0xFF)。
  3. 供电或复位问题
      如果AK8963没有正确上电或者处于复位状态,也可能返回0x00。确保传感器的电源电压和启动顺序符合要求。

总结

  MPU9250作为一款集成了九轴传感器的高性能IMU,凭借其小巧的体积、低功耗和多种通信接口,成为了广泛应用于无人机、机器人、可穿戴设备和VR等领域的首选传感器模块。其内部集成的DMP不仅简化了外部数据处理,也为实现高精度姿态估计提供了强有力的支持。然而,在实际应用中,校准、磁干扰和信号完整性等问题依然需要开发者认真对待,通过软硬件设计优化实现更稳定可靠的系统。

  需要源代码可收藏加关注在评论留下邮箱获取!!!

猜你喜欢

转载自blog.csdn.net/qq_42250136/article/details/146076553
今日推荐