STM32单片机SPI读取MPU9250加速度计、陀螺仪、磁力计九轴数据(附程序)

MPU9250在STM32单片机SPI通信接口下的初始化及九轴数据读取总结

很多人用SPI接口读取MPU9250磁力计数据读不出来,关键问题在于MPU9250内部与磁力计是IIC接口(很坑,技术文档也没有特别说明,只在内部结构图中有展示),所以读取时必须要加延时,若只读取加速度和陀螺仪数据才可以达到1-20MHz的速率。
大部分单片机IIC接口不是很好用,相信很多朋友在调试的时候踩过不少坑,而且由于IIC自身的限制,采样速率有时不足为用,所以被迫换为SPI接口。但SPI接口调试磁力计依然受内部IIC限制,不过若只使用加速度和陀螺仪数据,速率便会比IIC快几倍。
本次博客记录笔者在实际调试过程中遇到的一些问题,以提醒后人在调试中应该注意的事项,以免踩坑。
本次调试采用STM32f103系列单片机进行数据读取。

一、初始化流程:
1.解除休眠;
2.初始化内部IIC(mpu9250内部采用IIC与磁力计通信);
3.SPI初始化加速度计和陀螺仪(配置参数具体参考技术手册);
4.通过SPI与内部IIC通信,初始化磁力计;
5.根据需求决定FIFO、DMP是否启用。
注意:
1.解除休眠后要加至少100ms延时,以保证mpu9250初始化完毕(内部IIC通信较慢),若只读取加速度计和陀螺仪的六轴数据而不使用磁力计的话,可以省去磁力计的初始化部分。
2.加速度计和陀螺仪的量程(精度)由实际使用情况配置,人体姿态捕获建议加速度计±8g,陀螺仪设置最大±2000°/s;

keil5环境初始化程序:

void Init_MPU9250(void)
{ 
 MPU9250_Write_Reg(PWR_MGMT_1, 0x80); //解除休眠状态
 delay_ms(100);//延时保证复位
 
/**********************Init SLV0 i2c**********************************/ 
//Use SPI-bus read slave0使能内部iic(mpu9250内部是通过iic与磁力计通信的)
 MPU9250_Write_Reg(INT_PIN_CFG ,0x30);// INT Pin / Bypass Enable Configuration  
 MPU9250_Write_Reg(I2C_MST_CTRL,0x4d);//I2C MAster mode and Speed 400 kHz
 MPU9250_Write_Reg(USER_CTRL ,0x20); // I2C_MST _EN 
 MPU9250_Write_Reg(I2C_MST_DELAY_CTRL ,0x01);//延时使能I2C_SLV0 _DLY_ enable  
 MPU9250_Write_Reg(I2C_SLV0_CTRL ,0x81); //enable IIC and EXT_SENS_DATA==1 Byte
 
///*******************Init GYRO and ACCEL******************************/ 
 MPU9250_Write_Reg(CONFIG, 0x07);      //低通滤波频率,典型值:0x07(3600Hz)此寄存器内决定Internal_Sample_Rate==8K
 MPU9250_Write_Reg(SMPLRT_DIV, 0x07);  //陀螺仪采样率,典型值:0x07(1kHz) (SAMPLE_RATE= Internal_Sample_Rate / (1 + SMPLRT_DIV) )
 MPU9250_Write_Reg(GYRO_CONFIG, 0x08); //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
 MPU9250_Write_Reg(ACCEL_CONFIG_2, 0x08);//加速计高通滤波频率 典型值 :0x08  (1.13kHz) 
 MPU9250_Write_Reg(ACCEL_CONFIG, 0x08);//加速计自检、测量范围及高通滤波频率,典型值:0x00/+-2g. 0x08/+-4g. 0x10/+-8g. 0x18(不自检,16G)
 
  /**********************Init MAG **********************************/
  i2c_Mag_write(AK8963_CNTL2_REG,AK8963_CNTL2_SRST); // Reset AK8963
  i2c_Mag_write(AK8963_CNTL1_REG,0x12); // use i2c to set AK8963 working on Continuous measurement mode1 & 16-bit output 
  
//  MPU9250_Write_Reg(MPU9250_RA_FIFO_EN, 0xFF);//FIFO?? 
//  delay_ms(10);
  
//  /***********************DMP??**********************************/
//  MPU9250_Write_Reg(MPU9250_RA_INT_PIN_CFG,0x00); //??????
//  MPU9250_Write_Reg(MPU9250_RA_INT_ENABLE,0x01);  //FIFO????
 
  delay_ms(10);
}

二、SPI接口配置及通信底层程序
本次调试使用的是硬件SPI接口,具体使用接口请按照硬件实际连接接口配置。

1.SPI接口初始化:

void spi_Init()
{
 SPI_InitTypeDef SPI_InitStructure;
 GPIO_InitTypeDef GPIO_InitStructure;
 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1|RCC_APB2Periph_GPIOA, ENABLE); 
 //GPIO口配置设置//
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
 GPIO_SetBits(GPIOA,GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
    /*Configure PA.4(NSS)--------------------------------------------*/
  GPIO_InitStructure.GPIO_Pin =GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
 GPIO_SetBits(GPIOA,GPIO_Pin_4);
  /* SPI1 configuration */ 
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;  //主机模式
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8位数据
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//SPI_CPOL_High=模式3,时钟空闲为高 //SPI_CPOL_Low=模式0,时钟空闲为低
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//SPI_CPHA_2Edge;//SPI_CPHA_1Edge, SPI_CPHA_2Edge;
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//SPI_NSS_Soft;//SPI_NSS_Hard
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;//SPI_BaudRatePrescaler_2=32M;//SPI_BaudRatePrescaler_4=18MHz
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//数据从高位开始发送
  SPI_InitStructure.SPI_CRCPolynomial = 7;
 
 SPI_Init(SPI1, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
 SPI_Cmd(SPI1, ENABLE); //使能SPI外设
}

2.SPI发送一个字节:

static u8 SPI1_ReadWriteByte(u8 TxData)
{  
 u8 retry=0;      
 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //等待SPI发送标志位空
  {
  retry++;
  if(retry>200)return 0;
  }
 SPI_I2S_SendData(SPI1, TxData); //发送数据
 retry=0;
 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) //等待SPI接收标志位空
  {
  retry++;
  if(retry>200)return 0;
  }
  return SPI_I2S_ReceiveData(SPI1); //接收数据 
}

3.MPU9250寄存器读写:
SPI写寄存器:

u8 MPU9250_Write_Reg(u8 reg,u8 value)
{
 u8 status;
 MPU_9250_ENABLE;   // MPU9250_CS=0;  //片选MPU9250
 status=SPI1_ReadWriteByte(reg); //发送reg地址
 SPI1_ReadWriteByte(value);//发送数据
 MPU_9250_DISENABLE;// MPU9250_CS=1;  //失能MPU9250
 return(status);//
}

SPI读寄存器:

u8 MPU9250_Read_Reg(u8 reg)
{
   u8 reg_val;
  MPU_9250_ENABLE;// MPU9250_CS=0;  //片选MPU9250
   SPI1_ReadWriteByte(reg|0x80); //reg地址+读命令
   reg_val=SPI1_ReadWriteByte(0xff);//任意数据
  MPU_9250_DISENABLE;// MPU9250_CS=1;  //失能MPU9250
 return(reg_val);
}

4.MPU9250内部IIC的写入和读取(磁力计用):
注意:
实测地磁计数据读取要加100us以上延时保证数据读取完整,否则磁力计数据会有问题;

内部IIC写入:

static void i2c_Mag_write(u8 reg,u8 value)
{
 MPU9250_Write_Reg(I2C_SLV0_ADDR ,MPU9250_AK8963_ADDR);//设置磁力计地址,mode: write
 delay_us(100);
 MPU9250_Write_Reg(I2C_SLV0_REG ,reg);//set reg addr
 delay_us(100);
 MPU9250_Write_Reg(I2C_SLV0_DO ,value);//send value 
 delay_us(100);//此处因为MPU内部I2C读取速度较慢,延时等待内部写完毕
}

内部IIC读取:

static u8 i2c_Mag_read(u8 reg)
{
 MPU9250_Write_Reg(I2C_SLV0_ADDR ,MPU9250_AK8963_ADDR|0x80); //设置磁力计地址,mode:read
 delay_us(100);
 MPU9250_Write_Reg(I2C_SLV0_REG ,reg);// set reg addr
 delay_us(100);
 MPU9250_Write_Reg(I2C_SLV0_DO ,0xff);//read
 delay_us(100);//此处因为MPU内部I2C读取速度较慢,必须延时等待内部读取完毕
 return MPU9250_Read_Reg(EXT_SENS_DATA_00);
}

三、九轴数据的读取
1.加速度读取:

void READ_MPU9250_ACCEL(void)//
{ 
   BUF[0]=MPU9250_Read_Reg(ACCEL_XOUT_L); 
   BUF[1]=MPU9250_Read_Reg(ACCEL_XOUT_H);
   mpu_value.Accel[0]= (BUF[1]<<8)|BUF[0];
   mpu_value.Accel[0]/=164;          //读取计算X轴数据
   BUF[2]=MPU9250_Read_Reg(ACCEL_YOUT_L);
   BUF[3]=MPU9250_Read_Reg(ACCEL_YOUT_H);
   mpu_value.Accel[1]= (BUF[3]<<8)|BUF[2];
   mpu_value.Accel[1]/=164;          //读取计算Y轴数据
   BUF[4]=MPU9250_Read_Reg(ACCEL_ZOUT_L); 
   BUF[5]=MPU9250_Read_Reg(ACCEL_ZOUT_H);
   mpu_value.Accel[2]=  (BUF[5]<<8)|BUF[4];
   mpu_value.Accel[2]/=164;           //读取计算Z轴数据  
}

2.陀螺仪读取:

void READ_MPU9250_GYRO(void)
{ 
   BUF[0]=MPU9250_Read_Reg(GYRO_XOUT_L); 
   BUF[1]=MPU9250_Read_Reg(GYRO_XOUT_H);
   mpu_value.Gyro[0]= (BUF[1]<<8)|BUF[0];
   mpu_value.Gyro[0]/=164;            //读取计算X轴数据
   BUF[2]=MPU9250_Read_Reg(GYRO_YOUT_L);
   BUF[3]=MPU9250_Read_Reg(GYRO_YOUT_H);
   mpu_value.Gyro[1]= (BUF[3]<<8)|BUF[2];
   mpu_value.Gyro[1]/=164;            //读取计算Y轴数据
   BUF[4]=MPU9250_Read_Reg(GYRO_ZOUT_L);
   BUF[5]=MPU9250_Read_Reg(GYRO_ZOUT_H);
   mpu_value.Gyro[2]= (BUF[5]<<8)|BUF[4];
   mpu_value.Gyro[2]/=164;             //读取计算Z轴数据
 }

3.磁力计读取:
**注意:
i2c_Mag_read(AK8963_ST2_REG) 此步读取不可省略;
数据读取结束寄存器,reading this register means data reading end;
AK8963_ST2_REG 同时具有数据非正常溢出检测功能;
详情参考 MPU9250 PDF。

void READ_MPU9250_MAG(void)
{  
 u8 x_axis,y_axis,z_axis; 
 
 x_axis=i2c_Mag_read(AK8963_ASAX);// X轴灵敏度调整值
 y_axis=i2c_Mag_read(AK8963_ASAY);
 z_axis=i2c_Mag_read(AK8963_ASAZ);
 
 if((i2c_Mag_read(AK8963_ST1_REG)&AK8963_ST1_DOR)==0)//data ready
 {
   //读取计算X轴数据
   BUF[0]=i2c_Mag_read(MAG_XOUT_L); //Low data 
   if((i2c_Mag_read(AK8963_ST2_REG)&AK8963_ST2_HOFL)==1)// data reading end register & check Magnetic sensor overflow occurred 
   {
    BUF[0]=i2c_Mag_read(MAG_XOUT_L);//reload data
   } 
   BUF[1]=i2c_Mag_read(MAG_XOUT_H); //High data 
   if((i2c_Mag_read(AK8963_ST2_REG)&AK8963_ST2_HOFL)==1)// data reading end register
   {
    BUF[1]=i2c_Mag_read(MAG_XOUT_H);
   }
   mpu_value.Mag[0]=((BUF[1]<<8)|BUF[0])*(((x_axis-128)>>8)+1);  //灵敏度纠正 公式见/RM-MPU-9250A-00 PDF/ 5.13 
   
  //读取计算Y轴数据
   BUF[2]=i2c_Mag_read(MAG_YOUT_L); //Low data 
   if((i2c_Mag_read(AK8963_ST2_REG)&AK8963_ST2_HOFL)==1)// data reading end register
   {
    BUF[2]=i2c_Mag_read(MAG_YOUT_L);
   }   
   BUF[3]=i2c_Mag_read(MAG_YOUT_H); //High data 
   if((i2c_Mag_read(AK8963_ST2_REG)&AK8963_ST2_HOFL)==1)// data reading end register
   {
    BUF[3]=i2c_Mag_read(MAG_YOUT_H);
   }
    mpu_value.Mag[1]=((BUF[3]<<8)|BUF[2])*(((y_axis-128)>>8)+1); 
   
  //读取计算Z轴数据
   BUF[4]=i2c_Mag_read(MAG_ZOUT_L); //Low data 
   if((i2c_Mag_read(AK8963_ST2_REG)&AK8963_ST2_HOFL)==1)// data reading end register
   {
    BUF[4]=i2c_Mag_read(MAG_ZOUT_L);
   }  
   BUF[5]=i2c_Mag_read(MAG_ZOUT_H); //High data 
   if((i2c_Mag_read(AK8963_ST2_REG)&AK8963_ST2_HOFL)==1)// data reading end register
   {
    BUF[5]=i2c_Mag_read(MAG_ZOUT_H);
   }
    mpu_value.Mag[2]=((BUF[5]<<8)|BUF[4])*(((z_axis-128)>>8)+1); 
 }            
}

附百度云盘原代码连接:
链接:https://pan.baidu.com/s/1x_p7Y_4w7Al_eA5ZxNfECw
提取码:jnce

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页