• 基于单片机设计的电子指南针(LSM303DLH模块(三轴磁场 + 三轴加速度)


    一、前言

    本项目是基于单片机设计的电子指南针,主要利用STC89C52作为主控芯片和LSM303DLH模块作为指南针模块。通过LCD1602液晶显示屏来展示检测到的指南针信息。

    在日常生活中,指南针是一种非常实用的工具,可以帮助我们确定方向,特别是在户外探险、航海、定位等场景中。传统的磁罗盘指南针存在一些不便之处,如体积较大、不易携带、容易受到外界干扰等。设计一款基于单片机的电子指南针是比较有意义的项目。

    为了实现这个项目,选择了STC89C52作为主控芯片。STC89C52是一款功能强大且成本较低的单片机,具有丰富的接口和强大的处理能力,非常适合用于嵌入式应用。同时,为了获得准确的指南针数据,采用了LSM303DLH模块作为指南针模块。该模块集成了三轴磁场传感器和三轴加速度传感器,能够提供高精度和稳定的指南针数据。

    在项目的具体实现中,通过STC89C52与LSM303DLH模块进行通信,获取指南针传感器的原始数据。对这些原始数据进行处理和计算,通过磁场数据确定方向,并结合加速度数据来提高测量的准确性。最后,将计算得到的指南针信息通过LCD1602液晶显示屏展示出来,用户可以直观地查看当前的方向。

    通过该电子指南针,用户可以方便地获得当前的方向信息,无论是在户外旅行、徒步探险还是其他需要导航的场景中,都能提供实时准确的方向指引。该项目不仅具有一定的技术挑战性,也能为用户带来便利和实用性。

    image-20230913134357085

    image-20230913134451215

    二、项目设计过程

    本项目的硬件模块接线、硬件设计思路以及软件设计思路如下:

    2.1 硬件模块接线

    (1)将STC89C52的VCC引脚连接到电源正极,将GND引脚连接到电源负极。

    (2)将LSM303DLH模块的VCC引脚连接到电源正极,将GND引脚连接到电源负极。

    (3)将LSM303DLH模块的SCL引脚连接到STC89C52的P2.0引脚,作为I2C的串行时钟线。

    (4)将LSM303DLH模块的SDA引脚连接到STC89C52的P2.1引脚,作为I2C的串行数据线。

    (5)将LCD1602液晶显示屏的VCC引脚连接到电源正极,将GND引脚连接到电源负极。

    (6)将LCD1602液晶显示屏的RS引脚连接到STC89C52的P0.0引脚,作为指令/数据选择线。

    (7)将LCD1602液晶显示屏的RW引脚连接到STC89C52的P0.1引脚,作为读写选择线。

    (8)将LCD1602液晶显示屏的E引脚连接到STC89C52的P0.2引脚,作为使能控制线。

    (9)将LCD1602液晶显示屏的D0-D7引脚连接到STC89C52的P1口引脚或P3口引脚,作为数据线。

    2.2 硬件设计思路

    (1)主控芯片选择了STC89C52,其具有丰富的IO口和强大的处理能力,适合用于该项目。

    (2)指南针模块采用了LSM303DLH,它集成了磁场和加速度传感器,能够提供准确的指南针数据。

    (3)LCD1602液晶显示屏用于显示检测到的指南针信息,在硬件设计中需要连接正确的引脚。

    2.3 软件设计思路

    (1)在软件设计中,需要配置STC89C52的IO口,以及I2C总线通信。

    (2)通过I2C总线与LSM303DLH进行通信,获取指南针模块的原始数据。

    (3)对获取的原始数据进行处理和计算,得到当前的指南针信息,确定方向。

    (4)将计算得到的指南针信息通过LCD1602液晶显示屏进行显示。

    (5)编写相应的函数来实现LCD1602的初始化、显示字符、显示字符串等功能。

    (6)通过主循环不断更新指南针信息和LCD1602的显示。

    本项目的硬件模块接线涉及到主控芯片、指南针模块和LCD1602液晶显示屏的连接。硬件设计思路是选择适合的芯片和模块,确保正常的数据传输和显示功能。软件设计思路包括配置IO口、I2C通信、数据处理和LCD1602显示功能的实现。通过这些设计,实现了一个基于单片机的电子指南针,并能够通过LCD1602显示屏显示检测到的指南针信息。

    三、LSM303DLH 模块介绍

    LSM303DLH 是一种集成式数字三轴加速度计和磁力计模块,由STMicroelectronics公司生产。结合了两个传感器,提供了同时测量物体的加速度和磁场的功能。

    下面是 LSM303DLH 模块的一些主要特点和功能:

    (1)加速度计功能:LSM303DLH 可以测量物体在三个轴向(X、Y 和 Z 轴)上的加速度。它提供了高分辨率的加速度测量范围,通常为 ±2g(重力加速度)至 ±16g。这使得它适用于各种应用,如运动检测、姿态测量和震动监测等。

    (2)磁力计功能:LSM303DLH 还具有磁力计功能,可以测量物体周围的磁场。它使用磁阻式传感器来检测磁场的强度和方向,并提供三个轴向上的磁场测量数据。这使得它在指南针导航、地磁定位和磁场检测等应用中非常有用。

    (3)数字输出接口:LSM303DLH 通过I2C或SPI接口与主控制器通信。这些数字接口使得与微控制器、单片机或其他数字设备的集成变得简单。

    (4)高性能:LSM303DLH 提供高精度和低噪声的测量,以获得准确的加速度和磁场数据。它还具有温度补偿功能,可以提高测量的稳定性和精确性。

    (5)低功耗:LSM303DLH 设计为低功耗模式,可以在不太耗电的情况下运行。这对于依靠电池供电的移动设备和便携式应用非常重要。

    (6)应用领域:由于 LSM303DLH 模块同时提供了加速度计和磁力计功能,它适用于许多应用领域。例如,它可以用于移动设备中的姿态检测和自动旋转屏幕功能,用于导航系统中的指南针功能,以及用于运动追踪设备中的步数计算和运动分析等。

    四、项目代码设计

    #include 
    #include 
    
    // 定义LCD1602引脚连接
    sbit RS = P0^0;    // 指令/数据选择线
    sbit RW = P0^1;    // 读写选择线
    sbit E = P0^2;     // 使能控制线
    
    // 定义I2C总线连接
    sbit SCL = P2^0;   // I2C串行时钟线
    sbit SDA = P2^1;   // I2C串行数据线
    
    // 函数声明
    void delay_us(unsigned int us);
    void delay_ms(unsigned int ms);
    
    void I2C_Start();
    void I2C_Stop();
    void I2C_Ack();
    void I2C_NoAck();
    bit I2C_WaitAck();
    void I2C_SendByte(unsigned char dat);
    unsigned char I2C_ReceiveByte();
    
    void LCD_Init();
    void LCD_WriteCmd(unsigned char cmd);
    void LCD_WriteData(unsigned char dat);
    void LCD_SetCursor(unsigned char row, unsigned char col);
    void LCD_DisplayString(unsigned char row, unsigned char col, unsigned char *str);
    
    void Compass_Init();
    unsigned char Compass_Read();
    void Compass_Calculate(unsigned char raw_data, unsigned char *heading);
    
    // 主函数
    int main() {
        unsigned char heading;
        unsigned char str[16];
    
        LCD_Init();
        Compass_Init();
    
        while(1) {
            heading = Compass_Read();
            Compass_Calculate(heading, str);
    
            LCD_SetCursor(0, 0);
            LCD_DisplayString(0, 2, "Compass");
            LCD_SetCursor(1, 4);
            LCD_DisplayString(1, 6, str);
    
            delay_ms(500);
        }
    
        return 0;
    }
    
    // 延时函数,微秒级延时
    void delay_us(unsigned int us) {
        while (us--) {
            _nop_();
            _nop_();
            _nop_();
            _nop_();
        }
    }
    
    // 延时函数,毫秒级延时
    void delay_ms(unsigned int ms) {
        while (ms--) {
            delay_us(1000);
        }
    }
    
    // I2C总线开始
    void I2C_Start() {
        SDA = 1;
        SCL = 1;
        delay_us(5);
        SDA = 0;
        delay_us(5);
        SCL = 0;
    }
    
    // I2C总线结束
    void I2C_Stop() {
        SDA = 0;
        SCL = 1;
        delay_us(5);
        SDA = 1;
        delay_us(5);
    }
    
    // I2C总线发送应答信号
    void I2C_Ack() {
        SDA = 0;
        SCL = 1;
        delay_us(5);
        SCL = 0;
        delay_us(5);
    }
    
    // I2C总线发送不应答信号
    void I2C_NoAck() {
        SDA = 1;
        SCL = 1;
        delay_us(5);
        SCL = 0;
        delay_us(5);
    }
    
    // 等待I2C总线应答
    bit I2C_WaitAck() {
        unsigned int i = 500;
    
        SDA = 1;
        SCL = 1;
        delay_us(1);
    
        while (SDA) {
            if (--i == 0) {
                I2C_Stop();
                return 0;
            }
        }
    
        SCL = 0;
        return 1;
    }
    
    // I2C总线发送字节
    void I2C_SendByte(unsigned char dat) {
        unsigned char i;
    
        for (i = 0; i < 8; i++) {
            SDA = dat & 0x80;
            SCL = 1;
            delay_us(5);
            SCL = 0;
            delay_us(5);
            dat <<= 1;
        }
    }
    
    // I2C总线接收字节
    unsigned char I2C_ReceiveByte() {
        unsigned char i;
        unsigned char dat = 0;
    
        SDA = 1;
        for (i = 0; i < 8; i++) {
            dat <<= 1;
            SCL = 1;
            delay_us(5);
            dat |= SDA;
            SCL = 0;
            delay_us(5);
        }
    
        return dat;
    }
    
    // LCD初始化
    void LCD_Init() {
        delay_ms(50);
        LCD_WriteCmd(0x38);
        delay_us(50);
        LCD_WriteCmd(0x0C);
        delay_us(50);
        LCD_WriteCmd(0x01);
        delay_ms(5);
    }
    
    // LCD写入指令
    void LCD_WriteCmd(unsigned char cmd) {
        RS = 0;
        RW = 0;
        P1 = cmd;
        E = 1;
        delay_us(5);
        E = 0;
        delay_us(5);
    }
    
    // LCD写入数据
    void LCD_WriteData(unsigned char dat) {
        RS = 1;
        RW = 0;
        P1 = dat;
        E = 1;
        delay_us(5);
        E = 0;
        delay_us(5);
    }
    
    // LCD设置光标位置
    void LCD_SetCursor(unsigned char row, unsigned char col) {
        unsigned char addr;
    
        if (row == 0) {
            addr = 0x80 + col;
        }
        else {
            addr = 0xC0 + col;
        }
    
        LCD_WriteCmd(addr);
        delay_us(5);
    }
    
    // LCD显示字符串
    void LCD_DisplayString(unsigned char row, unsigned char col, unsigned char *str) {
        LCD_SetCursor(row, col);
    
        while (*str != '\0') {
            LCD_WriteData(*str++);
            delay_us(5);
        }
    }
    
    #define LSM303DLH_CTRL_REG1_A 0x20
    #define LSM303DLH_OUT_X_H_A 0x29
    
    // 指南针初始化
    void Compass_Init() {
        // 设置控制寄存器1,使能XYZ轴加速度计,数据速率=50Hz
        I2C_Start();
        I2C_SendByte(0x3A); // LSM303DLH的I2C地址,注意写操作要在读写位上加低电平
        I2C_WaitAck();
        I2C_SendByte(LSM303DLH_CTRL_REG1_A);
        I2C_WaitAck();
        I2C_SendByte(0x27);
        I2C_WaitAck();
        I2C_Stop();
    }
    
    // 读取指南针数据
    unsigned char Compass_Read() {
        unsigned char data;
    
        // 读取X轴高位数据寄存器
        I2C_Start();
        I2C_SendByte(0x3A);
        I2C_WaitAck();
        I2C_SendByte(LSM303DLH_OUT_X_H_A);
        I2C_WaitAck();
        I2C_Start();
        I2C_SendByte(0x3B);
        I2C_WaitAck();
        data = I2C_ReceiveByte();
        I2C_NoAck();
        I2C_Stop();
    
        return data;
    }
    
    #define LSM303DLH_OUT_X_H_M 0x03
    #define LSM303DLH_OUT_Y_H_M 0x05
    #define LSM303DLH_OUT_Z_H_M 0x07
    
    // 计算指南针方向
    void Compass_Calculate(unsigned char *heading) {
        int x, y, z;
        
        // 读取X轴、Y轴和Z轴的磁力计数据
        I2C_Start();
        I2C_SendByte(0x3C); // LSM303DLH的I2C地址,注意写操作要在读写位上加低电平
        I2C_WaitAck();
        I2C_SendByte(LSM303DLH_OUT_X_H_M);
        I2C_WaitAck();
        I2C_Start();
        I2C_SendByte(0x3D);
        I2C_WaitAck();
        x = (I2C_ReceiveByte() << 8) | I2C_ReceiveByte();
        x = -(x / 16); // 根据实际情况进行校正
        I2C_NoAck();
        I2C_Stop();
        
        I2C_Start();
        I2C_SendByte(0x3C);
        I2C_WaitAck();
        I2C_SendByte(LSM303DLH_OUT_Y_H_M);
        I2C_WaitAck();
        I2C_Start();
        I2C_SendByte(0x3D);
        I2C_WaitAck();
        y = (I2C_ReceiveByte() << 8) | I2C_ReceiveByte();
        y = -(y / 16); // 根据实际情况进行校正
        I2C_NoAck();
        I2C_Stop();
        
        I2C_Start();
        I2C_SendByte(0x3C);
        I2C_WaitAck();
        I2C_SendByte(LSM303DLH_OUT_Z_H_M);
        I2C_WaitAck();
        I2C_Start();
        I2C_SendByte(0x3D);
        I2C_WaitAck();
        z = (I2C_ReceiveByte() << 8) | I2C_ReceiveByte();
        z = -(z / 16); // 根据实际情况进行校正
        I2C_NoAck();
        I2C_Stop();
        
        // 计算方向角度
        *heading = atan2(y, x) * 180 / PI;
        if (*heading < 0) {
            *heading += 360;
        }
    }
    
    
    • 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

    五、总结

    这个项目是基于STC89C52单片机和LSM303DLH模块设计的电子指南针。通过LCD1602显示器,可以实时显示检测到的指南针信息。

    使用STC89C52作为主控芯片,搭建了整个系统的基础。通过配置引脚和初始化串口通信等必要的设置,确保单片机与其他硬件模块正常通信。

    使用LSM303DLH模块来获取指南针的数据。该模块具有三轴磁场和三轴加速度功能,通过I2C总线与单片机进行通信。我们需要正确配置I2C通信,并实现相应的读取数据的函数。通过读取LSM303DLH模块的磁场数据,可以得到当前的指南针方向。

    使用LCD1602显示器来显示指南针信息。通过初始化LCD1602和相应的控制函数,可以将当前的指南针方向以可视化的方式显示在LCD上,使用户能够方便地读取指南针信息。

    在整个项目中,需要注意LSM303DLH模块和LCD1602的正确连接,还需要考虑到磁场干扰、数据校准和滤波等问题,以确保指南针的准确性和稳定性。

    通过使用STC89C52单片机、LSM303DLH模块和LCD1602显示器,成功地设计并实现了一个电子指南针系统。这个系统可以读取磁场数据并计算出指南针的方向,并将其显示在LCD上,为用户提供了方便和准确的指南针功能。

  • 相关阅读:
    【牛客刷题】前端--JS篇(一)
    LeetCode 454 四数相加II 383. 赎金信 15. 三数之和 18. 四数之和
    【命令式编程和声明式编程】
    Python 爬取单个网页所需要加载的URL地址和CSS、JS文件地址
    工作流与BPM的区别
    Java代码审计——XML 外部实体注入(XXE)
    Virtualbox中对SD卡进行格式化和分区
    哈工大李治军老师操作系统笔记【17】:死锁处理(Learning OS Concepts By Coding Them !)
    IO 能够保证在确定的时间回来吗?
    【图像分类】【深度学习】【Pytorch版本】GoogLeNet(InceptionV1)模型算法详解
  • 原文地址:https://blog.csdn.net/xiaolong1126626497/article/details/134456723