MPU9250传感器
时间:2022-12-01 05:30:00
MPU9250 内部包括 3 轴陀螺仪、3 轴加速度计和 3 轴磁力计是这三个功能的输出 16 位数量; 通过常用的数据总线( IIC) 接口与单片机进行数据交互,传输速率 400 kHz /s。陀螺仪角速度测量范围±2000(° /s),具有良好的动态响应特性。加速度计的最大测量范围是±16g( g 静态测量精度高,是重力加速度)。磁感应强度测量范围为高灵度霍尔型传感器±4800μT,可用于辅助测量偏航角。
MPU9250 自带数字运动处理器DMP硬件加速发动机可以整合九轴传感器数据,输出到应用端 9 轴融合计算数据。 有了 DMP,我们可以利用运动处理库非常方便地实现姿态解决,减少运动处理操作系统的负荷,大大降低开发难度。
三轴陀螺仪
MPU9250陀螺仪有三个独立检测X, Y, Z轴的MEMS组成。检测每个轴的旋转(一旦轴发生变化,相应的电容传感器发生相应的变化,放大、调解、过滤信号,最终产生与角速率成正比的电压,然后将每个轴的电压转换为16位数据。ADC采样速率也可编程,每秒3.9-8000个。
三轴加速度
MPU9250的三轴加速度也是单独测量的。根据每个轴上的电容量测量轴的偏差。各种因素引起的测量偏差在结构上减少。加速度计的校准是根据工厂标准设置的,电源电压可能与您使用的不同。每个传感器都有一个特殊的ADC提供数字输出。
三轴磁力计
三轴磁力计采用高精度霍尔效应传感器采集地磁场X, Y, Z轴上的电磁强度。
IIC通信
MPU9250电路图连接如下

我们使用IIC让MPU9250与单片机通信,输出获得的传感器值。
IIC数据总线由两条通信线组成,必须包括一个主控制器件和多个从控制器件,通过地址与主控制器件通信。
在实际使用中,单片机通常用作主机,其他设备用作从机。单片机首先向设备发送信息,表示需要读取数据,然后改变传输方向,将数据发送到单片机。
在通信时,IIC通信线只有只有两根,数据线SDA的高低电平传输2进制的数据,时钟线SCL时钟节拍通过方波信号提供。在时钟的高电平周期内,SDA在线数据必须保持稳定,数据线只能在时钟SCL平时换低电。
IIC通信数据包括初始信号响应信号和结束信号。
当SCL高电平时,SDA从高到低的跳变被定义为起始条件。最终信号的条件是SCL高电平时,SDA从低到高的跳变被定义为停止条件。
从机响应主机所需的时钟仍由主机提供。响应出现在每个主机完成8个数据位传输后的时钟周期中。低电平0表示响应,1表示非响应。
关于通信协议的具体内容,您可以在网上找到详细的介绍。作为一名嵌入式软件工程师,这些常见的协议必须仔细研究,只有理解协议才能澄清协议在程序中实现的逻辑。
程序
由于使用IIC控制通信协议MPU我们需要实现9250IIC协议。
代码参考正点原子的源代码,包装函数更有效。
void IIC_Init(void){GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOA_CLK_ENABLE(); //使能GPIOB时钟//PH4、5初始化设置GPIO_Initure.Pin=GPIO_PIN_4|GPIO_PIN_5;GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出GPIO_Initure.Pull=GPIO_PULLUP; //上拉GPIO_Initure.Speed=GPIO_SPEED_FAST; //快速HAL_GPIO_Init(GPIOA,&GPIO_Initure);IIC_SDA=1;IIC_SCL=1;}//产生IIC起始信号void IIC_Start(void){SDA_OUT(); //sda线输出IIC_SDA=1;IIC_SCL=1;delay_us(4);IIC_SDA=0;//START:when CLK is high,DATA change form high to lowdelay_us(4);IIC_SCL=0;//钳住I2C总线准备发送或接收数据}//产生IIC停止信号void IIC_Stop(void){SDA_OUT();//sda线输出IIC_SCL=0;IIC_SDA=0;//STOP:when CLK is high DATA change form low to highdelay_us(4);IIC_SCL=1;delay_us(4);IIC_SDA=1;//发送I2C总线结束信号}//等待响应信号到来//返回值:1.接收应答失败// 成功接收应答u8 IIC_Wait_Ack(void){u8 ucErrTime=0;SDA_IN(); //SDA设置为输入IIC_SDA=1;delay_us(1);IIC_SCL=1;delay_us(1);while(READ_SDA){ucErrTime ;if(ucErrTime>250){IIC_Stop();return 1;}}IIC_SCL=0;//时钟输出0return 0;}//产生ACK应答void IIC_Ack(void){IIC_SCL=0;SDA_OUT();IIC_SDA=0;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;}//不产生ACK应答void IIC_NAck(void){IIC_SCL=0;SDA_OUT();IIC_SDA=1;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;}//IIC发送一个字节//返回从机有无应答//1,有应答//0,无应答void IIC_Send_Byte(u8 txd){u8 t;SDA_OUT();IIC_SCL=0;//拉低时钟开始数据传输for(t=0;t<8;t++){IIC_SDA=(txd&0x80)>>7;txd<<=1;delay_us(2); //对TEA5767这三个延时都是必须的IIC_SCL=1;delay_us(2);IIC_SCL=0;delay_us(2);}}//读1个字节,ack=1时,发送ACK,ack=0,发送nACKu8 IIC_Read_Byte(unsigned char ack){unsigned char i,receive=0;SDA_IN();//SDA设置为输入for(i=0;i<8;i++ ){IIC_SCL=0;delay_us(2);IIC_SCL=1;receive<<=1;if(READ_SDA)receive++;delay_us(1);}if (!ack)IIC_NAck();//发送nACKelseIIC_Ack(); //发送ACKreturn receive;}u8 MPU9250_Init(void){u8 res=0;IIC_Init(); //初始化IIC总线MPU_Write_Byte(MPU9250_ADDR,MPU_PWR_MGMT1_REG,0X80);//复位MPU9250delay_ms(100); //延时100msMPU_Write_Byte(MPU9250_ADDR,MPU_PWR_MGMT1_REG,0X00);//唤醒MPU9250MPU_Set_Gyro_Fsr(3); //陀螺仪传感器,±2000dpsMPU_Set_Accel_Fsr(0); //加速度传感器,±2gMPU_Set_Rate(50); //设置采样率50HzMPU_Write_Byte(MPU9250_ADDR,MPU_INT_EN_REG,0X00); //关闭所有中断MPU_Write_Byte(MPU9250_ADDR,MPU_USER_CTRL_REG,0X00);//I2C主模式关闭MPU_Write_Byte(MPU9250_ADDR,MPU_FIFO_EN_REG,0X00); //关闭FIFOMPU_Write_Byte(MPU9250_ADDR,MPU_INTBP_CFG_REG,0X82);//INT引脚低电平有效,开启bypass模式,可以直接读取磁力计res=MPU_Read_Byte(MPU9250_ADDR,MPU_DEVICE_ID_REG); //读取MPU6500的IDif(res==MPU6500_ID) //器件ID正确{MPU_Write_Byte(MPU9250_ADDR,MPU_PWR_MGMT1_REG,0X01); //设置CLKSEL,PLL X轴为参考MPU_Write_Byte(MPU9250_ADDR,MPU_PWR_MGMT2_REG,0X00); //加速度与陀螺仪都工作MPU_Set_Rate(50); //设置采样率为50Hz}else return 1;res=MPU_Read_Byte(AK8963_ADDR,MAG_WIA); //读取AK8963 IDif(res==AK8963_ID){MPU_Write_Byte(AK8963_ADDR,MAG_CNTL1,0X11); //设置AK8963为单次测量模式}else return 1;return 0;}//设置MPU9250陀螺仪传感器满量程范围//fsr:0,±250dps;1,±500dps;2,±1000dps;3,±2000dps//返回值:0,设置成功// 其他,设置失败u8 MPU_Set_Gyro_Fsr(u8 fsr){return MPU_Write_Byte(MPU9250_ADDR,MPU_GYRO_CFG_REG,fsr<<3);//设置陀螺仪满量程范围}//设置MPU9250加速度传感器满量程范围//fsr:0,±2g;1,±4g;2,±8g;3,±16g//返回值:0,设置成功// 其他,设置失败u8 MPU_Set_Accel_Fsr(u8 fsr){return MPU_Write_Byte(MPU9250_ADDR,MPU_ACCEL_CFG_REG,fsr<<3);//设置加速度传感器满量程范围}//设置MPU9250的数字低通滤波器//lpf:数字低通滤波频率(Hz)//返回值:0,设置成功// 其他,设置失败u8 MPU_Set_LPF(u16 lpf){u8 data=0;if(lpf>=188)data=1;else if(lpf>=98)data=2;else if(lpf>=42)data=3;else if(lpf>=20)data=4;else if(lpf>=10)data=5;else data=6;return MPU_Write_Byte(MPU9250_ADDR,MPU_CFG_REG,data);//设置数字低通滤波器}//设置MPU9250的采样率(假定Fs=1KHz)//rate:4~1000(Hz)//返回值:0,设置成功// 其他,设置失败u8 MPU_Set_Rate(u16 rate){u8 data;if(rate>1000)rate=1000;if(rate<4)rate=4;data=1000/rate-1;data=MPU_Write_Byte(MPU9250_ADDR,MPU_SAMPLE_RATE_REG,data); //设置数字低通滤波器return MPU_Set_LPF(rate/2); //自动设置LPF为采样率的一半}//得到陀螺仪值(原始值)//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)//返回值:0,成功// 其他,错误代码u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz){u8 buf[6],res;res=MPU_Read_Len(MPU9250_ADDR,MPU_GYRO_XOUTH_REG,6,buf);if(res==0){*gx=((u16)buf[0]<<8)|buf[1];*gy=((u16)buf[2]<<8)|buf[3];*gz=((u16)buf[4]<<8)|buf[5];}return res;;}//得到加速度值(原始值)//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)//返回值:0,成功// 其他,错误代码u8 MPU_Get_Accelerometer(short *ax,short *ay,short *az){u8 buf[6],res;res=MPU_Read_Len(MPU9250_ADDR,MPU_ACCEL_XOUTH_REG,6,buf);if(res==0){*ax=((u16)buf[0]<<8)|buf[1];*ay=((u16)buf[2]<<8)|buf[3];*az=((u16)buf[4]<<8)|buf[5];}return res;;}//得到磁力计值(原始值)//mx,my,mz:磁力计x,y,z轴的原始读数(带符号)//返回值:0,成功// 其他,错误代码u8 MPU_Get_Magnetometer(short *mx,short *my,short *mz){u8 buf[6],res;res=MPU_Read_Len(AK8963_ADDR,MAG_XOUT_L,6,buf);if(res==0){*mx=((u16)buf[1]<<8)|buf[0];*my=((u16)buf[3]<<8)|buf[2];*mz=((u16)buf[5]<<8)|buf[4];}MPU_Write_Byte(AK8963_ADDR,MAG_CNTL1,0X11); //AK8963每次读完以后都需要重新设置为单次测量模式return res;;}//IIC连续写//addr:器件地址//reg:寄存器地址//len:写入长度//buf:数据区//返回值:0,正常// 其他,错误代码u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf){u8 i;IIC_Start();IIC_Send_Byte((addr<<1)|0); //发送器件地址+写命令if(IIC_Wait_Ack()) //等待应答{IIC_Stop();return 1;}IIC_Send_Byte(reg); //写寄存器地址IIC_Wait_Ack(); //等待应答for(i=0;i{IIC_Send_Byte(buf[i]); //发送数据if(IIC_Wait_Ack()) //等待ACK{IIC_Stop();return 1;}}IIC_Stop();return 0;}//IIC连续读//addr:器件地址//reg:要读取的寄存器地址//len:要读取的长度//buf:读取到的数据存储区//返回值:0,正常// 其他,错误代码u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf){IIC_Start();IIC_Send_Byte((addr<<1)|0); //发送器件地址+写命令if(IIC_Wait_Ack()) //等待应答{IIC_Stop();return 1;}IIC_Send_Byte(reg); //写寄存器地址IIC_Wait_Ack(); //等待应答IIC_Start();IIC_Send_Byte((addr<<1)|1); //发送器件地址+读命令IIC_Wait_Ack(); //等待应答while(len){if(len==1)*buf=IIC_Read_Byte(0);//读数据,发送nACKelse *buf=IIC_Read_Byte(1); //读数据,发送ACKlen--;buf++;}IIC_Stop(); //产生一个停止条件return 0;}//IIC写一个字节//devaddr:器件IIC地址//reg:寄存器地址//data:数据//返回值:0,正常// 其他,错误代码u8 MPU_Write_Byte(u8 addr,u8 reg,u8 data){IIC_Start();IIC_Send_Byte((addr<<1)|0); //发送器件地址+写命令if(IIC_Wait_Ack()) //等待应答{IIC_Stop();return 1;}IIC_Send_Byte(reg); //写寄存器地址IIC_Wait_Ack(); //等待应答IIC_Send_Byte(data); //发送数据if(IIC_Wait_Ack()) //等待ACK{IIC_Stop();return 1;}IIC_Stop();return 0;}//IIC读一个字节//reg:寄存器地址//返回值:读到的数据u8 MPU_Read_Byte(u8 addr,u8 reg){u8 res;IIC_Start();IIC_Send_Byte((addr<<1)|0); //发送器件地址+写命令IIC_Wait_Ack(); //等待应答IIC_Send_Byte(reg); //写寄存器地址IIC_Wait_Ack(); //等待应答IIC_Start();IIC_Send_Byte((addr<<1)|1); //发送器件地址+读命令IIC_Wait_Ack(); //等待应答res=IIC_Read_Byte(0); //读数据,发送nACKIIC_Stop(); //产生一个停止条件return res;}
下面的代码是控制MPU9250的关键代码,是针对芯片本身的。也是主要代码。
首先初始化DMP
u8 mpu_dmp_init(void){u8 res=0;struct int_param_s int_param;unsigned char accel_fsr;unsigned short gyro_rate, gyro_fsr;unsigned short compass_fsr;IIC_Init(); //初始化IIC总线if(mpu_init(&int_param)==0) //初始化MPU9250{res=inv_init_mpl(); //初始化MPLif(res)return 1;inv_enable_quaternion();inv_enable_9x_sensor_fusion();inv_enable_fast_nomot();inv_enable_gyro_tc();inv_enable_vector_compass_cal();inv_enable_magnetic_disturbance();inv_enable_eMPL_outputs();res=inv_start_mpl(); //开启MPLif(res)return 1;res=mpu_set_sensors(INV_XYZ_GYRO|INV_XYZ_ACCEL|INV_XYZ_COMPASS);//设置所需要的传感器if(res)return 2;res=mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL); //设置FIFOif(res)return 3;res=mpu_set_sample_rate(DEFAULT_MPU_HZ); //设置采样率if(res)return 4;res=mpu_set_compass_sample_rate(1000/COMPASS_READ_MS); //设置磁力计采样率if(res)return 5;mpu_get_sample_rate(&gyro_rate);mpu_get_gyro_fsr(&gyro_fsr);mpu_get_accel_fsr(&accel_fsr);mpu_get_compass_fsr(&compass_fsr);inv_set_gyro_sample_rate(1000000L/gyro_rate);inv_set_accel_sample_rate(1000000L/gyro_rate);inv_set_compass_sample_rate(COMPASS_READ_MS*1000L);inv_set_gyro_orientation_and_scale(inv_orientation_matrix_to_scalar(gyro_orientation),(long)gyro_fsr<<15);inv_set_accel_orientation_and_scale(inv_orientation_matrix_to_scalar(gyro_orientation),(long)accel_fsr<<15);inv_set_compass_orientation_and_scale(inv_orientation_matrix_to_scalar(comp_orientation),(long)compass_fsr<<15);res=dmp_load_motion_driver_firmware(); //加载dmp固件if(res)return 6;res=dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation));//设置陀螺仪方向if(res)return 7;res=dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT|DMP_FEATURE_TAP| //设置dmp功能DMP_FEATURE_ANDROID_ORIENT|DMP_FEATURE_SEND_RAW_ACCEL|DMP_FEATURE_SEND_CAL_GYRO|DMP_FEATURE_GYRO_CAL);if(res)return 8;res=dmp_set_fifo_rate(DEFAULT_MPU_HZ); //设置DMP输出速率(最大不超过200Hz)if(res)return 9;res=run_self_test(); //自检if(res)return 10;res=mpu_set_dmp_state(1); //使能DMPif(res)return 11;}return 0;}
获取mp1的数据
u8 mpu_mpl_get_data(float *pitch,float *roll,float *yaw){unsigned long sensor_timestamp,timestamp;short gyro[3], accel_short[3],compass_short[3],sensors;unsigned char more;long compass[3],accel[3],quat[4],temperature;long data[9];int8_t accuracy;if(dmp_read_fifo(gyro, accel_short, quat, &sensor_timestamp, &sensors,&more))return 1;if(sensors&INV_XYZ_GYRO){inv_build_gyro(gyro,sensor_timestamp); //把新数据发送给MPLmpu_get_temperature(&temperature,&sensor_timestamp);inv_build_temp(temperature,sensor_timestamp); //把温度值发给MPL,只有陀螺仪需要温度值}if(sensors&INV_XYZ_ACCEL){accel[0] = (long)accel_short[0];accel[1] = (long)accel_short[1];accel[2] = (long)accel_short[2];inv_build_accel(accel,0,sensor_timestamp); //把加速度值发给MPL}if (!mpu_get_compass_reg(compass_short, &sensor_timestamp)){compass[0]=(long)compass_short[0];compass[1]=(long)compass_short[1];compass[2]=(long)compass_short[2];inv_build_compass(compass,0,sensor_timestamp); //把磁力计值发给MPL}inv_execute_on_data();inv_get_sensor_type_euler(data,&accuracy,×tamp);*roll = (data[0]/q16);*pitch = -(data[1]/q16);*yaw = -data[2] / q16;return 0;}
其中,数据从队列中读取代码如下
int dmp_read_fifo(short *gyro, short *accel, long *quat,unsigned long *timestamp, short *sensors, unsigned char *more){unsigned char fifo_data[MAX_PACKET_LENGTH];unsigned char ii = 0;sensors[0] = 0;if (mpu_read_fifo_stream(dmp.packet_length, fifo_data, more))return -1;if (dmp.feature_mask & (DMP_FEATURE_LP_QUAT | DMP_FEATURE_6X_LP_QUAT)) {#ifdef FIFO_CORRUPTION_CHECKlong quat_q14[4], quat_mag_sq;#endifquat[0] = ((long)fifo_data[0] << 24) | ((long)fifo_data[1] << 16) |((long)fifo_data[2] << 8) | fifo_data[3];quat[1] = ((long)fifo_data[4] << 24) | ((long)fifo_data[5] << 16) |((long)fifo_data[6] << 8) | fifo_data[7];quat[2] = ((long)fifo_data[8] << 24) | ((long)fifo_data[9] << 16) |((long)fifo_data[10] << 8) | fifo_data[11];quat[3] = ((long)fifo_data[12] << 24) | ((long)fifo_data[13] << 16) |((long)fifo_data[14] << 8) | fifo_data[15];ii += 16;#ifdef FIFO_CORRUPTION_CHECKquat_q14[0] = quat[0] >> 16;quat_q14[1] = quat[1] >> 16;quat_q14[2] = quat[2] >> 16;quat_q14[3] = quat[3] >> 16;quat_mag_sq = quat_q14[0] * quat_q14[0] + quat_q14[1] * quat_q14[1] +quat_q14[2] * quat_q14[2] + quat_q14[3] * quat_q14[3];if ((quat_mag_sq < QUAT_MAG_SQ_MIN) ||(quat_mag_sq > QUAT_MAG_SQ_MAX)) {/@@* Quaternion is outside of the acceptable threshold. */mpu_reset_fifo();sensors[0] = 0;return -1;}sensors[0] |= INV_WXYZ_QUAT;#endif}if (dmp.feature_mask & DMP_FEATURE_SEND_RAW_ACCEL) {accel[0] = ((short)fifo_data[ii+0] << 8) | fifo_data[ii+1];accel[1] = ((short)fifo_data[ii+2] << 8) | fifo_data[ii+3];accel[2] = ((short)fifo_data[ii+4] << 8) | fifo_data[ii+5];ii += 6;sensors[0] |= INV_XYZ_ACCEL;}if (dmp.feature_mask & DMP_FEATURE_SEND_ANY_GYRO) {gyro[0] = ((short)fifo_data[ii+0] << 8) | fifo_data[ii+1];gyro[1] = ((short)fifo_data[ii+2] << 8) | fifo_data[ii+3];gyro[2] = ((short)fifo_data[ii+4] << 8) | fifo_data[ii+5];ii += 6;sensors[0] |= INV_XYZ_GYRO;}if (dmp.feature_mask & (DMP_FEATURE_TAP | DMP_FEATURE_ANDROID_ORIENT))decode_gesture(fifo_data + ii);get_ms(timestamp);return 0;}
结果

