• 基于单片机设计的水平仪(STC589C52+MPU6050)


    一、前言

    【1】项目背景

    水平仪是一种常见的测量工具,用于检测物体或设备的水平姿态。在许多应用中,如建筑、制造和航空等领域,保持设备的水平姿态是非常重要的。为了实现实时的水平检测和显示,基于单片机设计的水平仪是一个常见的解决方案。

    数字水平仪是一种用于测量物体相对于水平面的角度的仪器。它基于单片机设计,主控芯片为STC89C52,姿态检测采用MPU6050六轴传感器,显示屏用于显示水平姿态数据,锂电池供电。该仪器具有高精度、低功耗、易操作等特点,广泛应用于建筑、工程、测绘等领域。

    整个系统的设计思路是通过MPU6050获取设备的姿态数据,然后利用STC89C52进行数据处理和计算,最后将计算得到的水平偏移值通过SPI接口传输到0.96寸的OLED显示屏上进行实时显示。

    基于单片机设计的数字水平仪具有以下功能特点:

    1. 主控芯片:本设计采用STC89C52单片机作为主控芯片,具有强大的处理能力和丰富的外设接口,能够满足数字水平仪的功能需求。
    2. 姿态检测:通过MPU6050六轴传感器实现对物体姿态的实时检测,包括加速度计、陀螺仪和磁力计等,能够精确测量物体在三维空间中的倾斜角度。
    3. 显示屏显示:采用液晶显示屏实时显示水平姿态数据,用户可以通过显示屏直观地了解物体的倾斜情况。
    4. 锂电池供电:采用锂电池作为电源,具有高能量密度、长寿命和环保等优点,能够满足数字水平仪长时间工作的需求。
    5. 低功耗设计:通过合理的硬件设计和软件优化,实现低功耗运行,降低能耗,延长电池使用寿命。
    6. 数据存储与传输:内置存储器可存储大量姿态数据,支持USB接口进行数据传输,方便用户进行数据分析和处理。
    7. 易于操作:数字水平仪具有简洁明了的操作界面,用户只需简单设置即可开始测量,无需复杂的操作步骤。
    8. 稳定性高:通过高精度的姿态检测和数据处理算法,实现对物体倾斜角度的准确测量,保证测量结果的稳定性和可靠性。

    image-20230913122223179

    下面是手机上的水平仪软件显示效果: 原理是一样的

    image-20230913122020531

    image-20230913121840612

    【2】项目的关键点包括

    (1)硬件设计:包括将STC89C52和MPU6050连接在一起,确保它们之间的通信正常。同时,需要将OLED显示屏与STC89C52通过SPI接口连接起来,以便将姿态数据显示在屏幕上。

    (2)软件设计:需要编写嵌入式软件,包括驱动程序和算法,以实现数据的采集、处理和显示。主控芯片STC89C52上的程序需要读取MPU6050传感器的数据,并进行姿态计算,然后将结果发送到OLED显示屏上进行显示。

    (3)界面设计:在OLED显示屏上实时显示水平偏移值,需要设计一个简洁直观的用户界面,使用户能够清楚地了解设备的姿态状态。

    通过该项目,能够实现一个基于单片机设计的水平仪,可以实时检测设备的水平姿态,并将结果显示在OLED屏幕上。这对于许多需要保持设备水平的应用场景非常有用,提高了工作效率和准确性。

    二、项目软硬件设计思路

    【1】硬件设计思路

    (1)主控芯片选择:选择了STC89C52作为主控芯片。STC89C52是一款常用的单片机,具有丰富的外设接口和强大的处理能力,适合用于嵌入式应用。它具有8位的数据总线和12MHz的主频,能够满足的需求。

    (2)姿态检测传感器选择:选择了MPU6050作为姿态检测传感器。MPU6050是一种集成了三轴陀螺仪和三轴加速度计的传感器模块,能够准确地检测设备的姿态变化。它通过I2C接口与主控芯片进行通信,传输姿态数据。

    (3)OLED显示屏选择:选择了一款采用SPI接口的0.96寸OLED显示屏。SPI接口可以提供高速的数据传输,适合实时显示姿态数据。OLED显示屏具有高对比度、低功耗和快速响应的特点,非常适合作为水平偏移值的显示设备。

    (4)硬件接线:在硬件设计中,需要将STC89C52、MPU6050和OLED显示屏进行合适的接线连接。具体接线方式如下:

    将STC89C52的引脚与MPU6050的I2C接口连接,实现主控芯片与姿态传感器之间的通信。

    将STC89C52的引脚与OLED显示屏的SPI接口连接,以便将姿态数据传输到显示屏上。

    【2】软件设计思路

    (1)初始化:在软件设计中,首先需要进行硬件的初始化设置。包括初始化STC89C52的引脚和外设配置,以及初始化MPU6050和OLED显示屏的通信设置。

    (2)数据采集:通过主控芯片的I2C接口,读取MPU6050传感器的原始数据。MPU6050提供了陀螺仪和加速度计的数据,可以通过读取寄存器获取这些数据。

    (3)姿态计算:利用获取的陀螺仪和加速度计数据,进行姿态计算。常见的姿态计算算法包括互补滤波算法和卡尔曼滤波算法。

    (4)水平偏移值计算:根据姿态计算的结果,计算出水平偏移值。水平偏移值可以通过比较设备的当前姿态与水平状态的差异来确定。

    (5)数据显示:将计算得到的水平偏移值通过SPI接口发送到OLED显示屏。需要设计一个简洁的用户界面,在屏幕上实时显示水平偏移值。

    (6)循环执行:以上步骤需要在一个循环中不断执行,以实现实时的姿态检测和显示。循环的周期可以根据实际需求进行设置,通常需要考虑到实时性和性能的平衡。

    【3】硬件连线说明

    在此项目中,硬件模块需要连接到STC89C52单片机的不同引脚。

    下面是硬件模块与单片机引脚的连接描述:

    (1)MPU6050连接:

    • MPU6050的SCL引脚(时钟线)连接到STC89C52的P1.0引脚,作为I2C总线的时钟线。
    • MPU6050的SDA引脚(数据线)连接到STC89C52的P1.1引脚,作为I2C总线的数据线。
    • MPU6050的VCC引脚连接到电源正极(3.3V或5V)。
    • MPU6050的GND引脚连接到电源地线。

    (2)OLED显示屏连接:

    • OLED显示屏的SCL引脚(时钟线)连接到STC89C52的P1.2引脚,作为SPI总线的时钟线。
    • OLED显示屏的SDA引脚(数据线)连接到STC89C52的P1.3引脚,作为SPI总线的数据线。
    • OLED显示屏的RST引脚(复位线)连接到STC89C52的P1.4引脚,用于复位显示屏。
    • OLED显示屏的DC引脚(数据/命令选择线)连接到STC89C52的P1.5引脚,用于选择发送数据或命令。
    • OLED显示屏的CS引脚(片选线)连接到STC89C52的P1.6引脚,用于选中显示屏。
    • OLED显示屏的VCC引脚连接到电源正极(3.3V或5V)。
    • OLED显示屏的GND引脚连接到电源地线。

    三、项目代码设计

    #include 
    #include 
    
    // 定义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引脚
    
    // 姿态检测传感器相关定义
    sbit MPU_SCL = P2^6;    // I2C时钟引脚
    sbit MPU_SDA = P2^7;    // I2C数据引脚
    
    // 定义全局变量
    float pitch = 0.0;       // 当前设备的俯仰角
    
    // OLED显示屏相关函数
    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总线相关函数
    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相关函数
    void MPU_Init();
    void MPU_WriteReg(unsigned char reg, unsigned char dat);
    unsigned char MPU_ReadReg(unsigned char reg);
    void MPU_ReadData(short *data);
    
    // 延时函数
    void Delay(unsigned int n);
    
    // 主函数
    void main() {
        unsigned char str[16];
        
        MPU_Init();   // 初始化MPU6050
        OLED_Init();   // 初始化OLED显示屏
        
        while (1) {
            short data[3];
            MPU_ReadData(data);   // 读取姿态传感器数据
            
            pitch = -atan2(data[1], data[2]) * (180.0 / 3.14159);   // 计算俯仰角度
            
            sprintf(str, "Pitch:%.2f", pitch);   // 格式化俯仰角数据
            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显示屏写数据
    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] != '\0') {
            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总线发送应答信号
    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总线发送非应答信号
    void I2C_NAck() {
        MPU_SCL = 0;
        MPU_SDA = 1;
        Delay(1);
        MPU_SCL = 1;
        Delay(1);
        MPU_SCL = 0;
    }
    
    // I2C总线发送一个字节数据
    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总线读取一个字节数据
    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);    // 数据
        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();  // 读取数据
        I2C_NAck();
        I2C_Stop();
        
        return dat;
    }
    
    // MPU6050读取数据
    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();   // 读取数据
            I2C_Ack();
        }
        
        buf[13] = I2C_ReadByte();   // 读取数据
        I2C_NAck();
        I2C_Stop();
        
        // 数据转换
        data[0] = ((short)buf[0] << 8) | buf[1];
        data[1] = ((short)buf[2] << 8) | buf[3];
        data[2] = ((short)buf[4] << 8) | buf[5];
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344

    四、总结

    这个项目是基于单片机设计的水平仪,使用了STC89C52作为主控芯片和MPU6050作为姿态检测传感器。其主要功能是检测当前设备的姿态,并计算出水平偏移值,最后通过OLED显示屏实时展示。

    整个项目涉及到硬件和软件两个方面。硬件方面,使用STC89C52作为主控芯片,负责控制整个系统的运行和数据处理。MPU6050姿态检测传感器用于获取设备的姿态信息,包括加速度和角速度。OLED显示屏采用SPI接口的0.96寸显示屏,用于将计算得到的水平偏移值实时显示出来。

    软件方面,编写嵌入式C程序来实现系统的功能。通过STC89C52与MPU6050进行通信,获取姿态传感器的原始数据。根据这些原始数据进行姿态计算,得到水平偏移值。再将计算得到的水平偏移值通过SPI接口发送给OLED显示屏,实时显示在屏幕上。

    项目利用STC89C52和MPU6050实现了一个水平仪,能够检测设备的姿态并计算出水平偏移值,并通过OLED显示屏实时展示。这个水平仪可以在许多应用场景中使用,如建筑工地、航空航天等需要测量水平的领域。

  • 相关阅读:
    【Linux03-基本工具之GCC】Linux下的C语言编译器
    社交媒体与社会网络分析,深度解读社交网络营销
    06 linux: 文件管理命令
    Milvus踩坑笔记
    【Local/Docker/K8S/Racher】Local/Docker/K8S/Racher安装Celery并启动异步任务-20220820
    10. Spring Boot2.5 实战 Docker 容器
    linux解决报错 libstdc++.so.6: version GLIBCXX_3.4.30 not found
    内切相减原理绘制CAD图形
    前端周刊第二十六期
    ssm+vue+elementUI 公廉租房维保系统-#毕业设计
  • 原文地址:https://blog.csdn.net/xiaolong1126626497/article/details/134439581