MPU9250在STM32单片机SPI通信接口下的初始化及九轴数据读取总结
大部分单片机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/1HpvmgxPJeVJ02VYLWLVyAA
提取码:aj6p