一、前言
【1】項目背景
水平儀是一種常見的測量工具,用于檢測物體或設備的水平姿態(tài)。在許多應用中,如建筑、制造和航空等領域,保持設備的水平姿態(tài)是非常重要的。為了實現(xiàn)實時的水平檢測和顯示,基于單片機設計的水平儀是一個常見的解決方案。
數(shù)字水平儀是一種用于測量物體相對于水平面的角度的儀器。它基于單片機設計,主控芯片為STC89C52,姿態(tài)檢測采用MPU6050六軸傳感器,顯示屏用于顯示水平姿態(tài)數(shù)據(jù),鋰電池供電。該儀器具有高精度、低功耗、易操作等特點,廣泛應用于建筑、工程、測繪等領域。
整個系統(tǒng)的設計思路是通過MPU6050獲取設備的姿態(tài)數(shù)據(jù),然后利用STC89C52進行數(shù)據(jù)處理和計算,最后將計算得到的水平偏移值通過SPI接口傳輸?shù)?.96寸的OLED顯示屏上進行實時顯示。
基于單片機設計的數(shù)字水平儀具有以下功能特點:
- 主控芯片:本設計采用STC89C52單片機作為主控芯片,具有強大的處理能力和豐富的外設接口,能夠滿足數(shù)字水平儀的功能需求。
- 姿態(tài)檢測:通過MPU6050六軸傳感器實現(xiàn)對物體姿態(tài)的實時檢測,包括加速度計、陀螺儀和磁力計等,能夠精確測量物體在三維空間中的傾斜角度。
- 顯示屏顯示:采用液晶顯示屏實時顯示水平姿態(tài)數(shù)據(jù),用戶可以通過顯示屏直觀地了解物體的傾斜情況。
- 鋰電池供電:采用鋰電池作為電源,具有高能量密度、長壽命和環(huán)保等優(yōu)點,能夠滿足數(shù)字水平儀長時間工作的需求。
- 低功耗設計:通過合理的硬件設計和軟件優(yōu)化,實現(xiàn)低功耗運行,降低能耗,延長電池使用壽命。
- 數(shù)據(jù)存儲與傳輸:內置存儲器可存儲大量姿態(tài)數(shù)據(jù),支持USB接口進行數(shù)據(jù)傳輸,方便用戶進行數(shù)據(jù)分析和處理。
- 易于操作:數(shù)字水平儀具有簡潔明了的操作界面,用戶只需簡單設置即可開始測量,無需復雜的操作步驟。
- 穩(wěn)定性高:通過高精度的姿態(tài)檢測和數(shù)據(jù)處理算法,實現(xiàn)對物體傾斜角度的準確測量,保證測量結果的穩(wěn)定性和可靠性。
下面是手機上的水平儀軟件顯示效果: 原理是一樣的
【2】項目的關鍵點包括
(1)硬件設計:包括將STC89C52和MPU6050連接在一起,確保它們之間的通信正常。同時,需要將OLED顯示屏與STC89C52通過SPI接口連接起來,以便將姿態(tài)數(shù)據(jù)顯示在屏幕上。
(2)軟件設計:需要編寫嵌入式軟件,包括驅動程序和算法,以實現(xiàn)數(shù)據(jù)的采集、處理和顯示。主控芯片STC89C52上的程序需要讀取MPU6050傳感器的數(shù)據(jù),并進行姿態(tài)計算,然后將結果發(fā)送到OLED顯示屏上進行顯示。
(3)界面設計:在OLED顯示屏上實時顯示水平偏移值,需要設計一個簡潔直觀的用戶界面,使用戶能夠清楚地了解設備的姿態(tài)狀態(tài)。
通過該項目,能夠實現(xiàn)一個基于單片機設計的水平儀,可以實時檢測設備的水平姿態(tài),并將結果顯示在OLED屏幕上。這對于許多需要保持設備水平的應用場景非常有用,提高了工作效率和準確性。
二、項目軟硬件設計思路
【1】硬件設計思路
(1)主控芯片選擇:選擇了STC89C52作為主控芯片。STC89C52是一款常用的單片機,具有豐富的外設接口和強大的處理能力,適合用于嵌入式應用。它具有8位的數(shù)據(jù)總線和12MHz的主頻,能夠滿足的需求。
(2)姿態(tài)檢測傳感器選擇:選擇了MPU6050作為姿態(tài)檢測傳感器。MPU6050是一種集成了三軸陀螺儀和三軸加速度計的傳感器模塊,能夠準確地檢測設備的姿態(tài)變化。它通過I2C接口與主控芯片進行通信,傳輸姿態(tài)數(shù)據(jù)。
(3)OLED顯示屏選擇:選擇了一款采用SPI接口的0.96寸OLED顯示屏。SPI接口可以提供高速的數(shù)據(jù)傳輸,適合實時顯示姿態(tài)數(shù)據(jù)。OLED顯示屏具有高對比度、低功耗和快速響應的特點,非常適合作為水平偏移值的顯示設備。
(4)硬件接線:在硬件設計中,需要將STC89C52、MPU6050和OLED顯示屏進行合適的接線連接。具體接線方式如下:
將STC89C52的引腳與MPU6050的I2C接口連接,實現(xiàn)主控芯片與姿態(tài)傳感器之間的通信。
將STC89C52的引腳與OLED顯示屏的SPI接口連接,以便將姿態(tài)數(shù)據(jù)傳輸?shù)斤@示屏上。
【2】軟件設計思路
(1)初始化:在軟件設計中,首先需要進行硬件的初始化設置。包括初始化STC89C52的引腳和外設配置,以及初始化MPU6050和OLED顯示屏的通信設置。
(2)數(shù)據(jù)采集:通過主控芯片的I2C接口,讀取MPU6050傳感器的原始數(shù)據(jù)。MPU6050提供了陀螺儀和加速度計的數(shù)據(jù),可以通過讀取寄存器獲取這些數(shù)據(jù)。
(3)姿態(tài)計算:利用獲取的陀螺儀和加速度計數(shù)據(jù),進行姿態(tài)計算。常見的姿態(tài)計算算法包括互補濾波算法和卡爾曼濾波算法。
(4)水平偏移值計算:根據(jù)姿態(tài)計算的結果,計算出水平偏移值。水平偏移值可以通過比較設備的當前姿態(tài)與水平狀態(tài)的差異來確定。
(5)數(shù)據(jù)顯示:將計算得到的水平偏移值通過SPI接口發(fā)送到OLED顯示屏。需要設計一個簡潔的用戶界面,在屏幕上實時顯示水平偏移值。
(6)循環(huán)執(zhí)行:以上步驟需要在一個循環(huán)中不斷執(zhí)行,以實現(xiàn)實時的姿態(tài)檢測和顯示。循環(huán)的周期可以根據(jù)實際需求進行設置,通常需要考慮到實時性和性能的平衡。
【3】硬件連線說明
在此項目中,硬件模塊需要連接到STC89C52單片機的不同引腳。
下面是硬件模塊與單片機引腳的連接描述:
(1)MPU6050連接:
- MPU6050的SCL引腳(時鐘線)連接到STC89C52的P1.0引腳,作為I2C總線的時鐘線。
- MPU6050的SDA引腳(數(shù)據(jù)線)連接到STC89C52的P1.1引腳,作為I2C總線的數(shù)據(jù)線。
- MPU6050的VCC引腳連接到電源正極(3.3V或5V)。
- MPU6050的GND引腳連接到電源地線。
(2)OLED顯示屏連接:
- OLED顯示屏的SCL引腳(時鐘線)連接到STC89C52的P1.2引腳,作為SPI總線的時鐘線。
- OLED顯示屏的SDA引腳(數(shù)據(jù)線)連接到STC89C52的P1.3引腳,作為SPI總線的數(shù)據(jù)線。
- OLED顯示屏的RST引腳(復位線)連接到STC89C52的P1.4引腳,用于復位顯示屏。
- OLED顯示屏的DC引腳(數(shù)據(jù)/命令選擇線)連接到STC89C52的P1.5引腳,用于選擇發(fā)送數(shù)據(jù)或命令。
- OLED顯示屏的CS引腳(片選線)連接到STC89C52的P1.6引腳,用于選中顯示屏。
- OLED顯示屏的VCC引腳連接到電源正極(3.3V或5V)。
- OLED顯示屏的GND引腳連接到電源地線。
三、項目代碼設計
#include <reg52.h>
#include <intrins.h>
// 定義OLED顯示屏引腳
sbit OLED_RST = P1^0; // RST引腳
sbit OLED_DC = P1^1; // DC引腳
sbit OLED_DIN = P1^2; // DIN引腳
sbit OLED_CLK = P1^3; // CLK引腳
sbit OLED_CS = P1^4; // CS引腳
// 姿態(tài)檢測傳感器相關定義
sbit MPU_SCL = P2^6; // I2C時鐘引腳
sbit MPU_SDA = P2^7; // I2C數(shù)據(jù)引腳
// 定義全局變量
float pitch = 0.0; // 當前設備的俯仰角
// OLED顯示屏相關函數(shù)
void OLED_WrCmd(unsigned char cmd);
void OLED_WrDat(unsigned char dat);
void OLED_Init();
void OLED_SetPos(unsigned char x, unsigned char y);
void OLED_Fill(unsigned char bmp_data);
void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *str);
// I2C總線相關函數(shù)
void I2C_Start();
void I2C_Stop();
unsigned char I2C_WaitAck();
void I2C_Ack();
void I2C_NAck();
void I2C_SendByte(unsigned char dat);
unsigned char I2C_ReadByte();
// MPU6050相關函數(shù)
void MPU_Init();
void MPU_WriteReg(unsigned char reg, unsigned char dat);
unsigned char MPU_ReadReg(unsigned char reg);
void MPU_ReadData(short *data);
// 延時函數(shù)
void Delay(unsigned int n);
// 主函數(shù)
void main() {
unsigned char str[16];
MPU_Init(); // 初始化MPU6050
OLED_Init(); // 初始化OLED顯示屏
while (1) {
short data[3];
MPU_ReadData(data); // 讀取姿態(tài)傳感器數(shù)據(jù)
pitch = -atan2(data[1], data[2]) * (180.0 / 3.14159); // 計算俯仰角度
sprintf(str, "Pitch:%.2f", pitch); // 格式化俯仰角數(shù)據(jù)
OLED_ShowString(0, 0, str); // 在OLED顯示屏上顯示俯仰角度
Delay(100);
}
}
// OLED顯示屏寫命令
void OLED_WrCmd(unsigned char cmd) {
unsigned char i;
OLED_DC = 0;
OLED_CS = 0;
for (i = 0; i < 8; i++) {
OLED_CLK = 0;
if (cmd & 0x80) {
OLED_DIN = 1;
} else {
OLED_DIN = 0;
}
OLED_CLK = 1;
cmd <<= 1;
}
OLED_CS = 1;
}
// OLED顯示屏寫數(shù)據(jù)
void OLED_WrDat(unsigned char dat) {
unsigned char i;
OLED_DC = 1;
OLED_CS = 0;
for (i = 0; i < 8; i++) {
OLED_CLK = 0;
if (dat & 0x80) {
OLED_DIN = 1;
} else {
OLED_DIN = 0;
}
OLED_CLK = 1;
dat <<= 1;
}
OLED_CS = 1;
}
// OLED顯示屏初始化
void OLED_Init() {
OLED_RST = 0;
Delay(100);
OLED_RST = 1;
Delay(100);
OLED_WrCmd(0xae); // 關閉顯示
OLED_WrCmd(0x00); // 設置低列地址
OLED_WrCmd(0x10); // 設置高列地址
OLED_WrCmd(0x40); // 設置起始行地址
OLED_WrCmd(0x81); // 對比度設置
OLED_WrCmd(0xcf); // 設置對比度
OLED_WrCmd(0xa1); // 設置段重映射
OLED_WrCmd(0xc8); // 設置列重映射
OLED_WrCmd(0xa6); // 正常顯示
OLED_WrCmd(0xa8); // 多路復用設置
OLED_WrCmd(0x3f); // 設置多路復用
OLED_WrCmd(0xd3); // 設置顯示偏移
OLED_WrCmd(0x00); // 設置顯示偏移
OLED_WrCmd(0xd5); // 設置顯示時鐘分頻
OLED_WrCmd(0x80); // 設置顯示時鐘分頻
OLED_WrCmd(0xd9); // 設置預充電周期
OLED_WrCmd(0xf1); // 設置預充電周期
OLED_WrCmd(0xda); // 設置COM硬件引腳配置
OLED_WrCmd(0x12); // 設置COM硬件引腳配置
OLED_WrCmd(0xdb); // 設置VCOMH電壓倍率
OLED_WrCmd(0x40); // 設置VCOMH電壓倍率
OLED_WrCmd(0x8d); // 設置DC-DC電壓輸出開關
OLED_WrCmd(0x14); // 設置DC-DC電壓輸出開關
OLED_WrCmd(0xaf); // 打開顯示
OLED_Fill(0x00); // 清屏
}
// OLED顯示屏設置位置
void OLED_SetPos(unsigned char x, unsigned char y) {
OLED_WrCmd(0xb0 + y);
OLED_WrCmd(((x & 0xf0) >> 4) | 0x10);
OLED_WrCmd((x & 0x0f) | 0x01);
}
// OLED顯示屏填充
void OLED_Fill(unsigned char bmp_data) {
unsigned char y, x;
for (y = 0; y < 8; y++) {
OLED_WrCmd(0xb0 + y);
OLED_WrCmd(0x00);
OLED_WrCmd(0x10);
for (x = 0; x < 128; x++) {
OLED_WrDat(bmp_data);
}
}
}
// OLED顯示屏顯示字符串
void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *str) {
unsigned char c = 0, i = 0;
while (str[i] != '?') {
c = str[i] - 32;
if (x > 120) {
x = 0;
y++;
}
OLED_SetPos(x, y);
for (i = 0; i < 6; i++) {
OLED_WrDat(F6x8[c][i]);
}
i++;
x += 6;
}
}
// I2C總線開始信號
void I2C_Start() {
MPU_SDA = 1;
MPU_SCL = 1;
Delay(1);
MPU_SDA = 0;
Delay(1);
MPU_SCL = 0;
}
// I2C總線停止信號
void I2C_Stop() {
MPU_SDA = 0;
MPU_SCL = 1;
Delay(1);
MPU_SDA = 1;
Delay(1);
}
// I2C總線等待應答信號
unsigned char I2C_WaitAck() {
unsigned char ack;
MPU_SDA = 1;
Delay(1);
MPU_SCL = 1;
Delay(1);
ack = MPU_SDA;
MPU_SCL = 0;
return ack;
}
// I2C總線發(fā)送應答信號
void I2C_Ack() {
MPU_SCL = 0;
MPU_SDA = 0;
Delay(1);
MPU_SCL = 1;
Delay(1);
MPU_SCL = 0;
MPU_SDA = 1;
Delay(1);
}
// I2C總線發(fā)送非應答信號
void I2C_NAck() {
MPU_SCL = 0;
MPU_SDA = 1;
Delay(1);
MPU_SCL = 1;
Delay(1);
MPU_SCL = 0;
}
// I2C總線發(fā)送一個字節(jié)數(shù)據(jù)
void I2C_SendByte(unsigned char dat) {
unsigned char i;
for (i = 0; i < 8; i++) {
MPU_SDA = (dat & 0x80) >> 7;
dat <<= 1;
Delay(1);
MPU_SCL = 1;
Delay(1);
MPU_SCL = 0;
Delay(1);
}
MPU_SDA = 1;
Delay(1);
MPU_SCL = 1;
Delay(1);
MPU_SCL = 0;
}
// I2C總線讀取一個字節(jié)數(shù)據(jù)
unsigned char I2C_ReadByte() {
unsigned char i, dat;
for (i = 0; i < 8; i++) {
dat <<= 1;
MPU_SCL = 1;
Delay(1);
dat |= MPU_SDA;
MPU_SCL = 0;
Delay(1);
}
return dat;
}
// MPU6050初始化
void MPU_Init() {
I2C_Start();
I2C_SendByte(0xd0); // 輸入器件地址
I2C_WaitAck();
I2C_SendByte(0x6b); // PWR_MGMT_1寄存器地址
I2C_WaitAck();
I2C_SendByte(0x00); // 寫0,喚醒設備
I2C_WaitAck();
I2C_Stop();
}
// MPU6050寫寄存器
void MPU_WriteReg(unsigned char reg, unsigned char dat) {
I2C_Start();
I2C_SendByte(0xd0); // 輸入器件地址
I2C_WaitAck();
I2C_SendByte(reg); // 寄存器地址
I2C_WaitAck();
I2C_SendByte(dat); // 數(shù)據(jù)
I2C_WaitAck();
I2C_Stop();
}
// MPU6050讀寄存器
unsigned char MPU_ReadReg(unsigned char reg) {
unsigned char dat;
I2C_Start();
I2C_SendByte(0xd0); // 輸入器件地址
I2C_WaitAck();
I2C_SendByte(reg); // 寄存器地址
I2C_WaitAck();
I2C_Start();
I2C_SendByte(0xd1); // 輸出器件地址
I2C_WaitAck();
dat = I2C_ReadByte(); // 讀取數(shù)據(jù)
I2C_NAck();
I2C_Stop();
return dat;
}
// MPU6050讀取數(shù)據(jù)
void MPU_ReadData(short *data) {
unsigned char i;
unsigned char buf[14];
I2C_Start();
I2C_SendByte(0xd0); // 輸入器件地址
I2C_WaitAck();
I2C_SendByte(0x3b); // 寄存器地址
I2C_WaitAck();
I2C_Start();
I2C_SendByte(0xd1); // 輸出器件地址
I2C_WaitAck();
for (i = 0; i < 13; i++) {
buf[i] = I2C_ReadByte(); // 讀取數(shù)據(jù)
I2C_Ack();
}
buf[13] = I2C_ReadByte(); // 讀取數(shù)據(jù)
I2C_NAck();
I2C_Stop();
// 數(shù)據(jù)轉換
data[0] = ((short)buf[0] << 8) | buf[1];
data[1] = ((short)buf[2] << 8) | buf[3];
data[2] = ((short)buf[4] << 8) | buf[5];
}
四、總結
這個項目是基于單片機設計的水平儀,使用了STC89C52作為主控芯片和MPU6050作為姿態(tài)檢測傳感器。其主要功能是檢測當前設備的姿態(tài),并計算出水平偏移值,最后通過OLED顯示屏實時展示。
整個項目涉及到硬件和軟件兩個方面。硬件方面,使用STC89C52作為主控芯片,負責控制整個系統(tǒng)的運行和數(shù)據(jù)處理。MPU6050姿態(tài)檢測傳感器用于獲取設備的姿態(tài)信息,包括加速度和角速度。OLED顯示屏采用SPI接口的0.96寸顯示屏,用于將計算得到的水平偏移值實時顯示出來。
軟件方面,編寫嵌入式C程序來實現(xiàn)系統(tǒng)的功能。通過STC89C52與MPU6050進行通信,獲取姿態(tài)傳感器的原始數(shù)據(jù)。根據(jù)這些原始數(shù)據(jù)進行姿態(tài)計算,得到水平偏移值。再將計算得到的水平偏移值通過SPI接口發(fā)送給OLED顯示屏,實時顯示在屏幕上。
項目利用STC89C52和MPU6050實現(xiàn)了一個水平儀,能夠檢測設備的姿態(tài)并計算出水平偏移值,并通過OLED顯示屏實時展示。這個水平儀可以在許多應用場景中使用,如建筑工地、航空航天等需要測量水平的領域。