MPU9250传感器
MPU9250 內部包括 3 軸陀螺儀、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通信協議控制MPU9250,我們需要實現IIC協議。
代碼參考正點原子的源碼,封裝好的函數用起來比較高效。
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,接收應答失敗// 0,接收應答成功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<len;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;}
結果
總結
以上是生活随笔為你收集整理的MPU9250传感器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 金花关键词工具如何使用 金花关键词工具使
- 下一篇: SwiftUI(一)- VStack、H