MPU6050快速開發使用

MPU6050是什麼?
它是由3軸陀螺儀、3軸加速度傳感器組成的6軸運動處理芯片,還可以通過一個預留IIC接口外接磁力傳感器(如GY-282磁羅盤),其它型號還有:MPU6000、MPU9150、MPU9250等。
我們常用它來給系統輸入歐拉角。

關於歐拉角:
在這裏插入圖片描述
如下圖:
α是 x 軸與交點線的夾角 β 是 z 軸與 Z 軸的夾角, γ 是交點線與 X 軸的夾角。
很可惜地,對於夾角的順序和標記,夾角的兩個軸的指定,並沒有任何常規。 科學家對此從未達成共識。每當用到歐拉角時,我們必須明確的表示出夾角的順序,指定其參考軸。
實際上,有許多方法可以設定兩個座標系 的相對取向。歐拉角方法只是其中的一種。此外,不同的作者會用不同組合的歐拉角來描述,或用不同的名字表示同樣的歐拉角。因此,使用歐拉角前,必須先做好明確的定義。
在這裏插入圖片描述

硬件快速搭建:
通信接口:IIC
電源:2.4-3.4V

典型電路:
在這裏插入圖片描述
管腳說明:
在這裏插入圖片描述
第8腳 VLOGIC一般直接接電源即可。(接不同電壓就是指其IO口電壓,如此腳接1.8V,則其IO口邏輯電壓即爲1.8V)
第9腳 是IIC地址選擇,接低:地址爲0x68,接高:地址爲0x69;
與MCU的連接電路(STM32爲例):
在這裏插入圖片描述
特點參數:

① 以數字形式輸出 6 軸或 9 軸(需外接磁傳感器)的旋轉矩陣、四元數 ( 、歐拉角格式 (Euler Angle 的融合演算數據(需 DMP 支持)
② 具有 131 LSBs/ LSBs/°°/sec 敏感度與全格感測範圍爲± 250 、± 500 、± 1000 與± 2000 °°/sec的 3 軸角速度感測器 陀螺儀
③ 集成可程序控制,範圍爲± 2g 、± 4g 、± 8g 和± 16g 的 3 軸加速度傳感器
④ 移除加速器與陀螺儀軸間敏感度,降低設定給予的影響與感測器的飄移
⑤ 自帶數字運 動處理 (DMP: Digital Motion 引擎可減少 MCU 複雜的融合演算數據、感測器同步化、姿勢感應等的負荷
⑥ 內建運作時間偏差與磁力感測器校正演算技術,免除了客戶須另外進行校正的需求
⑦ 自帶一個數字溫度傳感器
⑧ 帶數字輸入同步引腳 (Sync 支持視頻電子影相穩定技術與 GPS
⑨ 可程序控制的中斷 ( interrupt),支持姿勢識別、搖攝、畫面放大縮小、滾動、快速下降中斷、 high G 中斷、零動作感應、觸擊感應、搖動感應功能
⑩ VDD 供電電壓爲 2.5V 5% 、 3.0V 5% 、 3.3V 5% VLOGIC 可低至 1.8V 5%
⑪ 陀螺儀工作電流: 5mA ,陀螺儀待機電流 5uA ;加速器工作電流 500uA ,加速器省電模式電流: 40uA@10Hz
⑫ 自帶 1024 字節 FIFO ,有助於降低系統功耗
⑬ 高達 400Khz 的 IIC 通信接口
⑭ 超小封裝尺寸: 4x4x0.9mm QFN

MPU6050程序快速開發:

程序開發步驟:
MPU6050的初始化:

1、初始化IIC接口;
2、設置角速度傳感器、加速度傳感器的量程範圍;
3、設置其它參數,如:
      配置中斷、設置AUX IIC接口、設置FIFO、設置陀螺儀採樣率、設置數字濾波器等。
4、設置系統時鐘;
5、使能角速度、加速度傳感器;
這些設置都是通過配置MPU6050內部寄存器設置的,具體步驟可參考正點原子關於MPU6050章節的介紹;

下面是正點原子STM32的MPU6050初始化代碼:

u8 MPU_Init(void)
{ 
	u8 res;
    GPIO_InitTypeDef  GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO時鐘 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//先使能外設IO PORTA時鐘 
	
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;	 // 端口配置
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推輓輸出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度爲50MHz
    GPIO_Init(GPIOA, &GPIO_InitStructure);					 //根據設定參數初始化GPIOA

	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁止JTAG,從而PA15可以做普通IO使用,否則PA15不能做普通IO!!!
	
	MPU_AD0_CTRL=0;			//控制MPU6050的AD0腳爲低電平,從機地址爲:0X68
	
	MPU_IIC_Init();//初始化IIC總線
	MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X80);	//復位MPU6050
    delay_ms(100);
	MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X00);	//喚醒MPU6050 
	MPU_Set_Gyro_Fsr(3);					//陀螺儀傳感器,±2000dps
	MPU_Set_Accel_Fsr(0);					//加速度傳感器,±2g
	MPU_Set_Rate(50);						//設置採樣率50Hz
	MPU_Write_Byte(MPU_INT_EN_REG,0X00);	//關閉所有中斷
	MPU_Write_Byte(MPU_USER_CTRL_REG,0X00);	//I2C主模式關閉
	MPU_Write_Byte(MPU_FIFO_EN_REG,0X00);	//關閉FIFO
	MPU_Write_Byte(MPU_INTBP_CFG_REG,0X80);	//INT引腳低電平有效
	res=MPU_Read_Byte(MPU_DEVICE_ID_REG);
	if(res==MPU_ADDR)//器件ID正確
	{
		MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X01);	//設置CLKSEL,PLL X軸爲參考
		MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X00);	//加速度與陀螺儀都工作
		MPU_Set_Rate(50);						//設置採樣率爲50Hz
 	}else return 1;
	return 0;
}
//設置MPU6050陀螺儀傳感器滿量程範圍
//fsr:0,±250dps;1,±500dps;2,±1000dps;3,±2000dps
//返回值:0,設置成功
//    其他,設置失敗 
u8 MPU_Set_Gyro_Fsr(u8 fsr)
{
	return MPU_Write_Byte(MPU_GYRO_CFG_REG,fsr<<3);//設置陀螺儀滿量程範圍  
}
//設置MPU6050加速度傳感器滿量程範圍
//fsr:0,±2g;1,±4g;2,±8g;3,±16g
//返回值:0,設置成功
//    其他,設置失敗 
u8 MPU_Set_Accel_Fsr(u8 fsr)
{
	return MPU_Write_Byte(MPU_ACCEL_CFG_REG,fsr<<3);//設置加速度傳感器滿量程範圍  
}
//設置MPU6050的數字低通濾波器
//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(MPU_CFG_REG,data);//設置數字低通濾波器  
}
//設置MPU6050的採樣率(假定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(MPU_SAMPLE_RATE_REG,data);	//設置數字低通濾波器
 	return MPU_Set_LPF(rate/2);	//自動設置LPF爲採樣率的一半
}

//得到溫度值
//返回值:溫度值(擴大了100倍)
short MPU_Get_Temperature(void)
{
    u8 buf[2]; 
    short raw;
	float temp;
	MPU_Read_Len(MPU_ADDR,MPU_TEMP_OUTH_REG,2,buf); 
    raw=((u16)buf[0]<<8)|buf[1];  
    temp=36.53+((double)raw)/340;  
    return temp*100;;
}
//得到陀螺儀值(原始值)
//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(MPU_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(MPU_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;;
}
//IIC連續寫
//addr:器件地址 
//reg:寄存器地址
//len:寫入長度
//buf:數據區
//返回值:0,正常
//    其他,錯誤代碼
u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
	u8 i; 
    MPU_IIC_Start(); 
	MPU_IIC_Send_Byte((addr<<1)|0);//發送器件地址+寫命令	
	if(MPU_IIC_Wait_Ack())	//等待應答
	{
		MPU_IIC_Stop();		 
		return 1;		
	}
    MPU_IIC_Send_Byte(reg);	//寫寄存器地址
    MPU_IIC_Wait_Ack();		//等待應答
	for(i=0;i<len;i++)
	{
		MPU_IIC_Send_Byte(buf[i]);	//發送數據
		if(MPU_IIC_Wait_Ack())		//等待ACK
		{
			MPU_IIC_Stop();	 
			return 1;		 
		}		
	}    
    MPU_IIC_Stop();	 
	return 0;	
} 
//IIC連續讀
//addr:器件地址
//reg:要讀取的寄存器地址
//len:要讀取的長度
//buf:讀取到的數據存儲區
//返回值:0,正常
//    其他,錯誤代碼
u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{ 
 	MPU_IIC_Start(); 
	MPU_IIC_Send_Byte((addr<<1)|0);//發送器件地址+寫命令	
	if(MPU_IIC_Wait_Ack())	//等待應答
	{
		MPU_IIC_Stop();		 
		return 1;		
	}
    MPU_IIC_Send_Byte(reg);	//寫寄存器地址
    MPU_IIC_Wait_Ack();		//等待應答
    MPU_IIC_Start();
	MPU_IIC_Send_Byte((addr<<1)|1);//發送器件地址+讀命令	
    MPU_IIC_Wait_Ack();		//等待應答 
	while(len)
	{
		if(len==1)*buf=MPU_IIC_Read_Byte(0);//讀數據,發送nACK 
		else *buf=MPU_IIC_Read_Byte(1);		//讀數據,發送ACK  
		len--;
		buf++; 
	}    
    MPU_IIC_Stop();	//產生一個停止條件 
	return 0;	
}
//IIC寫一個字節 
//reg:寄存器地址
//data:數據
//返回值:0,正常
//    其他,錯誤代碼
u8 MPU_Write_Byte(u8 reg,u8 data) 				 
{ 
    MPU_IIC_Start(); 
	MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);//發送器件地址+寫命令	
	if(MPU_IIC_Wait_Ack())	//等待應答
	{
		MPU_IIC_Stop();		 
		return 1;		
	}
    MPU_IIC_Send_Byte(reg);	//寫寄存器地址
    MPU_IIC_Wait_Ack();		//等待應答 
	MPU_IIC_Send_Byte(data);//發送數據
	if(MPU_IIC_Wait_Ack())	//等待ACK
	{
		MPU_IIC_Stop();	 
		return 1;		 
	}		 
    MPU_IIC_Stop();	 
	return 0;
}
//IIC讀一個字節 
//reg:寄存器地址 
//返回值:讀到的數據
u8 MPU_Read_Byte(u8 reg)
{
	u8 res;
    MPU_IIC_Start(); 
	MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);//發送器件地址+寫命令	
	MPU_IIC_Wait_Ack();		//等待應答 
    MPU_IIC_Send_Byte(reg);	//寫寄存器地址
    MPU_IIC_Wait_Ack();		//等待應答
    MPU_IIC_Start();
	MPU_IIC_Send_Byte((MPU_ADDR<<1)|1);//發送器件地址+讀命令	
    MPU_IIC_Wait_Ack();		//等待應答 
	res=MPU_IIC_Read_Byte(0);//讀取數據,發送nACK 
    MPU_IIC_Stop();			//產生一個停止條件 
	return res;		
}

經過上面的設置,即可讀出MPU6050 的加速度傳感器角速度傳感器的原始數據。不過這些原始數據對於希望獲取姿態數據的情況時用處不大,我們期望得到的是歐拉角:航向角 yaw 、橫滾角 roll 、俯仰角 pitch。要得到歐拉角數據,就得利用我們的原始數據,進行姿態融合解算,這個比較複雜,幸而MPU6050 自帶了數字運動處理器,即 DMP 。它可以將內部角度、陀螺等傳感器進行融合計算,輸出一個四元數,然後我們再通過計算公式將四元數轉換爲歐拉角。
使用MPU6050內置DMP可大大簡化驅動代碼設計,MCU將不需要進行姿態解算,可降低MCU負擔。
MPU6050的DMP輸出的四元數是q30格式,就是浮點數放大了2的30次方,在具體換算成歐拉角之前,需先將其轉換爲浮點數,也就是除以2的30次方即可,也就是:“歐拉角=MPU6050輸出的四元數 / 2^30(1073741824)”;下面給出正點原子的代碼:

#define q30  1073741824.0f

q0=quat[0] / q30; //q30 格式轉換爲浮點數
q1=quat[1] / q30;
q2=quat[2] / q30;
q3=quat[3] / q30;
//計算得到俯仰角、橫滾角、航向角:
pitch=asin( 2 * q1 * q3 + 2 * q0* q2)* 57.3; 俯仰角
roll =atan2(2 * q2 * q3 + 2 * q0 * q1, 2 * q1 * q1 2 * q2* q2 + 1)* 57.3; 橫滾角
yaw=atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1 q2*q2 q3*q3) * 57.3; 航向角
//公式中的57.3是弧度抓換爲角度,即180/ π,這樣得到的結果就是以度(°)爲單位的。(關於爲什麼這樣計算的數學推導這裏不做深究)

上述中的q0、q1、q2、q3,如何獲得?它是MPU6050官方給出的一套C代碼驅動庫中完成的,實際開發中,需要將其移植到項目工程中,具體移植還是可以直接參考正點原子,其實主要就是要用它的兩個.c文件:“inv_mpu.c和inv_mpu_dmp_motion_driver.c”。移植完成後,即可直接調用函數接口,正點原子寫出了兩個函數可完成最終的歐拉角獲取:
使用DMP前要先初始化MPU6050的DMP,下面是代碼:
(這是已經移植好MPU6050官方驅動庫文件的情況下)

//mpu6050,dmp初始化
//返回值:0,正常
//    其他,失敗
u8 mpu_dmp_init(void)
{
	u8 res=0;
	MPU_IIC_Init(); 	//初始化IIC總線
	if(mpu_init()==0)	//初始化MPU6050
	{
		res=mpu_set_sensors(INV_XYZ_GYRO|INV_XYZ_ACCEL);//設置所需要的傳感器
		if(res)return 1; 
		res=mpu_configure_fifo(INV_XYZ_GYRO|INV_XYZ_ACCEL);//設置FIFO
		if(res)return 2; 
		res=mpu_set_sample_rate(DEFAULT_MPU_HZ);	//設置採樣率
		if(res)return 3; 
		res=dmp_load_motion_driver_firmware();		//加載dmp固件
		if(res)return 4; 
		res=dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation));//設置陀螺儀方向
		if(res)return 5; 
		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 6; 
		res=dmp_set_fifo_rate(DEFAULT_MPU_HZ);	//設置DMP輸出速率(最大不超過200Hz)
		if(res)return 7;   
		res=run_self_test();		//自檢
		if(res)return 8;    
		res=mpu_set_dmp_state(1);	//使能DMP
		if(res)return 9;
	}else return 10;
	return 0;
}

如此,通過“u8 mpu_dmp_init(void)”這個函數即可獲取MPU6050的FIFO輸出的四元數,但我們還要經過上述的換算,才能得到歐拉角,函數代碼如下:

//得到dmp處理後的數據(注意,本函數需要比較多堆棧,局部變量有點多)
//pitch:俯仰角 精度:0.1°   範圍:-90.0° <---> +90.0°
//roll:橫滾角  精度:0.1°   範圍:-180.0°<---> +180.0°
//yaw:航向角   精度:0.1°   範圍:-180.0°<---> +180.0°
//返回值:0,正常
//    其他,失敗
u8 mpu_dmp_get_data(float *pitch,float *roll,float *yaw)
{
	float q0=1.0f,q1=0.0f,q2=0.0f,q3=0.0f;
	unsigned long sensor_timestamp;
	short gyro[3], accel[3], sensors;
	unsigned char more;
	long quat[4]; 
	if(dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors,&more))return 1;	 
	/* Gyro and accel data are written to the FIFO by the DMP in chip frame and hardware units.
	 * This behavior is convenient because it keeps the gyro and accel outputs of dmp_read_fifo and mpu_read_fifo consistent.
	**/
	/*if (sensors & INV_XYZ_GYRO )
	send_packet(PACKET_TYPE_GYRO, gyro);
	if (sensors & INV_XYZ_ACCEL)
	send_packet(PACKET_TYPE_ACCEL, accel); */
	/* Unlike gyro and accel, quaternions are written to the FIFO in the body frame, q30.
	 * The orientation is set by the scalar passed to dmp_set_orientation during initialization. 
	**/
	if(sensors&INV_WXYZ_QUAT) 
	{
		q0 = quat[0] / q30;	//q30格式轉換爲浮點數
		q1 = quat[1] / q30;
		q2 = quat[2] / q30;
		q3 = quat[3] / q30; 
		//計算得到俯仰角/橫滾角/航向角
		*pitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3;	// pitch
		*roll = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3;	// roll
		*yaw = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3) * 57.3;	//yaw
	}else return 2;
	return 0;
}

至此,我們即得到了MPU6050的三個歐拉角:pitch、roll、yaw。

關於GY282電子羅盤:

它使用了6050的寄存器,並聯在6050的內部的數據總線上了,而並非直接在系統總線上通過sda和scl讀取,系統能夠直接尋址。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章