第44章 MPU6050傳感器—姿態檢測
全套200集視頻教程和1000頁PDF教程請到秉火論壇下載:www.firebbs.cn
野火視頻教程優酷觀看網址:http://i.youku.com/firege
本章參考數據:《STM32F4xx參考手冊》、《STM32F4xx規格書》、庫說明文檔《stm32f4xx_dsp_stdperiph_lib_um.chm》。
關於MPU6050的參考資料:《MPU-60X0寄存器》、《MPU6050數據手冊》以及官方驅動《motion_driver_6.12》。
本章講解的內容跨領域的知識較多,若您感興趣,請自行查閱各方面的資料,對比學習。
44.1 姿態檢測
1. 基本認識
在飛行器中,飛行姿態是非常重要的參數,見圖 441,以飛機自身的中心建立坐標系,當飛機繞坐標軸旋轉的時候,會分別影響偏航角、橫滾角及俯仰角。
圖 441 表示飛機姿態的偏航角、橫滾角及俯仰角
假如我們知道飛機初始時是左上角的狀態,只要想辦法測量出基於原始狀態的三個姿態角的變化量,再進行疊加,就可以獲知它的實時姿態了。
2. 坐標系
抽象來說,姿態是"載體坐標系"與"地理坐標系"之間的轉換關系。
圖 442 地球坐標系、地理坐標系與載體坐標系
我們先來了解三種常用的坐標系:
地球坐標系:以地球球心為原點,Z軸沿地球自轉軸方向,X、Y軸在赤道平面內的坐標系。
地理坐標系:它的原點在地球表面(或運載體所在的點),Z軸沿當地地理垂線的方向(重力加速度方向),XY軸沿當地經緯線的切線方向。根據各個軸方向的不同,可選為"東北天"、"東南天"、"西北天"等坐標系。這是我們日常生活中使用的坐標系,平時說的東南西北方向與這個坐標系東南西北的概念一致。
載體坐標系:載體坐標系以運載體的質心為原點,一般根據運載體自身結構方向構成坐標系,如Z軸上由原點指向載體頂部,Y軸指向載體頭部,X軸沿載體兩側方向。上面說基於飛機建立的坐標系就是一種載體坐標系,可類比到汽車、艦船、人體、動物或手機等各種物體。
地理坐標系與載體坐標系都以載體為原點,所以它們可以經過簡單的旋轉進行轉換,載體的姿態角就是根據載體坐標系與地理坐標系的夾角來確定的。配合圖 441,發揮您的空間想象力,假設初始狀態中,飛機的Z軸、X軸及Y軸分別與地理坐標系的天軸、北軸、東軸平行。如當飛機繞自身的"Z"軸旋轉,它會使自身的"Y"軸方向與地理坐標系的"南北"方向偏離一定角度,該角度就稱為偏航角(Yaw);當載體繞自身的"X"軸旋轉,它會使自身的"Z"軸方向與地理坐標系的"天地"方向偏離一定角度,該角度稱為俯仰角(Pitch);當載體繞自身的"Y"軸旋轉,它會使自身的"X"軸方向與地理坐標系的"東西"方向偏離一定角度,該角度稱為橫滾角。
表 441 姿態角的關系
坐標系間的旋轉角度 |
說明 |
載體自身旋轉 |
偏航角(Yaw) |
Y軸與北軸的夾角 |
繞載體Z軸旋轉可改變 |
俯仰角(Pitch) |
Z軸與天軸的夾角 |
繞載體X軸旋轉可改變 |
橫滾角(Roll) |
X軸與東軸的夾角 |
繞載體Y軸旋轉可改變 |
這些角度也稱歐拉角,是用於描述姿態的非常直觀的角度。
44.1.2 利用陀螺儀檢測角度
最直觀的角度檢測器就是陀螺儀了,見圖 443,它可以檢測物體繞坐標軸轉動的"角速度",如同將速度對時間積分可以求出路程一樣,將角速度對時間積分就可以計算出旋轉的"角度"。
圖 443 陀螺儀檢測示意圖
陀螺儀檢測的缺陷
由於陀螺儀測量角度時使用積分,會存在積分誤差,見圖 444,若積分時間Dt越小,誤差就越小。這十分容易理解,例如計算路程時,假設行車時間為1小時,我們隨機選擇行車過程某個時刻的速度Vt乘以1小時,求出的路程誤差是極大的,因為行車的過程中並不是每個時刻都等於該時刻速度的,如果我們每5分鍾檢測一次車速,可得到Vt1、Vt2、Vt3-Vt12這12個時刻的車速,對各個時刻的速度乘以時間間隔(5分鍾),並對這12個結果求和,就可得出一個相對精確的行車路程了,不斷提高采樣頻率,就可以使積分時間Dt變小,降低誤差。
圖 444 積分誤差
同樣地,提高陀螺儀傳感器的采樣頻率,即可減少積分誤差,目前非常普通的陀螺儀傳感器的采樣頻率都可以達到8KHz,已能滿足大部分應用的精度要求。
更難以解決的是器件本身誤差帶來的問題。例如,某種陀螺儀的誤差是0.1度/秒,當陀螺儀靜止不動時,理想的角速度應為0,無論它靜止多久,對它進行積分測量得的旋轉角度都是0,這是理想的狀態;而由於存在0.1度/秒的誤差,當陀螺儀靜止不動時,它采樣得的角速度一直為0.1度/秒,若靜止了1分鍾,對它進行積分測量得的旋轉角度為6度,若靜止了1小時,陀螺儀進行積分測量得的旋轉角度就是360度,即轉過了一整圈,這就變得無法忍受了。只有當正方向誤差和負方向誤差能正好互相抵消的時候,才能消除這種累計誤差。
44.1.3 利用加速度計檢測角度
由於直接用陀螺儀測量角度在長時間測量時會產生累計誤差,因而我們又引入了檢測傾角的傳感器。
圖 445 T字型水平儀
測量傾角最常見的例子是建築中使用的水平儀,在重力的影響下,水平儀內的氣泡能大致反映水柱所在直線與重力方向的夾角關系,利用圖 445中的T字型水平儀,可以檢測出圖 441中說明的橫滾角與俯仰角,但是偏航角是無法以這樣的方式檢測的。
在電子設備中,一般使用加速度傳感器來檢測傾角,它通過檢測器件在各個方向的形變情況而采樣得到受力數據,根據F=ma轉換,傳感器直接輸出加速度數據,因而被稱為加速度傳感器。由於地球存在重力場,所以重力在任何時刻都會作用於傳感器,當傳感器靜止的時候(實際上加速度為0),傳感器會在該方向檢測出加速度g,不能認為重力方向測出的加速度為g,就表示傳感器在該方向作加速度為g的運動。
當傳感器的姿態不同時,它在自身各個坐標軸檢測到的重力加速度是不一樣的,利用各方向的測量結果,根據力的分解原理,可求出各個坐標軸與重力之間的夾角,見圖 446。
圖 446 重力檢測
因為重力方向是與地理坐標系的"天地"軸固連的,所以通過測量載體坐標系各軸與重力方向的夾角即可求得它與地理坐標系的角度旋轉關系,從而獲知載體姿態。
加速度傳感器檢測的缺陷
由於這種傾角檢測方式是利用重力進行檢測的,它無法檢測到偏航角(Yaw),原理跟T字型水平儀一樣,無論如何設計水平儀,水泡都無法指示這樣的角度。
另一個缺陷是加速度傳感器並不會區分重力加速度與外力加速度,當物體運動的時候,它也會在運動的方向檢測出加速度,特別在震動的狀態下,傳感器的數據會有非常大的數據變化,此時難以反應重力的實際值。
44.1.4 利用磁場檢測角度
為了彌補加速度傳感器無法檢測偏航角(Yaw)的問題,我們再引入磁場檢測傳感器,它可以檢測出各個方向上的磁場大小,通過檢測地球磁場,它可實現指南針的功能,所以也被稱為電子羅盤。由於地磁場與地理坐標系的"南北"軸固聯,利用磁場檢測傳感器的指南針功能,就可以測量出偏航角(Yaw)了。
磁場檢測器的缺陷
與指南針的缺陷一樣,使用磁場傳感器會受到外部磁場干擾,如載體本身的電磁場干擾,不同地理環境的磁鐵礦干擾等等。
44.1.5 利用GPS檢測角度
使用GPS可以直接檢測出載體在地球上的坐標,假如載體在某時刻測得坐標為A,另一時刻測得坐標為B,利用兩個坐標即可求出它的航向,即可以確定偏航角,且不受磁場的影響,但這種檢測方式只有當載體產生大范圍位移的時候才有效(GPS民用精度大概為10米級)。
44.1.6 姿態融合與四元數
可以發現,使用陀螺儀檢測角度時,在靜止狀態下存在缺陷,且受時間影響,而加速度傳感器檢測角度時,在運動狀態下存在缺陷,且不受時間影響,剛好互補。假如我們同時使用這兩種傳感器,並設計一個濾波算法,當物體處於靜止狀態時,增大加速度數據的權重,當物體處於運動狀時,增大陀螺儀數據的權重,從而獲得更准確的姿態數據。同理,檢測偏航角,當載體在靜止狀態時,可增大磁場檢測器數據的權重,當載體在運動狀態時,增大陀螺儀和GPS檢測數據的權重。這些采用多種傳感器數據來檢測姿態的處理算法被稱為姿態融合。
在姿態融合解算的時候常常使用"四元數"來表示姿態,它由三個實數及一個虛數組成,因而被稱之為四元數。使用四元數表示姿態並不直觀,但因為使用歐拉角(即前面說的偏航角、橫滾角及俯仰角)表示姿態的時候會有"萬向節死鎖"問題,且運算比較復雜,所以一般在數據處理的時候會使用四元數,處理完畢后再把四元數轉換成歐拉角。在這里我們只要了解四元數是姿態的另一種表示方式即可,感興趣的話可自行查閱相關資料。
44.2 傳感器
1. 傳感器工作原理
前文提到了各種傳感器,在這里大致講解一下傳感器的工作原理。我們講的傳感器一般是指把物理量轉化成電信號量的裝置,見圖 447。
圖 447傳感器工作原理
敏感元件直接感受被測物理量,並輸出與該物理量有確定關系的信號,經過轉換元件將該物理量信號轉換為電信號,變換電路對轉換元件輸出的電信號進行放大調制,最后輸出容易檢測的電信號量。例如,溫度傳感器可把溫度量轉化成電壓信號量輸出,且溫度值與電壓值成比例關系,我們只要使用ADC測量出電壓值,並根據轉換關系即可求得實際溫度值。而前文提到的陀螺儀、加速度及磁場傳感器也是類似的,它們檢測的角速度、加速度及磁場強度與電壓值有確定的轉換關系。
2. 傳感器參數
傳感器一般使用精度、分辨率及采樣頻率這些參數來進行比較,衡量它的性能,見表 442。
表 442 傳感器參數
參數 |
說明 |
線性誤差 |
指傳感器測量值與真實物理量值之間的擬合度誤差。 |
分辨率 |
指傳感器可檢測到的最小物理量的單位。 |
采樣頻率 |
指在單位時間內的采樣次數。 |
其中誤差與分辨率是比較容易混淆的概念,以使用尺子測量長度為例,誤差就是指尺子准不准,使用它測量出10厘米,與計量機構標准的10厘米有多大區別,若區別在5毫米以內,我們則稱這把尺子的誤差為5毫米。而分辨率是指尺子的最小刻度值,假如尺子的最小刻度值為1厘米,我們稱這把尺子的分辨率為1厘米,它只能用於測量厘米級的尺寸,對於毫米級的長度,這就無法用這把尺子進行測量了。如果把尺子加熱拉長,尺子的誤差會大於5毫米,但它的分辨率仍為1厘米,只是它測出的1厘米值與真實值之間差得更遠了。
3. 物理量的表示方法
大部分傳感器的輸出都是與電壓成比例關系的,電壓值一般采用ADC來測量,而ADC一般有固定的位數,如8位ADC、12位ADC等,ADC的位數會影響測量的分辨率及量程。例如圖 448,假設用一個2位的ADC來測量長度,2位的ADC最多只能表示0、1、2、3這四個數,假如它的分辨率為20厘米,那么它最大的測量長度為60厘米,假如它的分辨率為10厘米,那么它的最大測量長度為30厘米,由此可知,對於特定位數的ADC,量程和分辨率不可兼得。
圖 448 ADC表示的物理量范圍
在實際應用中,常常直接用ADC每位表征的物理量值來表示分辨率,如每位代表20厘米,我們稱它的分辨率為1LSB/20cm,它等效於5位表示1米:5LSB/m。其中的LSB(Least Significant Bit),意為最ADC的低有效位。
使用采樣得到的ADC數值,除以分辨率,即可求取得到物理量。例如使用分辨率為5LSB/m、線性誤差為0.1m的傳感器進行長度測量,其ADC采樣得到數據值為"20",可計算知道該傳感器的測量值為4米,而該長度的真實值介於3.9-4.1米之間。
44.3 MPU6050簡介
接下來我們使用傳感器實例來講解如何檢測物體的姿態。在我們的STM32F4實驗板上有一個MPU6050芯片,它是一種六軸傳感器模塊,采用InvenSense公司的MPU6050作為主芯片,能同時檢測三軸加速度、三軸陀螺儀(三軸角速度)的運動數據以及溫度數據。利用MPU6050芯片內部的DMP模塊(Digital Motion Processor數字運動處理器),可對傳感器數據進行濾波、融合處理,它直接通過I2C接口向主控器輸出姿態解算后的姿態數據,降低主控器的運算量。其姿態解算頻率最高可達200Hz,非常適合用於對姿態控制實時要求較高的領域。常見應用於手機、智能手環、四軸飛行器及計步器等的姿態檢測。
圖 449 MPU6050傳感器的坐標及方向
圖 449中表示的坐標系及旋轉符號標出了MPU6050傳感器的XYZ軸的加速度有角速度的正方向。
44.4 MPU6050的特性參數
實驗板中使用的MPU6050傳感器參數見表 443。
表 443 MPU6050的特性參數
參數 |
說明 |
供電 |
3.3V-5V |
通訊接口 |
I2C協議,支持的I2C時鍾最高頻率為400KHz |
測量維度 |
加速度:3維陀螺儀:3維 |
ADC分辨率 |
加速度:16位陀螺儀:16位 |
加速度測量范圍 |
±2g、±4g、±8g、±16g 其中g為重力加速度常數,g=9.8m/s ² |
加速度最高分辨率 |
16384 LSB/g |
加速度線性誤差 |
0.1g |
加速度輸出頻率 |
最高1000Hz |
陀螺儀測量范圍 |
±250 º/s 、±500 º/s 、±1000 º/s、±2000 º/s、 |
陀螺儀最高分辨率 |
131 LSB/( º/s) |
陀螺儀線性誤差 |
0.1 º/s |
陀螺儀輸出頻率 |
最高 8000Hz |
DMP姿態解算頻率 |
最高200Hz |
溫度傳感器測量范圍 |
-40~ +85℃ |
溫度傳感器分辨率 |
340 LSB/℃ |
溫度傳感器線性誤差 |
±1℃ |
工作溫度 |
-40~ +85℃ |
功耗 |
500uA~3.9mA (工作電壓3.3V) |
該表說明,加速度與陀螺儀傳感器的ADC均為16位,它們的量程及分辨率可選多種模式,見圖 4411,量程越大,分辨率越低。
圖 4410 加速度配置跟量程的關系
圖 4411 陀螺儀的幾種量程配置
從表中還可了解到傳感器的加速度及陀螺儀的采樣頻率分別為1000Hz及8000Hz,它們是指加速度及角速度數據的采樣頻率,我們可以使用STM32控制器把這些數據讀取出來然后進行姿態融合解算,以求出傳感器當前的姿態(即求出偏航角、橫滾角、俯仰角)。而如果我們使用傳感器內部的DMP單元進行解算,它可以直接對采樣得到的加速度及角速度進行姿態解算,解算得到的結果再輸出給STM32控制器,即STM32無需自己計算,可直接獲取偏航角、橫滾角及俯仰角,該DMP每秒可輸出200次姿態數據。
44.5 MPU6050—獲取原始數據實驗
這一小節我們學習如何使用STM32控制MPU6050傳感器讀取加速度、角速度及溫度數據。在控制傳感器時,使用到了STM32的I2C驅動,就如同控制STM32一樣,對MPU6050傳感器的不同寄存器寫入不同內容可以實現不同模式的控制,從特定的寄存器讀取內容則可獲取測量數據,這部分關於MPU6050具體寄存器的內容我們不再展開,請您查閱《MPU-60X0寄存器》手冊獲知。
44.5.1 硬件設計
STM32與MPU6050的硬件連接見圖 428。
圖 4412 STM32與MPU6050的硬件連接
它的硬件連接非常簡單,SDA與SCL引出到STM32的I2C引腳,注意圖中的I2C沒有畫出上拉電阻,只是因為實驗板中其它芯片也使用了同樣的I2C總線,電阻畫到了其它芯片的圖里,沒有出現在這個圖中而已。傳感器的I2C設備地址可通過AD0引腳的電平控制,當AD0接地時,設備地址為0x68(七位地址),當AD0接電源時,設備地址為0x69(八位地址)。另外,傳感器的INT引腳接到了STM32的普通IO口,當傳感器有新數據的時候會通過INT引腳通知STM32。
由於MPU6050檢測時是基於自已中心坐標系的,所以在自己設計硬件時,您需要考慮它與所在設備的坐標系統的關系。
44.5.2 軟件設計
本小節講解的是"MPU6050基本數據讀取"實驗,請打開配套的代碼工程閱讀理解。為了方便展示及移植,我們把STM32的I2C驅動相關的代碼都編寫到"i2c.c"及"i2c.h"文件中,與MPU6050傳感器相關的代碼都寫到"mpu6050.c"及"mpu6050.h"文件中,這些文件是我們自己編寫的,不屬於標准庫的內容,可根據您的喜好命名文件。
1. 程序設計要點
(4) 初始化STM32的I2C;
(5) 使用I2C向MPU6050寫入控制參數;
(6) 定時讀取加速度、角速度及溫度數據。
2. 代碼分析
I2C的硬件定義
本實驗中的I2C驅動與MPU6050驅動分開主要是考慮到擴展其它傳感器時的通用性,如使用磁場傳感器、氣壓傳感器都可以使用同樣一個I2C驅動,這個驅動只要給出針對不同傳感器時的不同讀寫接口即可。關於STM32的I2C驅動原理請參考讀寫EEPROM的章節,本章講解的I2C驅動主要針對接口封裝講解,細節不再贅述。本實驗中的I2C硬件定義見代碼清單 441。
代碼清單 441 I2C的硬件定義(i2c.h文件)
1 /*引腳定義*/
2
3 #define SENSORS_I2C_SCL_GPIO_PORT GPIOB
4 #define SENSORS_I2C_SCL_GPIO_CLK RCC_AHB1Periph_GPIOB
5 #define SENSORS_I2C_SCL_GPIO_PIN GPIO_Pin_6
6 #define SENSORS_I2C_SCL_GPIO_PINSOURCE GPIO_PinSource6
7
8 #define SENSORS_I2C_SDA_GPIO_PORT GPIOB
9 #define SENSORS_I2C_SDA_GPIO_CLK RCC_AHB1Periph_GPIOB
10 #define SENSORS_I2C_SDA_GPIO_PIN GPIO_Pin_7
11 #define SENSORS_I2C_SDA_GPIO_PINSOURCE GPIO_PinSource7
12
13 #define SENSORS_I2C_AF GPIO_AF_I2C1
14
15 #define SENSORS_I2C I2C1
16 #define SENSORS_I2C_RCC_CLK RCC_APB1Periph_I2C1
這些宏根據傳感器使用的I2C硬件封裝起來了。
初始化I2C
接下來利用這些宏對I2C進行初始化,初始化過程與I2C讀寫EEPROM中的無異,見代碼清單 442。
代碼清單 442 初始化I2C(i2c.c文件)
1 /**
2 * @brief 初始化I2C總線,使用I2C前需要調用
3 * @param 無
4 * @retval 無
5 */
6 void I2cMaster_Init(void)
7 {
8 GPIO_InitTypeDef GPIO_InitStructure;
9 I2C_InitTypeDef I2C_InitStructure;
10
11 /* Enable I2Cx clock */
12 RCC_APB1PeriphClockCmd(SENSORS_I2C_RCC_CLK, ENABLE);
13
14 /* Enable I2C GPIO clock */
15 RCC_AHB1PeriphClockCmd(SENSORS_I2C_SCL_GPIO_CLK |
16 SENSORS_I2C_SDA_GPIO_CLK, ENABLE);
17
18 /* Configure I2Cx pin: SCL ----------------------------------------*/
19 GPIO_InitStructure.GPIO_Pin = SENSORS_I2C_SCL_GPIO_PIN;
20 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
21 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
22 GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
23 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
24
25 /* Connect pins to Periph */
26 GPIO_PinAFConfig(SENSORS_I2C_SCL_GPIO_PORT, SENSORS_I2C_SCL_GPIO_PINSOURCE,
27 SENSORS_I2C_AF);
28 GPIO_Init(SENSORS_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
29
30 /* Configure I2Cx pin: SDA ----------------------------------------*/
31 GPIO_InitStructure.GPIO_Pin = SENSORS_I2C_SDA_GPIO_PIN;
32
33 /* Connect pins to Periph */
34 GPIO_PinAFConfig(SENSORS_I2C_SDA_GPIO_PORT, SENSORS_I2C_SDA_GPIO_PINSOURCE,
35 SENSORS_I2C_AF);
36 GPIO_Init(SENSORS_I2C_SDA_GPIO_PORT, &GPIO_InitStructure);
37
38 I2C_DeInit(SENSORS_I2C);
39 I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
40 I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
41 I2C_InitStructure.I2C_OwnAddress1 = I2C_OWN_ADDRESS;
42 I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
43 I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
44 I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED;
45
46 /* Enable the I2C peripheral */
47 I2C_Cmd(SENSORS_I2C, ENABLE);
48
49 /* Initialize the I2C peripheral */
50 I2C_Init(SENSORS_I2C, &I2C_InitStructure);
51
52 return;
53 }
對讀寫函數的封裝
初始化完成后就是編寫I2C讀寫函數了,這部分跟EERPOM的一樣,主要是調用STM32標准庫函數讀寫數據寄存器及標志位,本實驗的這部分被編寫進ST_Sensors_I2C_WriteRegister及ST_Sensors_I2C_ReadRegister中了,在它們之上,再封裝成了Sensors_I2C_WriteRegister及Sensors_I2C_ReadRegister,見代碼清單 443。
代碼清單 443 對讀寫函數的封裝(i2c.c文件)
1
2 /**
3 * @brief 寫寄存器(多次嘗試),這是提供給上層的接口
4 * @param slave_addr: 從機地址
5 * @param reg_addr:寄存器地址
6 * @param len:寫入的長度
7 * @param data_ptr:指向要寫入的數據
8 * @retval 正常為0,不正常為非0
9 */
10 int Sensors_I2C_WriteRegister(unsigned char slave_addr,
11 unsigned char reg_addr,
12 unsigned short len,
13 const unsigned char *data_ptr)
14 {
15 char retries=0;
16 int ret = 0;
17 unsigned short retry_in_mlsec = Get_I2C_Retry();
18
19 tryWriteAgain:
20 ret = 0;
21 ret = ST_Sensors_I2C_WriteRegister( slave_addr, reg_addr, len, data_ptr);
22
23 if (ret && retry_in_mlsec) {
24 if ( retries++ > 4 )
25 return ret;
26
27 Delay(retry_in_mlsec);
28 goto tryWriteAgain;
29 }
30 return ret;
31 }
32
33 /**
34 * @brief 讀寄存器(多次嘗試),這是提供給上層的接口
35 * @param slave_addr: 從機地址
36 * @param reg_addr:寄存器地址
37 * @param len:要讀取的長度
38 * @param data_ptr:指向要存儲數據的指針
39 * @retval 正常為0,不正常為非0
40 */
41 int Sensors_I2C_ReadRegister(unsigned char slave_addr,
42 unsigned char reg_addr,
43 unsigned short len,
44 unsigned char *data_ptr)
45 {
46 char retries=0;
47 int ret = 0;
48 unsigned short retry_in_mlsec = Get_I2C_Retry();
49
50 tryReadAgain:
51 ret = 0;
52 ret = ST_Sensors_I2C_ReadRegister( slave_addr, reg_addr, len, data_ptr);
53
54 if (ret && retry_in_mlsec) {
55 if ( retries++ > 4 )
56 return ret;
57
58 Delay(retry_in_mlsec);
59 goto tryReadAgain;
60 }
61 return ret;
62 }
封裝后的函數主要是增加了錯誤重試機制,若讀寫出現錯誤,則會進行多次嘗試,多次嘗試均失敗后會返回錯誤代碼。這個函數作為I2C驅動對外的接口,其它使用I2C的傳感器調用這個函數進行讀寫寄存器。
MPU6050的寄存器定義
MPU6050有各種各樣的寄存器用於控制工作模式,我們把這些寄存器的地址、寄存器位使用宏定義到了mpu6050.h文件中了,見代碼清單 444。
代碼清單 444MPU6050的寄存器定義(mpu6050.h)
1 // MPU6050, Standard address 0x68
2 #define MPU6050_ADDRESS 0x68
3 #define MPU6050_WHO_AM_I 0x75
4 #define MPU6050_SMPLRT_DIV 0 //8000Hz
5 #define MPU6050_DLPF_CFG 0
6 #define MPU6050_GYRO_OUT 0x43 //MPU6050陀螺儀數據寄存器地址
7 #define MPU6050_ACC_OUT 0x3B //MPU6050加速度數據寄存器地址
8
9 #define MPU6050_ADDRESS_AD0_LOW 0x68 //AD0為低電平時的地址
10 #define MPU6050_ADDRESS_AD0_HIGH 0x69 //AD0為高電平時的地址
11 #define MPU6050_DEFAULT_ADDRESS MPU6050_ADDRESS_AD0_LOW
12
13 #define MPU6050_RA_XG_OFFS_TC 0x00 //[7] PWR_MODE, [6:1] XG_OFFS_TC, [0] OTP_BNK_VLD
14 #define MPU6050_RA_YG_OFFS_TC 0x01 //[7] PWR_MODE, [6:1] YG_OFFS_TC, [0] OTP_BNK_VLD
15 #define MPU6050_RA_ZG_OFFS_TC 0x02 //[7] PWR_MODE, [6:1] ZG_OFFS_TC, [0] OTP_BNK_VLD
16 #define MPU6050_RA_X_FINE_GAIN 0x03 //[7:0] X_FINE_GAIN
17 /*.........以下部分省略*/
初始化MPU6050
根據MPU6050的寄存器功能定義,我們使用I2C往寄存器寫入特定的控制參數,見代碼清單 445。
代碼清單 445 初始化MPU6050
1
2 /**
3 * @brief 寫數據到MPU6050寄存器
4 * @param reg_add:寄存器地址
5 * @param reg_data:要寫入的數據
6 * @retval
7 */
8 void MPU6050_WriteReg(u8 reg_add,u8 reg_dat)
9 {
10 Sensors_I2C_WriteRegister(MPU6050_ADDRESS,reg_add,1,®_dat);
11 }
12
13 /**
14 * @brief 從MPU6050寄存器讀取數據
15 * @param reg_add:寄存器地址
16 * @param Read:存儲數據的緩沖區
17 * @param num:要讀取的數據量
18 * @retval
19 */
20 void MPU6050_ReadData(u8 reg_add,unsigned char* Read,u8 num)
21 {
22 Sensors_I2C_ReadRegister(MPU6050_ADDRESS,reg_add,num,Read);
23 }
24
25
26 /**
27 * @brief 初始化MPU6050芯片
28 * @param
29 * @retval
30 */
31 void MPU6050_Init(void)
32 {
33 int i=0,j=0;
34 //在初始化之前要延時一段時間,若沒有延時,則斷電后再上電數據可能會出錯
35 for (i=0; i<1000; i++) {
36 for (j=0; j<1000; j++) {
37 ;
38 }
39 }
40 //解除休眠狀態
41 MPU6050_WriteReg(MPU6050_RA_PWR_MGMT_1, 0x00);
42 //陀螺儀采樣率
43 MPU6050_WriteReg(MPU6050_RA_SMPLRT_DIV , 0x07);
44 MPU6050_WriteReg(MPU6050_RA_CONFIG , 0x06);
45 //配置加速度傳感器工作在16G模式
46 MPU6050_WriteReg(MPU6050_RA_ACCEL_CONFIG , 0x01);
47 //陀螺儀自檢及測量范圍,典型值:0x18(不自檢,2000deg/s)
48 MPU6050_WriteReg(MPU6050_RA_GYRO_CONFIG, 0x18);
49 }
這段代碼首先使用MPU6050_ReadData及MPU6050_WriteRed函數封裝了I2C的底層讀寫驅動,接下來用它們在MPU6050_Init函數中向MPU6050寄存器寫入控制參數,設置了MPU6050的采樣率、量程(分辨率)。
讀傳感器ID
初始化后,可通過讀取它的"WHO AM I"寄存器內容來檢測硬件是否正常,該寄存器存儲了ID號0x68,見代碼清單 446。
代碼清單 446 讀取傳感器ID
1
2 /**
3 * @brief 讀取MPU6050的ID
4 * @param
5 * @retval 正常返回1,異常返回0
6 */
7 uint8_t MPU6050ReadID(void)
8 {
9 unsigned char Re = 0;
10 MPU6050_ReadData(MPU6050_RA_WHO_AM_I,&Re,1); //讀器件地址
11 if (Re != 0x68) {
12 MPU_ERROR("檢測不到MPU6050模塊,請檢查模塊與開發板的接線");
13 return 0;
14 } else {
15 MPU_INFO("MPU6050 ID = %d\r\n",Re);
16 return 1;
17 }
18
19 }
讀取原始數據
若傳感器檢測正常,就可以讀取它數據寄存器獲取采樣數據了,見代碼清單 447。
代碼清單 447 讀取傳感器數據
1
2 /**
3 * @brief 讀取MPU6050的加速度數據
4 * @param
5 * @retval
6 */
7 void MPU6050ReadAcc(short *accData)
8 {
9 u8 buf[6];
10 MPU6050_ReadData(MPU6050_ACC_OUT, buf, 6);
11 accData[0] = (buf[0] << 8) | buf[1];
12 accData[1] = (buf[2] << 8) | buf[3];
13 accData[2] = (buf[4] << 8) | buf[5];
14 }
15
16 /**
17 * @brief 讀取MPU6050的角加速度數據
18 * @param
19 * @retval
20 */
21 void MPU6050ReadGyro(short *gyroData)
22 {
23 u8 buf[6];
24 MPU6050_ReadData(MPU6050_GYRO_OUT,buf,6);
25 gyroData[0] = (buf[0] << 8) | buf[1];
26 gyroData[1] = (buf[2] << 8) | buf[3];
27 gyroData[2] = (buf[4] << 8) | buf[5];
28 }
29
30 /**
31 * @brief 讀取MPU6050的原始溫度數據
32 * @param
33 * @retval
34 */
35 void MPU6050ReadTemp(short *tempData)
36 {
37 u8 buf[2];
38 MPU6050_ReadData(MPU6050_RA_TEMP_OUT_H,buf,2); //讀取溫度值
39 *tempData = (buf[0] << 8) | buf[1];
40 }
41
42 /**
43 * @brief 讀取MPU6050的溫度數據,轉化成攝氏度
44 * @param
45 * @retval
46 */
47 void MPU6050_ReturnTemp(float*Temperature)
48 {
49 short temp3;
50 u8 buf[2];
51
52 MPU6050_ReadData(MPU6050_RA_TEMP_OUT_H,buf,2); //讀取溫度值
53 temp3= (buf[0] << 8) | buf[1];
54 *Temperature=((double) (temp3 /340.0))+36.53;
55 }
其中前以上三個函數分別用於讀取三軸加速度、角速度及溫度值,這些都是原始的ADC數值(16位長),對於加速度和角速度,把讀取得的ADC值除以分辨率,即可求得實際物理量數值。最后一個函數MPU6050_ReturnTemp展示了溫度ADC值與實際溫度值間的轉換,它是根據MPU6050的說明給出的轉換公式進行換算的,注意陀螺儀檢測的溫度會受自身芯片發熱的影響,嚴格來說它測量的是自身芯片的溫度,所以用它來測量氣溫是不太准確的。對於加速度和角速度值我們沒有進行轉換,在下一小節中我們直接利用這些數據交給DMP單元,求解出姿態角。
main函數
最后我們來看看本實驗的main函數,見代碼清單 448。
代碼清單 448 main 函數
1
2 /*簡單任務管理*/
3 uint32_t Task_Delay[NumOfTask]= {0};
4
5 /**
6 * @brief 主函數
7 * @param 無
8 * @retval 無
9 */
10 int main(void)
11 {
12 short Acel[3];
13 short Gyro[3];
14 float Temp;
15
16 SysTick_Init();
17 LED_GPIO_Config();
18 /*初始化USART1*/
19 Debug_USART_Config();
20
21 //初始化 I2C
22 I2cMaster_Init();
23 printf("\r\n歡迎使用秉火 STM32 F429 開發板。\r\n");
24 printf("\r\n這是一個I2C外設(AT24C02)讀寫測試例程 \r\n");
25 //MPU6050初始化
26 MPU6050_Init();
27
28 //檢測MPU6050
29 if (MPU6050ReadID() == 1) {
30 while (1) {
31 if (Task_Delay[0]==TASK_ENABLE) {
32 LED2_TOGGLE;
33 Task_Delay[0]=1000;
34 }
35 if (Task_Delay[1]==0) {
36 MPU6050ReadAcc(Acel);
37 printf("加速度:%8d%8d%8d",Acel[0],Acel[1],Acel[2]);
38 MPU6050ReadGyro(Gyro);
39 printf(" 陀螺儀%8d%8d%8d",Gyro[0],Gyro[1],Gyro[2]);
40 MPU6050_ReturnTemp(&Temp);
41 printf(" 溫度%8.2f\r\n",Temp);
42 Task_Delay[1]=500; //更新一次數據,可根據自己的需求,提高采樣頻率,如100ms采樣一次
43
44 }
45 //*************** 下面是增加任務的格式***********//
46 // if(Task_Delay[i]==0)
47 // {
48 // Task(i);
49 // Task_Delay[i]=;
50 // }
51 }
52 } else {
53 printf("\r\n沒有檢測到MPU6050傳感器!\r\n");
54 LED_RED;
55 while (1);
56 }
57 }
本實驗中控制MPU6050並沒有使用中斷檢測,我們是利用Systick定時器進行計時,隔一段時間讀取MPU6050的數據寄存器獲取采樣數據的,代碼中使用Task_Delay變量來控制定時時間,在Systick中斷里會每隔1ms對該變量值減1,所以當它的值為0時表示定時時間到。
在main函數里,調用I2cMaster_Init、MPU6050_Init及MPU6050ReadID函數后,就在whlie循環里判斷定時時間,定時時間到后就讀取加速度、角速度及溫度值,並使用串口打印信息到電腦端。
44.5.3 下載驗證
用USB線連接開發板"USB TO UART"接口跟電腦,在電腦端打開串口調試助手,把編譯好的程序下載到開發板。在串口調試助手可看到MPU6050采樣得到的調試信息。
44.6 MPU6050—利用DMP進行姿態解算
上一小節我們僅利用MPU6050采集了原始的數據,如果您對姿態解算的算法深有研究,可以自行編寫姿態解算的算法,並利用這些數據,使用STM32進行姿態解算,解算后輸出姿態角。而由於MPU6050內部集成了DMP,不需要STM32參與解算,可直接輸出姿態角,也不需要對解算算法作深入研究,非常方便,本章講解如何使用DMP進行解算。
實驗中使用的代碼主體是從MPU6050官方提供的驅動《motion_driver_6.12》移植過來的,該資料包里提供了基於STM32F4控制器的源代碼及使用python語言編寫的上位機,資料中還附帶了說明文檔,請您充分利用官方自帶的資料學習。
44.6.1 硬件設計
硬件設計與上一小節實驗中的完全一樣,且軟件中使用了INT引腳產生的中斷信號。
44.6.2 軟件設計
本小節講解的是"MPU6050_python上位機"實驗,請打開配套的代碼工程閱讀理解。本工程是從官方代碼移植過來的(IAR工程移植至MDK),改動並不多,我們主要給讀者講解一下該驅動的設計思路,方便應用。由於本工程的代碼十分龐大,在講解到某些函數時,請善用MDK的搜索功能,從而在工程中查找出對應的代碼。
1. 程序設計要點
(1) 提供I2C讀寫接口、定時服務及INT中斷處理;
(2) 從陀螺儀中獲取原始數據並處理;
(3) 更新數據並輸出。
2. 代碼分析
官方的驅動主要是了MPL軟件庫(Motion Processing Library),要移植該軟件庫我們需要為它提供I2C讀寫接口、定時服務以及MPU6050的數據更新標志。若需要輸出調試信息到上位機,還需要提供串口接口。
I2C讀寫接口
MPL庫的內部對I2C讀寫時都使用i2c_write及i2c_read函數,在文件"inv_mpu.c"中給出了它們的接口格式,見代碼清單 441。
代碼清單 449 I2C讀寫接口(inv_mpu.c文件)
1 /* The following functions must be defined for this platform:
2 * i2c_write(unsigned char slave_addr, unsigned char reg_addr,
3 * unsigned char length, unsigned char const *data)
4 * i2c_read(unsigned char slave_addr, unsigned char reg_addr,
5 * unsigned char length, unsigned char *data)
6 */
7
8 #define i2c_write Sensors_I2C_WriteRegister
9 #define i2c_read Sensors_I2C_ReadRegister
這些接口的格式與我們上一小節寫的I2C讀寫函數Sensors_I2C_ReadRegister及Sensors_I2C_WriteRegister一致,所以可直接使用宏替換。
提供定時服務
MPL軟件庫中使用到了延時及時間戳功能,要求需要提供delay_ms函數實現毫秒級延時,提供get_ms獲取毫秒級的時間戳,它們的接口格式也在"inv_mpu.c"文件中給出,見代碼清單 442。
代碼清單 4410 定時服務接口(inv_mpu.c文件)
1 /*
2 * delay_ms(unsigned long num_ms)
3 * get_ms(unsigned long *count)
4 */
5
6 #define delay_ms Delay_ms
7 #define get_ms get_tick_count
我們為接口提供的Delay_ms及get_tick_count函數定義在bsp_SysTick.c文件,我們使用SysTick每毫秒產生一次中斷,進行計時,見代碼清單 4411。
代碼清單 4411 使用Systick進行定時(bsp_SysTick.c)
1
2 static __IO u32 TimingDelay;
3 static __IO uint32_t g_ul_ms_ticks=0;
4 /**
5 * @brief us延時程序,1ms為一個單位
6 * @param
7 * @arg nTime: Delay_ms( 1 ) 則實現的延時為 1 ms
8 * @retval 無
9 */
10 void Delay_ms(__IO u32 nTime)
11 {
12 TimingDelay = nTime;
13
14 while (TimingDelay != 0);
15 }
16
17 /**
18 * @brief 獲取節拍程序
19 * @param 無
20 * @retval 無
21 * @attention 在 SysTick 中斷函數 SysTick_Handler()調用
22 */
23 void TimingDelay_Decrement(void)
24 {
25 if (TimingDelay != 0x00) {
26 TimingDelay--;
27 }
28 }
29
30 /**
31 * @brief 獲取當前毫秒值
32 * @param 存儲最新毫秒值的變量
33 * @retval 無
34 */
35 int get_tick_count(unsigned long *count)
36 {
37 count[0] = g_ul_ms_ticks;
38 return 0;
39 }
40
41 /**
42 * @brief 毫秒累加器,在中斷里每毫秒加1
43 * @param 無
44 * @retval 無
45 */
46 void TimeStamp_Increment (void)
47 {
48 g_ul_ms_ticks++;
49 }
上述代碼中的TimingDelay_Decrement和TimeStamp_Increment函數是在Systick的中斷服務函數中被調用的,見代碼清單 4412。systick被配置為每毫秒產生一次中斷,而每次中斷中會對TimingDelay變量減1,對g_ul_ms_ticks變量加1。它們分別用於Delay_ms函數利用TimingDelay的值進行阻塞延遲,而get_tick_count函數獲取的時間戳即g_ul_ms_ticks的值。
代碼清單 4412 Systick的中斷服務函數
1 /**
2 * @brief This function handles SysTick Handler.
3 * @param None
4 * @retval None
5 */
6 void SysTick_Handler(void)
7 {
8 TimingDelay_Decrement();
9 TimeStamp_Increment();
10 }
提供串口調試接口
MPL代碼庫的調試信息輸出函數都集中到了log_stm32.c文件中,我們可以為這些函數提供串口輸出接口,以便把這些信息輸出到上位機,見代碼清單 443。
代碼清單 4413 串口調試接口(log_stm32.c文件)
1 /*串口輸出接口*/
2 int fputcc(int ch)
3 {
4 /* 發送一個字節數據到USART1 */
5 USART_SendData(DEBUG_USART, (uint8_t) ch);
6
7 /* 等待發送完畢 */
8 while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET);
9
10 return (ch);
11 }
12
13 /*輸出四元數數據*/
14 void eMPL_send_quat(long *quat)
15 {
16 char out[PACKET_LENGTH];
17 int i;
18 if (!quat)
19 return;
20 memset(out, 0, PACKET_LENGTH);
21 out[0] = '$';
22 out[1] = PACKET_QUAT;
23 out[3] = (char)(quat[0] >> 24);
24 out[4] = (char)(quat[0] >> 16);
25 out[5] = (char)(quat[0] >> 8);
26 out[6] = (char)quat[0];
27 out[7] = (char)(quat[1] >> 24);
28 out[8] = (char)(quat[1] >> 16);
29 out[9] = (char)(quat[1] >> 8);
30 out[10] = (char)quat[1];
31 out[11] = (char)(quat[2] >> 24);
32 out[12] = (char)(quat[2] >> 16);
33 out[13] = (char)(quat[2] >> 8);
34 out[14] = (char)quat[2];
35 out[15] = (char)(quat[3] >> 24);
36 out[16] = (char)(quat[3] >> 16);
37 out[17] = (char)(quat[3] >> 8);
38 out[18] = (char)quat[3];
39 out[21] = '\r';
40 out[22] = '\n';
41
42 for (i=0; i<PACKET_LENGTH; i++) {
43 fputcc(out[i]);
44 }
45 }
上述代碼中的fputcc函數是我們自己編寫的串口輸出接口,它與我們重定向printf函數定義的fputc函數很功能類似。下面的eMPL_send_quat函數是MPL庫中的原函數,它用於打印"四元數信息",在這個log_stm32.c文件中還有輸出日志信息的_MLPrintLog函數,輸出原始信息到專用上位機的eMPL_send_data函數,它們都調用了fputcc進行輸出。
MPU6050的中斷接口
與我們上一小節中的基礎實驗不同,為了高效處理采樣數據,MPL代碼庫使用了MPU6050的INT中斷信號,為此我們要給提供中斷接口,見代碼清單 444。
代碼清單 4414中斷接口(stm32f4xx_it.c文件)
1
2 #define MPU_IRQHandler EXTI9_5_IRQHandler
3
4 void MPU_IRQHandler(void)
5 {
6
7 if (EXTI_GetITStatus(MPU_INT_EXTI_LINE) != RESET) { //確保是否產生了EXTI Line中斷
8 /* Handle new gyro*/
9 gyro_data_ready_cb();
10
11 EXTI_ClearITPendingBit(MPU_INT_EXTI_LINE); //清除中斷標志位
12 }
13 }
在工程中我們把MPU6050與STM32相連的引腳配置成了中斷模式,上述代碼是該引腳的中斷服務函數,在中斷里調用了MPL代碼庫的gyro_data_ready_cb函數,它設置了標志變量hal.new_gyro,以通知MPL庫有新的數據,其函數定義見代碼清單 4415。
代碼清單 4415 設置標志變量(main.c文件)
1 /* Every time new gyro data is available, this function is called in an
2 * ISR context. In this example, it sets a flag protecting the FIFO read
3 * function.
4 */
5 void gyro_data_ready_cb(void)
6 {
7 hal.new_gyro = 1;
8 }
main函數執行流程
了解MPL移植需要提供的接口后,我們直接看main函數了解如何利用MPL庫獲取姿態數據,見代碼清單 445。
代碼清單 4416 使用MPL進行姿態解算的過程
1 /**
2 * @brief main entry point.
3 * @par Parameters None
4 * @retval void None
5 * @par Required preconditions: None
6 */
7
8 int main(void)
9 {
10 inv_error_t result;
11 unsigned char accel_fsr, new_temp = 0;
12 unsigned short gyro_rate, gyro_fsr;
13 unsigned long timestamp;
14 struct int_param_s int_param;
15
16 SysTick_Init();
17 LED_GPIO_Config();
18 /*初始化USART1*/
19 Debug_USART_Config();
20 EXTI_MPU_Config();
21 // Configure I2C
22 I2cMaster_Init();
23
24 result = mpu_init(&int_param);
25 if (result) {
26 MPL_LOGE("Could not initialize gyro.\n");
27 LED_RED;
28 } else {
29 LED_GREEN;
30 }
31
32 result = inv_init_mpl();
33 if (result) {
34 MPL_LOGE("Could not initialize MPL.\n");
35 }
36
37 /* Compute 6-axis and 9-axis quaternions. */
38 inv_enable_quaternion();
39 inv_enable_9x_sensor_fusion();
40
41 /* Update gyro biases when not in motion.
42 * WARNING: These algorithms are mutually exclusive.
43 */
44 inv_enable_fast_nomot();
45 /* inv_enable_motion_no_motion(); */
46 /* inv_set_no_motion_time(1000); */
47
48 /* Update gyro biases when temperature changes. */
49 inv_enable_gyro_tc();
50
51 /* Allows use of the MPL APIs in read_from_mpl. */
52 inv_enable_eMPL_outputs();
53
54 result = inv_start_mpl();
55 if (result == INV_ERROR_NOT_AUTHORIZED) {
56 while (1) {
57 MPL_LOGE("Not authorized.\n");
58 }
59 }
60 if (result) {
61 MPL_LOGE("Could not start the MPL.\n");
62 }
63
64 /* Get/set hardware configuration. Start gyro. */
65 /* Wake up all sensors. */
66
67 mpu_set_sensors(INV_XYZ_GYRO | INV_XYZ_ACCEL);
68 /* Push both gyro and accel data into the FIFO. */
69 mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL);
70 mpu_set_sample_rate(DEFAULT_MPU_HZ);
71
72 /* Read back configuration in case it was set improperly. */
73 mpu_get_sample_rate(&gyro_rate);
74 mpu_get_gyro_fsr(&gyro_fsr);
75 mpu_get_accel_fsr(&accel_fsr);
76
77 /* Sync driver configuration with MPL. */
78 /* Sample rate expected in microseconds. */
79 inv_set_gyro_sample_rate(1000000L / gyro_rate);
80 inv_set_accel_sample_rate(1000000L / gyro_rate);
81
82 /* Set chip-to-body orientation matrix.
83 * Set hardware units to dps/g's/degrees scaling factor.
84 */
85 inv_set_gyro_orientation_and_scale(
86 inv_orientation_matrix_to_scalar(gyro_pdata.orientation),
87 (long)gyro_fsr<<15);
88 inv_set_accel_orientation_and_scale(
89 inv_orientation_matrix_to_scalar(gyro_pdata.orientation),
90 (long)accel_fsr<<15);
91
92 /* Initialize HAL state variables. */
93
94 hal.sensors = ACCEL_ON | GYRO_ON;
95 hal.dmp_on = 0;
96 hal.report = 0;
97 hal.rx.cmd = 0;
98 hal.next_pedo_ms = 0;
99 hal.next_compass_ms = 0;
100 hal.next_temp_ms = 0;
101
102 /* Compass reads are handled by scheduler. */
103 get_tick_count(×tamp);
104
105 /* To initialize the DMP:
106 * 1. Call dmp_load_motion_driver_firmware(). This pushes the DMP image in
107 * inv_mpu_dmp_motion_driver.h into the MPU memory.
108 * 2. Push the gyro and accel orientation matrix to the DMP.
109 * 3. Register gesture callbacks. Don't worry, these callbacks won't be
110 * executed unless the corresponding feature is enabled.
111 * 4. Call dmp_enable_feature(mask) to enable different features.
112 * 5. Call dmp_set_fifo_rate(freq) to select a DMP output rate.
113 * 6. Call any feature-specific control functions.
114 *
115 * To enable the DMP, just call mpu_set_dmp_state(1). This function can
116 * be called repeatedly to enable and disable the DMP at runtime.
117 *
118 * The following is a short summary of the features supported in the DMP
119 * image provided in inv_mpu_dmp_motion_driver.c:
120 * DMP_FEATURE_LP_QUAT: Generate a gyro-only quaternion on the DMP at
121 * 200Hz. Integrating the gyro data at higher rates reduces numerical
122 * errors (compared to integration on the MCU at a lower sampling rate).
123 * DMP_FEATURE_6X_LP_QUAT: Generate a gyro/accel quaternion on the DMP at
124 * 200Hz. Cannot be used in combination with DMP_FEATURE_LP_QUAT.
125 * DMP_FEATURE_TAP: Detect taps along the X, Y, and Z axes.
126 * DMP_FEATURE_ANDROID_ORIENT: Google's screen rotation algorithm. Triggers
127 * an event at the four orientations where the screen should rotate.
128 * DMP_FEATURE_GYRO_CAL: Calibrates the gyro data after eight seconds of
129 * no motion.
130 * DMP_FEATURE_SEND_RAW_ACCEL: Add raw accelerometer data to the FIFO.
131 * DMP_FEATURE_SEND_RAW_GYRO: Add raw gyro data to the FIFO.
132 * DMP_FEATURE_SEND_CAL_GYRO: Add calibrated gyro data to the FIFO. Cannot
133 * be used in combination with DMP_FEATURE_SEND_RAW_GYRO.
134 */
135 dmp_load_motion_driver_firmware();
136 dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_pdata.orientation));
137 dmp_register_tap_cb(tap_cb);
138 dmp_register_android_orient_cb(android_orient_cb);
139 /*
140 * Known Bug -
141 * DMP when enabled will sample sensor data at 200Hz and output to FIFO at the rate
142 * specified in the dmp_set_fifo_rate API. The DMP will then sent an interrupt once
143 * a sample has been put into the FIFO. Therefore if the dmp_set_fifo_rate is at 25Hz
144 * there will be a 25Hz interrupt from the MPU device.
145 *
146 * There is a known issue in which if you do not enable DMP_FEATURE_TAP
147 * then the interrupts will be at 200Hz even if fifo rate
148 * is set at a different rate. To avoid this issue include the DMP_FEATURE_TAP
149 *
150 * DMP sensor fusion works only with gyro at +-2000dps and accel +-2G
151 */
152 hal.dmp_features = DMP_FEATURE_6X_LP_QUAT | DMP_FEATURE_TAP |
153 DMP_FEATURE_ANDROID_ORIENT | DMP_FEATURE_SEND_RAW_ACCEL|
154 DMP_FEATURE_SEND_CAL_GYRO | DMP_FEATURE_GYRO_CAL;
155 dmp_enable_feature(hal.dmp_features);
156 dmp_set_fifo_rate(DEFAULT_MPU_HZ);
157 mpu_set_dmp_state(1);
158 hal.dmp_on = 1;
159
160 while (1) {
161
162 unsigned long sensor_timestamp;
163 int new_data = 0;
164 if (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_RXNE)) {
165 /* A byte has been received via USART. See handle_input for a list of
166 * valid commands.
167 */
168 USART_ClearFlag(DEBUG_USART, USART_FLAG_RXNE);
169 handle_input();
170 }
171 get_tick_count(×tamp);
172
173 /* Temperature data doesn't need to be read with every gyro sample.
174 * Let's make them timer-based like the compass reads.
175 */
176 if (timestamp > hal.next_temp_ms) {
177 hal.next_temp_ms = timestamp + TEMP_READ_MS;
178 new_temp = 1;
179 }
180
181 if (hal.new_gyro && hal.dmp_on) {
182 short gyro[3], accel_short[3], sensors;
183 unsigned char more;
184 long accel[3], quat[4], temperature;
185 /* This function gets new data from the FIFO when the DMP is in
186 * use. The FIFO can contain any combination of gyro, accel,
187 * quaternion, and gesture data. The sensors parameter tells the
188 * caller which data fields were actually populated with new data.
189 * For example, if sensors == (INV_XYZ_GYRO | INV_WXYZ_QUAT), then
190 * the FIFO isn't being filled with accel data.
191 * The driver parses the gesture data to determine if a gesture
192 * event has occurred; on an event, the application will be notified
193 * via a callback (assuming that a callback function was properly
194 * registered). The more parameter is non-zero if there are
195 * leftover packets in the FIFO.
196 */
197 dmp_read_fifo(gyro, accel_short, quat, &sensor_timestamp, &sensors, &more);
198 if (!more)
199 hal.new_gyro = 0;
200 if (sensors & INV_XYZ_GYRO) {
201 /* Push the new data to the MPL. */
202 inv_build_gyro(gyro, sensor_timestamp);
203 new_data = 1;
204 if (new_temp) {
205 new_temp = 0;
206 /* Temperature only used for gyro temp comp. */
207 mpu_get_temperature(&temperature, &sensor_timestamp);
208 inv_build_temp(temperature, sensor_timestamp);
209 }
210 }
211 if (sensors & INV_XYZ_ACCEL) {
212 accel[0] = (long)accel_short[0];
213 accel[1] = (long)accel_short[1];
214 accel[2] = (long)accel_short[2];
215 inv_build_accel(accel, 0, sensor_timestamp);
216 new_data = 1;
217 }
218 if (sensors & INV_WXYZ_QUAT) {
219 inv_build_quat(quat, 0, sensor_timestamp);
220 new_data = 1;
221 }
222 }
223
224 if (new_data) {
225 inv_execute_on_data();
226 /* This function reads bias-compensated sensor data and sensor
227 * fusion outputs from the MPL. The outputs are formatted as seen
228 * in eMPL_outputs.c. This function only needs to be called at the
229 * rate requested by the host.
230 */
231 read_from_mpl();
232 }
233 }
234 }
如您所見,main函數非常長,而且我們只是摘抄了部分,在原工程代碼中還有很多代碼,例如加入磁場數據使用9軸數據進行解算的功能(這是MPU9150的功能,MPU6050不支持)以及其它工作模式相關的控制示例。上述main函數的主要執行流程概括如下:
(1) 初始化STM32的硬件,如Systick、LED、調試串口、INT中斷引腳以及I2C外設的初始化;
(2) 調用MPL庫函數mpu_init初始化傳感器的基本工作模式(以下過程調用的大部分都是MPL庫函數,不再強調);
(3) 調用inv_init_mpl函數初始化MPL軟件庫,初始化后才能正常進行解算;
(4) 設置各種運算參數,如四元數運算(inv_enable_quaternion)、6軸或9軸數據融合(inv_enable_9x_sensor_fusion)等等;
(5) 設置傳感器的工作模式(mpu_set_sensors)、采樣率(mpu_set_sample_rate)、分辨率(inv_set_gyro_orientation_and_scale)等等;
(6) 當STM32驅動、MPL庫、傳感器工作模式、DMP工作模式等所有初始化工作都完成后進行while循環;
(7) 在while循環中檢測串口的輸入,若串口有輸入,則調用handle_input根據串口輸入的字符(命令),切換工作方式。這部分主要是為了支持上位機通過輸入命令,根據進行不同的處理,如開、關加速度信息的采集或調試信息的輸出等;
(8) 在while循環中檢測是否有數據更新(if (hal.new_gyro && hal.dmp_on)),當有數據更新的時候產生INT中斷,會使hal.new_gyro置1的,從而執行if里的條件代碼;
(9) 使用dmp_read_fifo把數據讀取到FIFO,這個FIFO是指MPL軟件庫定義的一個緩沖區,用來緩沖最新采集得的數據;
(10) 調用inv_build_gyro、inv_build_temp、inv_build_accel及inv_build_quat函數處理數據角速度、溫度、加速度及四元數數據,並對標志變量new_data置1;
(11) 在while循環中檢測new_data標志位,當有新的數據時執行if里的條件代碼;
(12) 調用inv_execute_on_data函數更新所有數據及狀態;
(13) 調用read_from_mpl函數向輸出最新的數據。
數據輸出接口
在上面main中最后調用的read_from_mpl函數演示了如何調用MPL數據輸出接口,通過這些接口我們可以獲得想要的數據,其函數定義見代碼清單 4417。
代碼清單 4417 MPL的數據輸出接口(main.c)
1 /* Get data from MPL.
2 * TODO: Add return values to the inv_get_sensor_type_xxx APIs to differentiate
3 * between new and stale data.
4 */
5 static void read_from_mpl(void)
6 {
7 long msg, data[9];
8 int8_t accuracy;
9 unsigned long timestamp;
10 float float_data[3] = {0};
11
12 if (inv_get_sensor_type_quat(data, &accuracy, (inv_time_t*)×tamp)) {
13 /* Sends a quaternion packet to the PC. Since this is used by the Python
14 * test app to visually represent a 3D quaternion, it's sent each time
15 * the MPL has new data.
16 */
17 eMPL_send_quat(data);
18
19 /* Specific data packets can be sent or suppressed using USB commands. */
20 if (hal.report & PRINT_QUAT)
21 eMPL_send_data(PACKET_DATA_QUAT, data);
22 }
23
24 if (hal.report & PRINT_ACCEL) {
25 if (inv_get_sensor_type_accel(data, &accuracy,
26 (inv_time_t*)×tamp))
27 eMPL_send_data(PACKET_DATA_ACCEL, data);
28 }
29 if (hal.report & PRINT_GYRO) {
30 if (inv_get_sensor_type_gyro(data, &accuracy,
31 (inv_time_t*)×tamp))
32 eMPL_send_data(PACKET_DATA_GYRO, data);
33 }
34
35 if (hal.report & PRINT_EULER) {
36 if (inv_get_sensor_type_euler(data, &accuracy,
37 (inv_time_t*)×tamp))
38 eMPL_send_data(PACKET_DATA_EULER, data);
39 }
40
41
42 /********************使用液晶屏顯示數據**************************/
43 if (1) {
44 char cStr [ 70 ];
45 unsigned long timestamp,step_count,walk_time;
46
47
48 /*獲取歐拉角*/
49 if (inv_get_sensor_type_euler(data, &accuracy,(inv_time_t*)×tamp)) {
50
51 #ifdef USE_LCD_DISPLAY
52 //inv_get_sensor_type_euler讀出的數據是Q16格式,所以左移16位.
53 sprintf ( cStr, "Pitch : %.4f ", data[0]*1.0/(1<<16) );
54 LCD_DisplayStringLine(LINE(7),(uint8_t* )cStr);
55
56 //inv_get_sensor_type_euler讀出的數據是Q16格式,所以左移16位.
57 sprintf ( cStr, "Roll : %.4f ", data[1]*1.0/(1<<16) );
58 LCD_DisplayStringLine(LINE(8),(uint8_t* )cStr);
59 //inv_get_sensor_type_euler讀出的數據是Q16格式,所以左移16位.
60 sprintf ( cStr, "Yaw : %.4f ", data[2]*1.0/(1<<16) );
61 LCD_DisplayStringLine(LINE(9),(uint8_t* )cStr);
62
63 /*溫度*/
64 mpu_get_temperature(data,(inv_time_t*)×tamp);
65 //inv_get_sensor_type_euler讀出的數據是Q16格式,所以左移16位.
66 sprintf ( cStr, "Temperature : %.2f ", data[0]*1.0/(1<<16) );
67 LCD_DisplayStringLine(LINE(10),(uint8_t* )cStr);
68 #endif
69
70 }
71
72 /*獲取步數*/
73 get_tick_count(×tamp);
74 if (timestamp > hal.next_pedo_ms) {
75
76 hal.next_pedo_ms = timestamp + PEDO_READ_MS;
77 dmp_get_pedometer_step_count(&step_count);
78 dmp_get_pedometer_walk_time(&walk_time);
79
80 #ifdef USE_LCD_DISPLAY
81 sprintf(cStr, "Walked steps : %ld steps over %ld milliseconds..",step_count,walk_time);
82 LCD_DisplayStringLine(LINE(11),(uint8_t* )cStr);
83 #endif
84 }
85 }
86 /*以下省略*/
87 }
88
上述代碼展示了使用inv_get_sensor_type_quat、inv_get_sensor_type_accel、inv_get_sensor_type_gyro、inv_get_sensor_type_euler及dmp_get_pedometer_step_count函數分別獲取四元數、加速度、角速度、歐拉角及計步器數據。
代碼中的eMPL_send_data函數是使用串口按照PYTHON上位機格式進行提交數據,上位機根據這些數據對三維模型作相應的旋轉。
另外我們自己在代碼中加入了液晶顯示的代碼(#ifdef USE_LCD_DISPLAY宏內的代碼),它把這些數據輸出到實驗板上的液晶屏上。
您可根據自己的數據使用需求,參考這個read_from_mpl函數對數據輸出接口的調用方式,編寫自己的應用。
44.6.3 下載驗證
直接下載本程序到開發板,在液晶屏上會觀察到姿態角、溫度、計步器數據,改變開發板的姿態,數據會更新(計步器數據要模擬走路才會更新),若直接連接串口調試助手,會接收到一系列的亂碼信息,這是正常的,這些數據需要使用官方的Python上位機解碼。
本實驗適用於官方提供的Python上位機,它可以把采樣的數據傳送到上位機,上位機會顯示三維模式的姿態。
注意:以下內容僅針對有Python編程語言基礎的用戶,若您不會Python,而又希望觀察到三維模型的姿態,請參考下一小節的實驗,它的使用更為簡單。
1. Python上位機源代碼及說明
MPU6050官方提供的上位機的使用說明可在配套資料《motion_driver6.12》源碼包documentation文件夾里的《Motion Driver 6.12 – Getting Started Guide》找到。上位機的源碼在《motion_driver6.12》源碼包的eMPL-pythonclient文件夾,里邊有三個python文件,見圖 4413。
圖 4413 源碼包里的python上位機源碼
2. 安裝Python環境
要利用上面的源碼,需要先安裝Python環境,該上位機支持python2.7環境(僅支持32位),並且需要安裝Pyserial庫、Pygame庫。
可通過如下網址找到安裝包。
Python: https://www.python.org/downloads/
Pyserial: https://pypi.python.org/pypi/pyserial
Pygame: http://www.pygame.org/download.shtml
3. Python上位機的使用步驟
先把本STM32工程代碼編譯后下載到開發板上運行,確認開發板的USB TO USART接口已與電腦相連,正常時開發板的液晶屏現象跟上一章例程的現象一樣。
使用命令行切換到python上位機的目錄,執行如下命令:
python eMPL-client.py <COM PORT NUMBER>
其中<COM PORT NUMBER>參數是STM32開發板在電腦端的串口設備號,運行命令后會彈出一個3D圖形窗口,顯示陀螺儀的姿態,見圖 4414。(圖中的"python2_32"是本機的python2.7-32位 python命令的名字,用戶默認用"python"命令即可。)
圖 4414 運行python上位機
這個上位機還可以接收命令來控制STM32進行數據輸出,選中圖中的pygame window窗口(彈出來的3D圖形窗口),然后按下鍵盤的字母"a "鍵,命令行窗口就會輸出加速度信息,按下"g"鍵,就會輸出陀螺儀信息。命令集說明如下:
'8' : Toggles Accel Sensor
'9' : Toggles Gyro Sensor
'0' : Toggles Compass Sensor
'a' : Prints Accel Data
'g' : Prints Gyro Data
'c' : Prints Compass Data
'e' : Prints Eular Data in radius
'r' : Prints Rotational Matrix Data
'q' : Prints Quaternions
'h' : Prints Heading Data in degrees
'i' : Prints Linear Acceleration data
'o' : Prints Gravity Vector data
'w' : Get compass accuracy and status
'd' : Register Dump
'p' : Turn on Low Power Accel Mode at 20Hz sampling
'l' : Load calibration data from flash memory
's' : Save calibration data to flash memory
't' : run factory self test and calibration routine
'1' : Change sensor output data rate to 10Hz
'2' : Change sensor output data rate to 20Hz
'3' : Change sensor output data rate to 40Hz
'4' : Change sensor output data rate to 50Hz
'5' : Change sensor output data rate to 100Hz
',' : set interrupts to DMP gestures only
'.' : set interrupts to DMP data ready
'6' : Print Pedometer data
'7' : Reset Pedometer data
'f' : Toggle DMP on/off
'm' : Enter Low Power Interrupt Mode
'x' : Reset the MSP430
'v' : Toggle DMP Low Power Quaternion Generation
44.7 MPU6050—使用第三方上位機
上一小節中的實驗必須配合使用官方提供的上位機才能看到三維模型,而且功能比較簡單,所以在小節中我們演示如何把數據輸出到第三方的上位機,直觀地觀察設備的姿態。
實驗中我們使用的是"匿名飛控地面站0512"版本的上位機,關於上位機的通訊協議可查閱《飛控通信協議》文檔,或到他們的官方網站了解。
44.7.1 硬件設計
硬件設計與上一小節實驗中的完全一樣。
44.7.2 軟件設計
本小節講解的是"MPU6050_DMP測試例程"實驗,請打開配套的代碼工程閱讀理解。本小節的內容主體跟上一小節一樣,區別主要是當獲取得到數據后,本實驗根據"匿名飛控"上位機的數據格式要求上傳數據。
1. 程序設計要點
(1) 了解上位機的通訊協議;
(2) 根據協議格式上傳數據到上位機;
2. 代碼分析
通訊協議
要按照上位機的格式上傳數據,首先要了解它的通訊協議,本實驗中的上位機協議說明見表 444。
表 444 匿名上位機的通訊協議(部分)
幀 |
幀頭 |
功能字 |
長度 |
數據 |
校驗 |
STATUS |
AAAA |
01 |
LEN |
int16 ROL*100 |
SUM |
SENSER |
AAAA |
02 |
LEN |
int16 ACC_X |
SUM |
表中說明了兩種數據幀,分別是STATUS幀及SENSER幀,數據幀中包含幀頭、功能字、長度、主體數據及校驗和。"幀頭"用於表示數據包的開始,均使用兩個字節的0xAA表示;"功能字"用於區分數據幀的類型,0x01表示STATUS幀,0x02表示SENSER幀;"長度"表示后面主體數據內容的字節數;"校驗和"用於校驗,它是前面所有內容的和。
其中的STATUS幀用於向上位機傳輸橫滾角、俯仰角及偏航角的值(100倍),SENSER幀用於傳輸加速度、角速度及磁場強度的原始數據。
發送數據包
根據以上數據格式的要求,我們定義了兩個函數,分別用於發送STATUS幀及SENSER幀,見代碼清單 442。
代碼清單 4418 發送數據包(main.c文件)
1
2 #define BYTE0(dwTemp) (*(char *)(&dwTemp))
3 #define BYTE1(dwTemp) (*((char *)(&dwTemp) + 1))
4 #define BYTE2(dwTemp) (*((char *)(&dwTemp) + 2))
5 #define BYTE3(dwTemp) (*((char *)(&dwTemp) + 3))
6
7 /**
8 * @brief 控制串口發送1個字符
9 * @param c:要發送的字符
10 * @retval none
11 */
12 void usart_send_char(uint8_t c)
13 {
14 while (USART_GetFlagStatus(DEBUG_USART,USART_FLAG_TXE)==RESET); //循環發送,直到發送完畢
15 USART_SendData(DEBUG_USART,c);
16 }
17
18
19
20 /*函數功能:根據匿名最新上位機協議寫的顯示姿態的程序(上位機0512版本)
21 *具體協議說明請查看上位機軟件的幫助說明。
22 */
23 void Data_Send_Status(float Pitch,float Roll,float Yaw)
24 {
25 unsigned char i=0;
26 unsigned char _cnt=0,sum = 0;
27 unsigned int _temp;
28 u8 data_to_send[50];
29
30 data_to_send[_cnt++]=0xAA;
31 data_to_send[_cnt++]=0xAA;
32 data_to_send[_cnt++]=0x01;
33 data_to_send[_cnt++]=0;
34
35 _temp = (int)(Roll*100);
36 data_to_send[_cnt++]=BYTE1(_temp);
37 data_to_send[_cnt++]=BYTE0(_temp);
38 _temp = 0-(int)(Pitch*100);
39 data_to_send[_cnt++]=BYTE1(_temp);
40 data_to_send[_cnt++]=BYTE0(_temp);
41 _temp = (int)(Yaw*100);
42 data_to_send[_cnt++]=BYTE1(_temp);
43 data_to_send[_cnt++]=BYTE0(_temp);
44 _temp = 0;
45 data_to_send[_cnt++]=BYTE3(_temp);
46 data_to_send[_cnt++]=BYTE2(_temp);
47 data_to_send[_cnt++]=BYTE1(_temp);
48 data_to_send[_cnt++]=BYTE0(_temp);
49
50 data_to_send[_cnt++]=0xA0;
51
52 data_to_send[3] = _cnt-4;
53 //和校驗
54 for (i=0; i<_cnt; i++)
55 sum+= data_to_send[i];
56 data_to_send[_cnt++]=sum;
57
58 //串口發送數據
59 for (i=0; i<_cnt; i++)
60 usart_send_char(data_to_send[i]);
61 }
62
63 /*函數功能:根據匿名最新上位機協議寫的顯示傳感器數據(上位機0512版本)
64 *具體協議說明請查看上位機軟件的幫助說明。
65 */
66 void Send_Data(int16_t *Gyro,int16_t *Accel)
67 {
68 unsigned char i=0;
69 unsigned char _cnt=0,sum = 0;
70 // unsigned int _temp;
71 u8 data_to_send[50];
72
73 data_to_send[_cnt++]=0xAA;
74 data_to_send[_cnt++]=0xAA;
75 data_to_send[_cnt++]=0x02;
76 data_to_send[_cnt++]=0;
77
78
79 data_to_send[_cnt++]=BYTE1(Accel[0]);
80 data_to_send[_cnt++]=BYTE0(Accel[0]);
81 data_to_send[_cnt++]=BYTE1(Accel[1]);
82 data_to_send[_cnt++]=BYTE0(Accel[1]);
83 data_to_send[_cnt++]=BYTE1(Accel[2]);
84 data_to_send[_cnt++]=BYTE0(Accel[2]);
85
86 data_to_send[_cnt++]=BYTE1(Gyro[0]);
87 data_to_send[_cnt++]=BYTE0(Gyro[0]);
88 data_to_send[_cnt++]=BYTE1(Gyro[1]);
89 data_to_send[_cnt++]=BYTE0(Gyro[1]);
90 data_to_send[_cnt++]=BYTE1(Gyro[2]);
91 data_to_send[_cnt++]=BYTE0(Gyro[2]);
92 data_to_send[_cnt++]=0;
93 data_to_send[_cnt++]=0;
94 data_to_send[_cnt++]=0;
95 data_to_send[_cnt++]=0;
96 data_to_send[_cnt++]=0;
97 data_to_send[_cnt++]=0;
98
99 data_to_send[3] = _cnt-4;
100 //和校驗
101 for (i=0; i<_cnt; i++)
102 sum+= data_to_send[i];
103 data_to_send[_cnt++]=sum;
104
105 //串口發送數據
106 for (i=0; i<_cnt; i++)
107 usart_send_char(data_to_send[i]);
108 }
函數比較簡單,就是根據輸入的內容,一字節一字節地按格式封裝好,然后調用串口發送到上位機。
發送數據
與上一小節一樣,我們使用read_from_mpl函數輸出數據,由於使用了不同的上位機,所以我們修改了它的具體內容,見代碼清單 443。
代碼清單 4419 read_from_mpl 函數(main.c文件)
1
2 extern struct inv_sensor_cal_t sensors;
3
4 /* Get data from MPL.
5 * TODO: Add return values to the inv_get_sensor_type_xxx APIs to differentiate
6 * between new and stale data.
7 */
8 static void read_from_mpl(void)
9 {
10 float Pitch,Roll,Yaw;
11 int8_t accuracy;
12 unsigned long timestamp;
13 long data[3];
14 /*獲取歐拉角*/
15 inv_get_sensor_type_euler(data, &accuracy,(inv_time_t*)×tamp);
16
17 //inv_get_sensor_type_euler讀出的數據是Q16格式,所以左移16位.
18 Pitch =data[0]*1.0/(1<<16) ;
19 Roll = data[1]*1.0/(1<<16);
20 Yaw = data[2]*1.0/(1<<16);
21
22 /*向匿名上位機發送姿態*/
23 Data_Send_Status(Pitch,Roll,Yaw);
24 /*向匿名上位機發送原始數據*/
25 Send_Data((int16_t *)&sensors.gyro.raw,(int16_t *)&sensors.accel.raw);
26 }
代碼中調用inv_get_sensor_type_euler獲取歐拉角,然后調用Data_Send_Status格式上傳到上位機,而加速度及角速度的原始數據直接從sensors結構體變量即可獲取,獲取后調用Send_Data發送出去。
44.7.3 下載驗證
直接下載本程序到開發板,在液晶屏上會觀察到姿態角、溫度、計步器數據,改變開發板的姿態,數據會更新(計步器數據要模擬走路才會更新),若直接連接串口調試助手,會接收到一系列的亂碼信息,這是正常的,這些數據需要使用"匿名飛控地面站"上位機解碼。
若通過液晶屏的信息了解到MPU6050模塊已正常工作,則可進一步在電腦上使用"ANO_TC匿名飛控地面站-0512.exe"(以下簡稱"匿名上位機")軟件查看可視化數據。
實驗步驟如下:
(1) 確認開發板的USB TO USART接口已與電腦相連,確認電腦端能查看到該串口設備。
(2) 打開配套資料里的"匿名上位機"軟件,在軟件界面打開開發板對應的串口(波特率為115200),把"基本收碼"、"高級收碼"、"飛控波形"功能設置為on狀態。點擊上方圖中的基本收發、波形顯示、飛控狀態圖標,會彈出窗口。具體見下文軟件配置圖。
(3) 在軟件的"基本收發"、"波形顯示"、"飛控狀態"頁面可看到滾動數據、隨着模塊晃動而變化的波形以及模塊姿態的3D可視化圖形。
44.8 每課一問
13. 把"MPU6050—獲取原始數據"實驗中的加速度及角速度數據轉換成物理量輸出到串口,並用它檢測您當地的重力加速度值。