一、前言
1.1 項目開發背景
在現代電子技術的快速發展中,各種智能設備的精確度和便捷性不斷提升,電子水平儀作為一種常見的測量工具,廣泛應用于建筑、家居安裝、機械制造等領域。傳統的水平儀通常依賴機械原理,如氣泡管或機械刻度盤,雖然簡單直觀,但在一些精度要求較高的場合,傳統工具往往難以滿足需求。因此,電子水平儀應運而生,憑借其數字化顯示和高精度測量,逐漸取代了傳統水平儀,成為更加智能化的測量工具。
本項目基于STM32F103C8T6單片機設計一款電子水平儀。STM32F103C8T6作為一種性能強大的微控制器,具有較高的計算能力和豐富的外設接口,能夠滿足項目對實時數據處理和顯示控制的需求。該單片機將與MPU6050傳感器相結合,利用其內置的加速度計和陀螺儀,實現設備的傾斜角度測量。MPU6050傳感器能夠高精度地檢測設備在三個軸向上的加速度信息,從而計算出設備在左右方向(X軸)和前后方向(Y軸)的傾斜角度。
為了更直觀地展示測量結果,本項目選用了一款高分辨率的OLED顯示屏。該顯示屏可以清晰地呈現實時的角度數據,并且提供了足夠的分辨率來實現可視化的圖形展示。通過中心點圓圈和滾動小球的設計,用戶可以輕松識別設備當前的水平狀態。這種可視化的反饋方式不僅更加直觀,而且提升了用戶體驗,特別適用于需要頻繁調整水平狀態的場景。
隨著智能化技術的普及,越來越多的傳統工具開始向數字化、智能化方向發展。電子水平儀作為數字工具的一種,其精度、穩定性和用戶友好性成為了設計的核心目標。項目通過結合MPU6050傳感器的高精度檢測與STM32F103C8T6單片機的強大處理能力,提供了一種新型的、可視化的電子水平儀設計方案。這種方案不僅滿足了基礎的角度測量功能,還通過創新的顯示方式,使得用戶能夠更加高效和精準地進行水平調整和測量。
核心的部件:
1.2 設計實現的功能
基于STM32設計一款電子水平儀
功能要求:
1. 本地一個SPI協議接口的OLED顯示屏,展示當前 設備左右方向的傾斜角度,設備前后方向的傾斜角度。
2. 能夠通過一個中心點圓圈+一個滾動的小球展示可視化展示當前的水平狀態。
3. 支持按鍵校準,校準水平儀的參數
硬件要求:
1. 單片機采用STM32F103C8T6
2. 水平狀態監測采用MPU6050
3. 顯示屏采用高分辨率的OLED顯示屏
4. 供電采用可充電鋰電池供電,方便便攜式攜帶
5. 一個按鈕,進行復位參數校準。
(1) 傾斜角度測量
通過 MPU6050 傳感器,實時測量設備在 X 軸(左右方向)和 Y 軸(前后方向)的傾斜角度。系統能夠精準地計算設備相對于水平的角度,并將其顯示在 OLED 屏幕上,提供清晰的數值反饋,幫助用戶了解設備當前的水平狀態。
(2) 實時水平狀態可視化顯示
在 OLED 顯示屏上,采用圖形化方式展示設備的水平狀態。通過中心圓圈和滾動的小球,用戶可以直觀地看到設備的水平偏差。小球會根據設備的傾斜方向和角度實時滾動,偏離水平時會向對應方向移動,水平時則停留在圓圈中心,增強用戶體驗。
(3) 數據濾波與角度計算
在數據處理方面,設計了合適的濾波算法,如卡爾曼濾波或簡單平均濾波,用于去除傳感器數據中的噪聲和提高計算的準確性。通過這些算法,系統能夠更加穩定和精準地輸出設備的傾斜角度。
(4) 校準功能
系統提供校準功能,用戶可以通過按下按鈕來重置設備的零點,校準設備的水平狀態。當按下按鈕時,設備的當前角度被重置為零,確保在每次使用時都能從準確的水平狀態開始。此功能特別適用于設備多次使用后的精度校正,保證每次測量的精度和可靠性。
(5) OLED 顯示角度數值
在 OLED 屏幕上,實時顯示設備的具體傾斜角度,分別顯示 X 軸(左右方向)和 Y 軸(前后方向)的傾斜角度數值。通過直觀的數字顯示,用戶可以準確了解當前設備的水平狀態,并在需要時進行調整。
(6) 便攜性與充電功能
該電子水平儀設計為便攜式設備,配備了可充電鋰電池,用戶可以在不依賴外部電源的情況下,長時間使用設備。通過電池管理模塊,支持 USB 充電接口,方便充電并延長使用時間。電池的可充電特性使得設備在戶外、工地等環境下更加便捷。
(7) 低功耗設計
為了提高設備的使用時長,設計中采用了低功耗模式。STM32F103C8T6 單片機與 MPU6050 傳感器均支持低功耗操作,系統在不進行操作時可以進入低功耗待機狀態,減少能耗,延長電池使用時間。
(8) 用戶友好界面
通過簡潔直觀的 OLED 顯示屏界面,用戶可以輕松獲取設備的實時傾斜角度和水平狀態。顯示內容清晰、易于閱讀,設計了視覺化的反饋方式,使得設備使用者能夠快速理解設備的水平偏差并進行相應調整。
1.3 項目硬件模塊組成
(1) STM32F103C8T6 單片機
STM32F103C8T6 是本項目的核心控制單元,負責數據處理、控制顯示屏和執行用戶輸入的操作。該單片機具備多種外設接口,如 I2C 和 GPIO,能夠與 MPU6050 傳感器和 OLED 顯示屏進行通信。此外,它具有較強的運算能力,能夠進行角度計算、校準功能以及實時顯示更新。
(2) MPU6050 傳感器
MPU6050 是集成了 3 軸加速度計和 3 軸陀螺儀的傳感器,用于檢測設備在 X 軸(左右方向)和 Y 軸(前后方向)的加速度,進而計算出設備的傾斜角度。通過 I2C 協議將數據傳輸給 STM32F103C8T6 單片機,用于后續角度計算和顯示。
(3) OLED 顯示屏
OLED 顯示屏用于顯示設備的傾斜角度和水平狀態。高分辨率的 OLED 顯示屏能清晰地呈現數值角度和可視化圖形(如中心圓圈和滾動小球)。通過 I2C 連接 STM32F103C8T6 單片機,實時顯示 X、Y 軸的傾斜角度以及水平狀態的圖形反饋。
(4) 可充電鋰電池
可充電鋰電池提供電子水平儀系統所需的電力,支持便攜式使用。它通常提供 3.7V 的電壓,滿足 STM32F103C8T6、MPU6050 傳感器和 OLED 顯示屏的工作需求。電池可以通過充電電路進行充電,以便用戶在外出時使用。
(5) 電池充電管理模塊
為了方便充電,配備電池充電管理模塊(TP4056)用于給可充電鋰電池充電。該模塊能夠通過 Micro-USB 或其他充電接口連接電源,并在充電時提供電池保護,防止過充或過放。
(6) 按鍵模塊
一個按鍵用于用戶與電子水平儀的交互,具體功能包括校準或復位。按鍵可以觸發 STM32F103C8T6 的GPIO引腳,在長按時啟動水平儀的參數校準功能,重新設置設備的零點,確保在不同使用環境下仍能獲得準確的測量結果。
1.4 設計思路
本項目的設計思路主要是通過結合硬件和軟件的緊密配合,開發一款精準且便于攜帶的電子水平儀。該電子水平儀通過 STM32F103C8T6 單片機作為核心處理單元,利用 MPU6050 傳感器獲取設備的加速度數據,實時計算設備的傾斜角度,并通過高分辨率的 OLED 顯示屏直觀地呈現出來。設計的關鍵在于如何準確捕捉設備的水平狀態并通過用戶友好的方式展示出來,同時確保系統的便攜性和易操作性。
系統的核心是 STM32F103C8T6 單片機,作為高性能的微控制器,它具有多種外設接口和足夠的運算能力,能夠進行實時數據處理。通過 I2C 協議,單片機與 MPU6050 傳感器進行通信,獲取加速度計和陀螺儀數據。這些數據將用于計算設備的傾斜角度,并進一步進行校準和調整,確保在任何情況下都能提供精準的水平測量。
在傳感器數據處理方面,設計的關鍵是如何精確地計算設備的角度。MPU6050 傳感器提供了 X、Y 和 Z 軸的加速度數據,我們將使用這些數據計算設備相對于水平面(X軸和Y軸的傾斜角度)的變化。為了減少噪聲和提高測量精度,設計時引入了數據濾波算法,以保證角度計算的穩定性和準確性。
OLED 顯示屏作為用戶與設備交互的界面,提供了豐富的顯示功能。設計中通過實時顯示當前設備的左右方向(X軸)和前后方向(Y軸)的傾斜角度,用戶可以清晰地了解設備的水平狀態。此外,為了增加可視化的體驗,我們設計了一個中心點圓圈,并通過小球的滾動來展示設備的傾斜程度。當設備完全水平時,小球將停留在中心圓圈內,偏離水平時,小球會向對應的方向移動。這樣直觀的顯示方式讓用戶能在瞬間理解設備的水平情況。
為了確保設備能夠適應不同的環境和使用場景,本設計還加入了校準功能。通過設置一個按鍵,用戶可以在需要時對設備進行參數復位和重新校準。當按下按鈕時,STM32 單片機會清除當前的角度偏差,并重置為零點,確保在多次使用過程中系統依然保持高精度。
便攜性是本項目的另一個重要設計方向。采用可充電鋰電池為電源,配備電池管理模塊,以支持系統在無外部電源的情況下長時間工作。電池的充電功能使得用戶可以在野外或移動環境中隨時使用,并且通過 USB 充電接口方便充電,解決了傳統水平儀無法持續工作的問題。
通過這些硬件和軟件的結合,整個電子水平儀系統實現了精確、直觀和便捷的功能。系統設計不僅具備高精度的水平測量功能,還通過簡潔的用戶界面和便攜設計使得用戶能夠在各種環境下使用,提供了一個全新的測量工具,取代了傳統的機械水平儀。
1.5 系統功能總結
功能編號 | 功能描述 | 實現方式 |
---|---|---|
(1) | 傾斜角度測量 | 通過 MPU6050 傳感器測量 X 軸和 Y 軸的傾斜角度,并進行計算顯示。 |
(2) | 實時水平狀態可視化顯示 | 在 OLED 屏幕上展示中心點圓圈及滾動小球,直觀展示設備的水平狀態。 |
(3) | 數據濾波與角度計算 | 使用卡爾曼濾波或簡單平均濾波去除噪聲,計算設備的精確傾斜角度。 |
(4) | 校準功能 | 通過按鍵觸發校準,重置設備角度為零,確保每次使用時從準確零點開始。 |
(5) | OLED 顯示角度數值 | 在 OLED 屏幕上實時顯示設備的 X 軸和 Y 軸傾斜角度的數值。 |
(6) | 便攜性與充電功能 | 配備可充電鋰電池,支持 USB 充電,適合便攜式使用,解決外部電源依賴問題。 |
(7) | 低功耗設計 | 系統支持低功耗模式,確保設備在待機時減少能耗,延長使用時間。 |
(8) | 用戶友好界面 | OLED 顯示屏提供清晰直觀的角度數值和圖形化的反饋,簡化操作和交互。 |
1.6 開發工具的選擇
開發工具選擇Keil,keil是一家世界領先的嵌入式微控制器軟件開發商,在2015年,keil被ARM公司收購。因為當前芯片選擇的是STM32F103系列,STMF103是屬于ARM公司的芯片構架、Cortex-M3內核系列的芯片,所以使用Kile來開發STM32是有先天優勢的,而keil在各大高校使用的也非常多,很多教科書里都是以keil來教學,開發51單片機、STM32單片機等等。
1.7 MPU6050模塊介紹
MPU6050 是一款由 Invensense 公司生產的六軸傳感器模塊,它結合了三軸加速度計和三軸陀螺儀,廣泛應用于運動追蹤、姿態檢測、電子穩定、虛擬現實等領域。MPU6050 采用 MEMS(微機電系統)技術,能夠提供高精度的角度和加速度數據,適用于需要實時動態感知的應用。
該傳感器模塊內置了一個三軸加速度計和一個三軸陀螺儀。三軸加速度計能夠測量物體在 X、Y 和 Z 軸上的加速度變化,單位為“g”(地球重力加速度),可以感知物體的靜態或動態加速度變化。三軸陀螺儀則用于測量物體在三個軸上的角速度,單位通常為“度/秒”(°/s),可提供物體旋轉的速率信息。通過這些數據,MPU6050 可以檢測物體的運動、旋轉以及傾斜角度,并為其他系統提供準確的傳感信息。
MPU6050 的最大特點之一是它內置了一個數字運動處理單元(DMP,Digital Motion Processor),它能夠對加速度計和陀螺儀的數據進行實時處理,從而減輕主處理器的計算負擔。DMP 還可以進行傳感器數據的濾波、姿態估算和融合處理,這使得 MPU6050 在傳感器輸出的穩定性和準確性上具有顯著優勢。此外,DMP 支持直接輸出已經處理和融合的運動數據,減少了主控單元的數據處理量,從而提升了整體系統的性能。
MPU6050 模塊支持 I2C 通信協議,具有兩線串行總線接口,便于與主控單元(如 STM32F103C8T6、Arduino 等)進行數據傳輸。其通信速率最高可達 400 kHz,因此可以在需要高頻率數據采集的應用中提供實時數據反饋。通過該接口,開發者能夠讀取加速度計和陀螺儀的數據,還能夠配置模塊的工作模式、靈敏度等參數,以滿足特定應用需求。
在實際應用中,MPU6050 具有較高的靈敏度和較低的功耗,適合用于移動設備、機器人、無人機、車載系統等對體積、功耗有要求的場景。其內置的低通濾波器和數字處理單元,使得它能夠有效地抑制噪聲,提高測量數據的穩定性,尤其在動態環境下,能夠準確地感知物體的運動狀態和姿態變化。
此外,MPU6050 還具有一些自檢功能,可以通過內部自檢機制判斷傳感器是否正常工作。模塊中還有內置溫度傳感器,用于補償溫度對加速度計和陀螺儀的影響,確保測量精度不會受到環境溫度變化的影響。
MPU6050 以其小巧的體積、高精度的傳感能力、內置數字運動處理單元(DMP)以及低功耗特點,成為許多運動監測和姿態檢測系統中不可或缺的核心傳感器。它不僅能夠提供高質量的數據,還能減少對主控制單元的計算壓力,使得整體系統更加高效、穩定。
二、代碼設計
模塊代碼可以去網盤下載學習:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink
2.1 工作原理
在電子水平儀中,MPU6050 傳感器的主要作用是通過測量加速度和角速度來計算設備的傾斜角度。它集成了三軸加速度計和三軸陀螺儀,這兩者協同工作,實現對設備在各個方向上的姿態和運動的監測。
MPU6050 在水平儀中的工作原理如下:
MPU6050 內部的三軸加速度計可以測量設備在三個方向(X、Y、Z軸)上的加速度。加速度計的基本工作原理是根據物體受到的力(如重力)產生的加速度來推算設備的運動狀態。在一個理想的水平狀態下,重力僅作用于Z軸(即垂直方向)。因此,設備的傾斜會導致加速度的分布發生變化,從而改變X軸和Y軸的加速度值。
當設備傾斜時,重力的作用方向將不再完全指向 Z 軸,而是分布到 X 和 Y 軸上,產生一定的加速度。這些加速度值可以用來計算設備的傾斜角度。通過對 X 和 Y 軸加速度值的計算,可以推算出設備的俯仰角度(pitch)和滾轉角度(roll)。例如,通過以下公式,可以計算出設備的俯仰角(pitch)和滾轉角(roll):
- 俯仰角(Pitch) :
[ text{pitch} = arctanleft(frac{accelX}{sqrt{accelY^2 + accelZ^2}}right) ] - 滾轉角(Roll) :
[ text{roll} = arctanleft(frac{-accelY}{sqrt{accelX^2 + accelZ^2}}right) ]
通過這些計算,可以獲取設備的實時傾斜角度。
陀螺儀工作原理
MPU6050 內部的三軸陀螺儀可以測量設備的旋轉速率,也就是角速度。陀螺儀通過檢測設備在各個軸上的旋轉速率(單位:度/秒或 rad/s)來捕捉設備的動態變化。與加速度計不同,陀螺儀主要檢測旋轉的角速度,能反映設備在實時運動中的變化。
在水平儀中,陀螺儀的工作原理通常用于跟蹤設備的快速變化。例如,當設備發生快速旋轉或傾斜時,陀螺儀的角速度數據可以幫助補充加速度計測量的數據,增強系統對動態變化的響應能力。特別是在靜態情況下,加速度計能夠提供較為穩定的角度測量,而在快速運動或旋轉時,陀螺儀可以提供更快的反應數據。
數據融合與姿態計算
雖然加速度計和陀螺儀都能提供關于設備傾斜的不同信息,但它們各自存在局限性。加速度計受到重力和運動加速度的影響,可能會產生一定的噪聲;而陀螺儀在長時間使用中可能會受到漂移的影響。因此,在水平儀中,通常會采用 傳感器融合算法 ,如互補濾波或 卡爾曼濾波 ,來結合加速度計和陀螺儀的數據,從而提高系統的精度和穩定性。
- 互補濾波 : 這種方法結合了加速度計和陀螺儀的優勢,通過加速度計提供的穩定低頻信息和陀螺儀提供的高頻旋轉信息,實現一個平衡的結果。
- 卡爾曼濾波 :卡爾曼濾波是一種更復雜的算法,它能夠基于信號的不確定性,計算出加速度計和陀螺儀數據的最優融合,提供更為精確和穩定的角度計算。
在水平儀中,結合加速度計和陀螺儀的數據后,可以獲得非常精確的設備俯仰和滾轉角度,從而在 OLED 屏幕上顯示出設備的水平狀態,直觀地向用戶反饋設備是否處于水平狀態。
工作流程總結
- 加速度計 :用于檢測設備在 X、Y 和 Z 軸上的加速度,并通過計算設備的俯仰角和滾轉角來推算其在水平面上的傾斜角度。
- 陀螺儀 :用于檢測設備在各個軸上的旋轉速率,幫助系統在快速運動和變化時實時更新角度信息。
- 數據融合 :結合加速度計和陀螺儀的輸出數據,通過傳感器融合算法(如互補濾波或卡爾曼濾波),獲取更為準確的設備角度數據。
- 角度計算 :根據加速度計和陀螺儀數據計算出設備的俯仰角和滾轉角,從而實時監控和顯示設備的傾斜狀態,向用戶反饋設備是否處于水平狀態。
2.2 MPU6050的驅動代碼
#include "mpu6050.h"
#include "sys.h"
#include "delay.h"
#include < stdio.h >
/*--------------------------------------------------------------------IIC協議底層模擬時序--------------------------------------------------------------------------------*/
/*
函數功能:MPU IIC 延時函數
*/
void MPU6050_IIC_Delay(void)
{
DelayUs(2);
}
/*
函數功能: 初始化IIC
*/
void MPU6050_IIC_Init(void)
{
RCC- >APB2ENR|=1< 3; //先使能外設IO PORTB時鐘
GPIOB- >CRL&=0X00FFFFFF; //PB6/7 推挽輸出
GPIOB- >CRL|=0X33000000;
GPIOB- >ODR|=3< 6; //PB6,7 輸出高
}
/*
函數功能: 產生IIC起始信號
*/
void MPU6050_IIC_Start(void)
{
MPU6050_SDA_OUT(); //sda線輸出
MPU6050_IIC_SDA=1;
MPU6050_IIC_SCL=1;
MPU6050_IIC_Delay();
MPU6050_IIC_SDA=0;//START:when CLK is high,DATA change form high to low
MPU6050_IIC_Delay();
MPU6050_IIC_SCL=0;//鉗住I2C總線,準備發送或接收數據
}
/*
函數功能: 產生IIC停止信號
*/
void MPU6050_IIC_Stop(void)
{
MPU6050_SDA_OUT();//sda線輸出
MPU6050_IIC_SCL=0;
MPU6050_IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
MPU6050_IIC_Delay();
MPU6050_IIC_SCL=1;
MPU6050_IIC_SDA=1;//發送I2C總線結束信號
MPU6050_IIC_Delay();
}
/*
函數功能: 等待應答信號到來
返 回 值:1,接收應答失敗
0,接收應答成功
*/
u8 MPU6050_IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
MPU6050_SDA_IN(); //SDA設置為輸入
MPU6050_IIC_SDA=1;MPU6050_IIC_Delay();
MPU6050_IIC_SCL=1;MPU6050_IIC_Delay();
while(MPU6050_READ_SDA)
{
ucErrTime++;
if(ucErrTime >250)
{
MPU6050_IIC_Stop();
return 1;
}
}
MPU6050_IIC_SCL=0;//時鐘輸出0
return 0;
}
/*
函數功能:產生ACK應答
*/
void MPU6050_IIC_Ack(void)
{
MPU6050_IIC_SCL=0;
MPU6050_SDA_OUT();
MPU6050_IIC_SDA=0;
MPU6050_IIC_Delay();
MPU6050_IIC_SCL=1;
MPU6050_IIC_Delay();
MPU6050_IIC_SCL=0;
}
/*
函數功能:不產生ACK應答
*/
void MPU6050_IIC_NAck(void)
{
MPU6050_IIC_SCL=0;
MPU6050_SDA_OUT();
MPU6050_IIC_SDA=1;
MPU6050_IIC_Delay();
MPU6050_IIC_SCL=1;
MPU6050_IIC_Delay();
MPU6050_IIC_SCL=0;
}
/*
函數功能:IIC發送一個字節
返回從機有無應答
1,有應答
0,無應答
*/
void MPU6050_IIC_Send_Byte(u8 txd)
{
u8 t;
MPU6050_SDA_OUT();
MPU6050_IIC_SCL=0;//拉低時鐘開始數據傳輸
for(t=0;t< 8;t++)
{
MPU6050_IIC_SDA=(txd&0x80) >>7;
txd< <=1;
MPU6050_IIC_SCL=1;
MPU6050_IIC_Delay();
MPU6050_IIC_SCL=0;
MPU6050_IIC_Delay();
}
}
/*
函數功能:讀1個字節,ack=1時,發送ACK,ack=0,發送nACK
*/
u8 MPU6050_IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
MPU6050_SDA_IN();//SDA設置為輸入
for(i=0;i< 8;i++ )
{
MPU6050_IIC_SCL=0;
MPU6050_IIC_Delay();
MPU6050_IIC_SCL=1;
receive< <=1;
if(MPU6050_READ_SDA)receive++;
MPU6050_IIC_Delay();
}
if(!ack)
MPU6050_IIC_NAck();//發送nACK
else
MPU6050_IIC_Ack(); //發送ACK
return receive;
}
/*--------------------------------------------------------------------MPU6050底層驅動代碼--------------------------------------------------------------------------------*/
/*
函數功能:初始化MPU6050
返 回 值:0,成功
其他,錯誤代碼
*/
u8 MPU6050_Init(void)
{
u8 res;
MPU6050_IIC_Init();//初始化IIC總線
MPU6050_Write_Byte(MPU_PWR_MGMT1_REG,0X80); //復位MPU6050
DelayMs(100);
MPU6050_Write_Byte(MPU_PWR_MGMT1_REG,0X00); //喚醒MPU6050
MPU6050_Set_Gyro_Fsr(3); //陀螺儀傳感器,±2000dps
MPU6050_Set_Accel_Fsr(0); //加速度傳感器,±2g
MPU6050_Set_Rate(50); //設置采樣率50Hz
MPU6050_Write_Byte(MPU_INT_EN_REG,0X00); //關閉所有中斷
MPU6050_Write_Byte(MPU_USER_CTRL_REG,0X00); //I2C主模式關閉
MPU6050_Write_Byte(MPU_FIFO_EN_REG,0X00); //關閉FIFO
MPU6050_Write_Byte(MPU_INTBP_CFG_REG,0X80); //INT引腳低電平有效
res=MPU6050_Read_Byte(MPU_DEVICE_ID_REG);
if(res==MPU6050_ADDR)//器件ID正確
{
MPU6050_Write_Byte(MPU_PWR_MGMT1_REG,0X01); //設置CLKSEL,PLL X軸為參考
MPU6050_Write_Byte(MPU_PWR_MGMT2_REG,0X00); //加速度與陀螺儀都工作
MPU6050_Set_Rate(50); //設置采樣率為50Hz
}else return 1;
return 0;
}
/*
設置MPU6050陀螺儀傳感器滿量程范圍
fsr:0,±250dps;1,±500dps;2,±1000dps;3,±2000dps
返回值:0,設置成功
其他,設置失敗
*/
u8 MPU6050_Set_Gyro_Fsr(u8 fsr)
{
return MPU6050_Write_Byte(MPU_GYRO_CFG_REG,fsr< 3);//設置陀螺儀滿量程范圍
}
/*
函數功能:設置MPU6050加速度傳感器滿量程范圍
函數功能:fsr:0,±2g;1,±4g;2,±8g;3,±16g
返 回 值:0,設置成功
其他,設置失敗
*/
u8 MPU6050_Set_Accel_Fsr(u8 fsr)
{
return MPU6050_Write_Byte(MPU_ACCEL_CFG_REG,fsr< 3);//設置加速度傳感器滿量程范圍
}
/*
函數功能:設置MPU6050的數字低通濾波器
函數參數:lpf:數字低通濾波頻率(Hz)
返 回 值:0,設置成功
其他,設置失敗
*/
u8 MPU6050_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 MPU6050_Write_Byte(MPU_CFG_REG,data);//設置數字低通濾波器
}
/*
函數功能:設置MPU6050的采樣率(假定Fs=1KHz)
函數參數:rate:4~1000(Hz)
返 回 值:0,設置成功
其他,設置失敗
*/
u8 MPU6050_Set_Rate(u16 rate)
{
u8 data;
if(rate >1000)rate=1000;
if(rate< 4)rate=4;
data=1000/rate-1;
data=MPU6050_Write_Byte(MPU_SAMPLE_RATE_REG,data); //設置數字低通濾波器
return MPU6050_Set_LPF(rate/2); //自動設置LPF為采樣率的一半
}
/*
函數功能:得到溫度值
返 回 值:返回值:溫度值(擴大了100倍)
*/
short MPU6050_Get_Temperature(void)
{
u8 buf[2];
short raw;
float temp;
MPU6050_Read_Len(MPU6050_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 MPU6050_Get_Gyroscope(short *gx,short *gy,short *gz)
{
u8 buf[6],res;
res=MPU6050_Read_Len(MPU6050_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 MPU6050_Get_Accelerometer(short *ax,short *ay,short *az)
{
u8 buf[6],res;
res=MPU6050_Read_Len(MPU6050_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 MPU6050_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
u8 i;
MPU6050_IIC_Start();
MPU6050_IIC_Send_Byte((addr< 1)|0);//發送器件地址+寫命令
if(MPU6050_IIC_Wait_Ack()) //等待應答
{
MPU6050_IIC_Stop();
printf("等待應答失敗rn");
return 1;
}
MPU6050_IIC_Send_Byte(reg); //寫寄存器地址
MPU6050_IIC_Wait_Ack(); //等待應答
for(i=0;i< len;i++)
{
MPU6050_IIC_Send_Byte(buf[i]); //發送數據
if(MPU6050_IIC_Wait_Ack()) //等待ACK
{
MPU6050_IIC_Stop();
printf("等待ACK失敗rn");
return 1;
}
}
MPU6050_IIC_Stop();
return 0;
}
/*
函數功能:IIC連續寫
函數參數:
IIC連續讀
addr:器件地址
reg:要讀取的寄存器地址
len:要讀取的長度
buf:讀取到的數據存儲區
返 回 值:0,成功,其他,錯誤代碼
*/
u8 MPU6050_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
MPU6050_IIC_Start();
MPU6050_IIC_Send_Byte((addr< 1)|0);//發送器件地址+寫命令
if(MPU6050_IIC_Wait_Ack()) //等待應答
{
MPU6050_IIC_Stop();
return 1;
}
MPU6050_IIC_Send_Byte(reg); //寫寄存器地址
MPU6050_IIC_Wait_Ack(); //等待應答
MPU6050_IIC_Start();
MPU6050_IIC_Send_Byte((addr< 1)|1);//發送器件地址+讀命令
MPU6050_IIC_Wait_Ack(); //等待應答
while(len)
{
if(len==1)*buf=MPU6050_IIC_Read_Byte(0);//讀數據,發送nACK
else *buf=MPU6050_IIC_Read_Byte(1); //讀數據,發送ACK
len--;
buf++;
}
MPU6050_IIC_Stop(); //產生一個停止條件
return 0;
}
/*
函數功能:IIC寫一個字節
函數參數:
reg:寄存器地址
data:數據
返 回 值:0,成功,其他,錯誤代碼
*/
u8 MPU6050_Write_Byte(u8 reg,u8 data)
{
MPU6050_IIC_Start();
MPU6050_IIC_Send_Byte((MPU6050_ADDR< 1)|0);//發送器件地址+寫命令
if(MPU6050_IIC_Wait_Ack()) //等待應答
{
MPU6050_IIC_Stop();
return 1;
}
MPU6050_IIC_Send_Byte(reg); //寫寄存器地址
MPU6050_IIC_Wait_Ack(); //等待應答
MPU6050_IIC_Send_Byte(data);//發送數據
if(MPU6050_IIC_Wait_Ack()) //等待ACK
{
MPU6050_IIC_Stop();
return 1;
}
MPU6050_IIC_Stop();
return 0;
}
/*
函數功能:IIC讀一個字節
函數參數:
reg:寄存器地址
data:數據
返 回 值:返回值:讀到的數據
*/
u8 MPU6050_Read_Byte(u8 reg)
{
u8 res;
MPU6050_IIC_Start();
MPU6050_IIC_Send_Byte((MPU6050_ADDR< 1)|0);//發送器件地址+寫命令
MPU6050_IIC_Wait_Ack(); //等待應答
MPU6050_IIC_Send_Byte(reg); //寫寄存器地址
MPU6050_IIC_Wait_Ack(); //等待應答
MPU6050_IIC_Start();
MPU6050_IIC_Send_Byte((MPU6050_ADDR< 1)|1);//發送器件地址+讀命令
MPU6050_IIC_Wait_Ack(); //等待應答
res=MPU6050_IIC_Read_Byte(0);//讀取數據,發送nACK
MPU6050_IIC_Stop(); //產生一個停止條件
return res;
}
2.3 整體代碼框架
初始化硬件、讀取傳感器數據、計算傾斜角度并通過 OLED 顯示屏顯示結果。
#include "stm32f10x.h"
#include "mpu6050.h"
#include "oled.h"
#include "math.h"
// 常量定義
#define ALPHA 0.98 // 互補濾波的權重系數
#define DEG_TO_RAD(x) ((x) * 3.14159265358979 / 180.0)
// 全局變量定義
float pitch = 0.0f; // 俯仰角
float roll = 0.0f; // 滾轉角
float pitch_prev = 0.0f; // 上一周期俯仰角
float roll_prev = 0.0f; // 上一周期滾轉角
float gyroX = 0.0f, gyroY = 0.0f, gyroZ = 0.0f; // 陀螺儀數據
float accelX = 0.0f, accelY = 0.0f, accelZ = 0.0f; // 加速度計數據
float gyroXrate = 0.0f, gyroYrate = 0.0f, gyroZrate = 0.0f; // 角速度
void delay_ms(uint32_t ms);
void MPU6050_Init(void);
void ReadMPU6050(void);
void UpdateAngles(void);
void Display(void);
int main(void)
{
// 初始化硬件
SystemInit(); // 初始化系統時鐘
MPU6050_Init(); // 初始化MPU6050
OLED_Init(); // 初始化OLED顯示屏
while (1)
{
// 讀取MPU6050傳感器數據
ReadMPU6050();
// 更新角度數據
UpdateAngles();
// 顯示結果到OLED屏幕
Display();
}
}
// 延時函數
void delay_ms(uint32_t ms)
{
uint32_t i;
for(i = 0; i < ms * 1000; i++)
{
__NOP();
}
}
// 初始化MPU6050傳感器
void MPU6050_Init(void)
{
// 假設已經實現了 I2C 初始化和MPU6050的配置
MPU6050_InitI2C(); // I2C初始化
MPU6050_Config(); // 配置MPU6050
}
// 讀取MPU6050傳感器數據
void ReadMPU6050(void)
{
// 從MPU6050讀取加速度和陀螺儀數據
accelX = (float)MPU6050_ReadAccelX(); // 讀取X軸加速度數據
accelY = (float)MPU6050_ReadAccelY(); // 讀取Y軸加速度數據
accelZ = (float)MPU6050_ReadAccelZ(); // 讀取Z軸加速度數據
gyroX = (float)MPU6050_ReadGyroX(); // 讀取X軸陀螺儀數據
gyroY = (float)MPU6050_ReadGyroY(); // 讀取Y軸陀螺儀數據
gyroZ = (float)MPU6050_ReadGyroZ(); // 讀取Z軸陀螺儀數據
// 將陀螺儀的角速度轉換為角度變化
gyroXrate = gyroX * DEG_TO_RAD(1.0f); // 角速度轉化為角度
gyroYrate = gyroY * DEG_TO_RAD(1.0f);
gyroZrate = gyroZ * DEG_TO_RAD(1.0f);
}
// 更新俯仰角和滾轉角
void UpdateAngles(void)
{
// 加速度計計算角度(靜態)
float accel_pitch = atan2f(accelX, sqrtf(accelY * accelY + accelZ * accelZ)) * 180.0f / M_PI;
float accel_roll = atan2f(accelY, sqrtf(accelX * accelX + accelZ * accelZ)) * 180.0f / M_PI;
// 陀螺儀計算角度(動態)
pitch += gyroXrate * 0.01f; // 0.01f為采樣時間間隔
roll += gyroYrate * 0.01f;
// 互補濾波(數據融合)
pitch = ALPHA * (pitch_prev + gyroXrate * 0.01f) + (1 - ALPHA) * accel_pitch;
roll = ALPHA * (roll_prev + gyroYrate * 0.01f) + (1 - ALPHA) * accel_roll;
// 更新上一周期角度
pitch_prev = pitch;
roll_prev = roll;
}
// 顯示數據到OLED屏幕
void Display(void)
{
// 清屏
OLED_Clear();
// 顯示俯仰角(pitch)和滾轉角(roll)
OLED_ShowString(0, 0, "Pitch: ");
OLED_ShowFloat(60, 0, pitch, 2); // 顯示俯仰角,保留2位小數
OLED_ShowString(0, 2, "Roll: ");
OLED_ShowFloat(60, 2, roll, 2); // 顯示滾轉角,保留2位小數
// 顯示中心圓圈與小球可視化(此部分需根據具體顯示庫實現)
OLED_DrawCircle(64, 32, 30); // 圓心 (64, 32),半徑 30
int ball_x = (int)(64 + 30 * sin(DEG_TO_RAD(roll)));
int ball_y = (int)(32 + 30 * sin(DEG_TO_RAD(pitch)));
OLED_DrawCircle(ball_x, ball_y, 3); // 小球,半徑3
}
- 硬件初始化 :
MPU6050_Init()
:初始化 MPU6050 傳感器。OLED_Init()
:初始化 OLED 顯示屏。
- MPU6050 數據讀取 :
ReadMPU6050()
從 MPU6050 讀取加速度計和陀螺儀數據。- 數據讀取后,轉換為設備的角速度和加速度,并存儲為
gyroXrate
、gyroYrate
、gyroZrate
和accelX
、accelY
、accelZ
。
- 數據融合與角度計算 :
UpdateAngles()
使用互補濾波算法融合加速度計和陀螺儀數據,更新設備的俯仰角(pitch)和滾轉角(roll)。- 計算的俯仰角和滾轉角實時更新并用于后續顯示。
- 顯示結果 :
Display()
函數清空 OLED 屏幕并顯示俯仰角和滾轉角。它還顯示了一個可視化效果,其中包括一個圓圈和一個根據角度變化的小球。- 顯示函數
OLED_ShowFloat()
和OLED_DrawCircle()
用于顯示浮動數字和繪制圓形。
三、總結
本項目設計并實現了一款基于 STM32F103C8T6 單片機的電子水平儀,采用 MPU6050 傳感器進行姿態檢測,并通過 OLED 顯示屏實時展示設備的俯仰角和滾轉角。通過應用互補濾波算法,項目有效地融合了加速度計和陀螺儀的數據,提供了高效、實時且精確的角度估算,滿足了設備在動態和靜態環境下的姿態檢測需求。
本系統設計具有高度的可擴展性,可以根據不同的應用場景進一步優化濾波算法、增加功能如按鍵校準、低功耗優化等。該電子水平儀不僅在電子設備的水平測量中具有廣泛應用,還可以作為嵌入式系統設計中的經典實踐,展示了如何結合傳感器數據融合、實時顯示和用戶交互。
審核編輯 黃宇
-
單片機
+關注
關注
6039文章
44582瀏覽量
636479 -
STM32F103C8T6
+關注
關注
108文章
161瀏覽量
83705
發布評論請先 登錄
相關推薦
評論