基于GD32开发板的GPS定位模块的使用操作

news2024/12/23 12:49:07

基于上一章的介绍,本章将介绍如何基于gd32开发板使用gps定位模块。

一、官方代码分析

    正点原子的官方测试例程,测试代码的逻辑还是比较简单的,主要就是先调用函数atk_mo1218_init()进行初始化,接着就调用 SkyTraq binary 协议的 API 函数来配置ATK-MO1218 模块,配置无误后,ATK-MO1218 模块便会根据配置的测量频率不断的输出数据,接下来就调用函数 atk_mo1218_update()来获取 ATK-MO1218 模块输出的各个数据信息,然后打印至串口调试助手。

6bc2bf6dbfcaa8436e26427e40dd6bed.png

void demo_run(void)
{
    uint8_t ret;
    
    //初始化
    ret = atk_mo1218_init(38400);
    if (ret != 0)
    {
        printf("ATK-MO1218 init failed!\r\n");
        while (1)
        {
            LED0_TOGGLE();
            delay_ms(200);
        }
    }
    
    //配置
    ret  = atk_mo1218_factory_reset(ATK_MO1218_FACTORY_RESET_REBOOT);
    ret += atk_mo1218_config_output_type(ATK_MO1218_OUTPUT_NMEA, ATK_MO1218_SAVE_SRAM_FLASH);
    ret += atk_mo1218_config_nmea_msg(1, 1, 1, 1, 1, 1, 0, ATK_MO1218_SAVE_SRAM_FLASH);
    ret += atk_mo1218_config_position_rate(ATK_MO1218_POSITION_RATE_5HZ, ATK_MO1218_SAVE_SRAM_FLASH);
    ret += atk_mo1218_config_gnss_for_navigation(ATK_MO1218_GNSS_GPS_BEIDOU, ATK_MO1218_SAVE_SRAM_FLASH);
    if (ret != 0)
    {
        printf("ATK-MO1218 configure failed!\r\n");
        while (1)
        {
            LED0_TOGGLE();
            delay_ms(200);
        }
    }
     
    while (1)
    {
        uint8_t ret;
        atk_mo1218_time_t utc;
        atk_mo1218_position_t position;
        int16_t altitude;
        uint16_t speed;
        atk_mo1218_fix_info_t fix_info;
        atk_mo1218_visible_satellite_info_t gps_satellite_info = {0};
        atk_mo1218_visible_satellite_info_t beidou_satellite_info = {0};
        uint8_t satellite_index;
        
        while (1)
        {
            //获取 ATK-MO1218 模块输出的各个数据信息
            ret = atk_mo1218_update(&utc, &position, &altitude, &speed, &fix_info, NULL, NULL, 5000);
            if (ret == ATK_MO1218_EOK)
            {
                /* UTC */
                printf("UTC Time: %04d-%02d-%02d %02d:%02d:%02d.%03d\r\n", utc.year, utc.month, utc.day, utc.hour, utc.minute, utc.second, utc.millisecond);
                
                //经纬度 (放大了100000)
                printf("Position: %d.%d'%s %d.%d'%s\r\n", position.longitude.degree / 100000, position.longitude.degree % 100000, (position.longitude.indicator == ATK_MO1218_LONGITUDE_EAST) ? "E" : "W", position.latitude.degree / 100000, position.latitude.degree % 100000, (position.latitude.indicator == ATK_MO1218_LATITUDE_NORTH) ? "N" : "S");
                
                //海拔高度 (放大了10)
                printf("Altitude: %d.%dm\r\n", altitude / 10, altitude % 10);
                
                // 速度(放大了10)
                printf("Speed: %d.%dKm/H\r\n", speed / 10, speed % 10);
                
                //定位质量
                printf("Fix quality: %s\r\n", (fix_info.quality == ATK_MO1218_GPS_UNAVAILABLE) ? "Unavailable" : ((fix_info.quality == ATK_MO1218_GPS_VALID_SPS) ? "SPS mode" : "differential GPS mode"));
                
                //用于定位的卫星数量
                printf("Satellites Used: %d\r\n", fix_info.satellite_num);
                
                //定位方式
                printf("Fix type: %s\r\n", (fix_info.type == ATK_MO1218_FIX_NOT_AVAILABLE) ? "Unavailable" : ((fix_info.type == ATK_MO1218_FIX_2D) ? "2D" : "3D"));
                
                //用于定位的卫星编号
                for (satellite_index=0; satellite_index<fix_info.satellite_num; satellite_index++)
                {
                    if (satellite_index == 0)
                    {
                        printf("Satellite ID:");
                    }
                    printf(" %d", fix_info.satellite_id[satellite_index]);
                    if (satellite_index == fix_info.satellite_num - 1)
                    {
                        printf("\r\n");
                    }
                }
                
                //位置、水平、垂直精度因子(放大了10)
                printf("PDOP: %d.%d\r\n", fix_info.pdop / 10, fix_info.pdop % 10);
                printf("HDOP: %d.%d\r\n", fix_info.hdop / 10, fix_info.hdop % 10);
                printf("VDOP: %d.%d\r\n", fix_info.vdop / 10, fix_info.vdop % 10);
                
                //可见的gps,北斗卫星数量
                printf("Number of GPS visible satellite: %d\r\n", gps_satellite_info.satellite_num);
                printf("Number of Beidou visible satellite: %d\r\n", beidou_satellite_info.satellite_num);
                
                printf("\r\n");
            }
            else
            {
                //ATK-MO1218模块未定位时,不输出NMEA协议的GSV语句,
                //导致因获取不到可见GPS、北斗卫星的信息而超时失败,
                //此时可将函数atk_mo1218_update()的入参gps_satellite_info和beidou_satellite_info传入NULL,
                //从而获取未定位时的其它数据
                printf("Error!\r\n");
            }
            
            delay_ms(1000);
        }
    }
}

        值得一提的是,由于 ATK-MO1218 模块通过 UART 发送给主控芯片的数据的长度是不固定的,因此主控芯片就无法直接通过接收到数据的长度来判断 ATK-MO1218 模块传来的一帧数据是否完成。对于这种通过 UART 接收不定长数据的情况,可以通过 UART 总线是否空闲来判断一帧的传输是否完成,恰巧 STM32 的 UART 提供了总线空闲中断功能,因此可以开启 UART 的总线空闲中断,并在中断中做相应的处理。

void ATK_MO1218_UART_IRQHandler(void)
{
    uint8_t tmp;
    
    if (__HAL_UART_GET_FLAG(&g_uart_handle, UART_FLAG_ORE) != RESET)       
    {
        __HAL_UART_CLEAR_OREFLAG(&g_uart_handle);                           
        (void)g_uart_handle.Instance->SR;                                   
        (void)g_uart_handle.Instance->DR;
    }
    
    if (__HAL_UART_GET_FLAG(&g_uart_handle, UART_FLAG_RXNE) != RESET)       
    {
        HAL_UART_Receive(&g_uart_handle, &tmp, 1, HAL_MAX_DELAY);           
        
        if (g_uart_rx_frame.sta.len < (ATK_MO1218_UART_RX_BUF_SIZE - 1))    
                                                                            
                                                                             
        {
            g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = tmp;            
            g_uart_rx_frame.sta.len++;                                      
        }
        else                                                               
        {
            g_uart_rx_frame.sta.len = 0;                                 
            g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = tmp;             
            g_uart_rx_frame.sta.len++;                                    
        }
    }
    
    if (__HAL_UART_GET_FLAG(&g_uart_handle, UART_FLAG_IDLE) != RESET)       
    {
        g_uart_rx_frame.sta.finsh = 1;                                      
        
        __HAL_UART_CLEAR_IDLEFLAG(&g_uart_handle);                          
    }
}

二、硬件

        开发板使用GD32F450,串口使用USART5,引脚为PC6和PC7。杜邦线按设备要求进行连接即可。

      使用了四根杜邦线连接到开发板的电源3.3V,GND和PC6、PC7引脚。

三、软件

        NEMA协议是一种用于GPS设备的通信协议,它定义了一系列标准消息格式,用于传输GPS设备的位置、速度和时间等信息。在使用gd32进行NEMA协议解析时,可以采用以下步骤:

1、配置串口通信参数,如波特率、数据位、停止位和校验位等。

2、通过串口接收数据,每次接收到一条完整的NEMA协议消息后,进行解析。

3、解析消息头,判断消息类型和数据长度。

4、解析消息体,提取需要的位置、速度和时间等信息。

5、对解析出来的数据进行处理或者存储,以便后续使用。

        需要注意的是,NEMA协议中的消息格式比较复杂,解析过程需要仔细处理各个字段的含义和数据类型,避免出现解析错误的情况。同时,由于GPS设备的数据传输速率较快,需要采用合适的缓存机制,确保数据不被丢失或者重复解析。

参考代码:

#include "gd32f30x.h"
#include <stdio.h>
#include <string.h>


/* 定义串口接收缓冲区大小 */
#define RX_BUF_SIZE  128
/* 定义NEMA协议消息类型枚举 */
typedef enum {
    NEMA_MSG_GPGGA,   // GGA消息
    NEMA_MSG_GPVTG,   // VTG消息
    NEMA_MSG_GPGSA,   // GSA消息
    NEMA_MSG_GPGSV,   // GSV消息
    NEMA_MSG_UNKNOWN, // 未知消息
} nema_msg_type_t;
/* 定义NEMA协议消息结构体 */
typedef struct {
    nema_msg_type_t type;   // 消息类型
    uint32_t time;          // 时间
    float latitude;         // 纬度
    float longitude;        // 经度
    float altitude;         // 海拔高度
    float speed;            // 速度
    float course;           // 航向
    uint8_t num_satellites; // 卫星数
} nema_msg_t;


/* 定义全局变量 */
static uint8_t rx_buf[RX_BUF_SIZE];
static uint8_t rx_index = 0;
static nema_msg_t nema_msg;


/* 串口接收中断处理函数 */
void USART5_IRQHandler(void)
{
    if (RESET != usart_interrupt_flag_get(USART5, USART_INT_FLAG_RBNE)) {
        /* 读取接收数据寄存器 */
        uint8_t data = usart_data_receive(USART5);
        /* 判断是否接收到换行符 */
        if (data == '\n') {
            /* 解析NEMA协议消息 */
            if (0 == strncmp((const char *)rx_buf, "$GPGGA,", 7)) {
                /* GGA消息 */
                nema_msg.type = NEMA_MSG_GPGGA;
                /* 解析消息体 */
                sscanf((const char *)rx_buf, "$GPGGA,%lu,%f,%c,%f,%c,%d,%d,%f,%f,M,%f,M,,",
                    &nema_msg.time, &nema_msg.latitude, &latitude_dir, &nema_msg.longitude,
                    &longitude_dir, &nema_msg.fix_quality, &nema_msg.num_satellites,
                    &nema_msg.hdop, &nema_msg.altitude, &nema_msg.geoid_sep);
            } else if (0 == strncmp((const char *)rx_buf, "$GPVTG,", 7)) {
                /* VTG消息 */
                nema_msg.type = NEMA_MSG_GPVTG;
                /* 解析消息体 */
                sscanf((const char *)rx_buf, "$GPVTG,%f,T,%f,M,%f,N,%f,K",
                    &nema_msg.course, &nema_msg.course_true, &nema_msg.speed, &nema_msg.speed_knots);
            } else if (0 == strncmp((const char *)rx_buf, "$GPGSA,", 7)) {
                /* GSA消息 */
                nema_msg.type = NEMA_MSG_GPGSA;
                /* 解析消息体 */
                sscanf((const char *)rx_buf, "$GPGSA,%c,%d,%d,%d,%d,%d,%d,%d,%d,%f,%f,%f",
                    &nema_msg.fix_mode, &nema_msg.fix_type, &nema_msg.sat1, &nema_msg.sat2,
                    &nema_msg.sat3, &nema_msg.sat4, &nema_msg.sat5, &nema_msg.sat6,
                    &nema_msg.sat7, &nema_msg.pdop, &nema_msg.hdop, &nema_msg.vdop);
            } else if (0 == strncmp((const char *)rx_buf, "$GPGSV,", 7)) {
                /* GSV消息 */
                nema_msg.type = NEMA_MSG_GPGSV;
                /* 解析消息体 */
                sscanf((const char *)rx_buf, "$GPGSV,%d,%d,%d",
                    &nema_msg.num_msgs, &nema_msg.msg_num, &nema_msg.num_sats);
            } else {
                /* 未知消息 */
                nema_msg.type = NEMA_MSG_UNKNOWN;
            }
            /* 清空接收缓冲区 */
            memset(rx_buf, 0, sizeof(rx_buf));
            rx_index = 0;
        } else {
            /* 累加接收缓冲区 */
            rx_buf[rx_index++] = data;
        }
    }
}


int main(void)
{
    /* 配置串口通信参数 */
    usart_deinit(USART5);
    usart_baudrate_set(USART5, 38400);
    usart_word_length_set(USART5, USART_WL_8BIT);
    usart_stop_bit_set(USART5, USART_STB_1BIT);
    usart_parity_config(USART5, USART_PM_NONE);
    usart_receive_config(USART5, USART_RECEIVE_ENABLE);
    usart_interrupt_enable(USART5, USART_INT_RBNE);
    nvic_enable_irq(NVIC_USART5_IRQ);
    usart_enable(USART5);


    /* 解析NEMA协议消息 */
    while (1) {
        if (nema_msg.type != NEMA_MSG_UNKNOWN) {
            /* 处理解析出来的数据 */
            // ...
            /* 清空消息结构体 */
            memset(&nema_msg, 0, sizeof(nema_msg));
        }
    }
}

实际移植了正点原子的例程到gd32的开发板,测试如下:

f304fc5ea73f23cea23eb6185436d2a9.png

获取打印的是utc时间,utc时间是8点56, 加上8则为本地时间,本地时间时16点56,和实际一致符合预期。

个人公众号:嵌入式学习与实践

28ca10adad322f0efa1fe553c860b683.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/600495.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mac host学习

参考&#xff1a; SSH中known_hosts文件作用和常见问题及解决方法 https://blog.csdn.net/luduoyuan/article/details/130070120在 Mac 上更改 DNS 设置 https://support.apple.com/zh-cn/guide/mac-help/mh14127/mac mac中有时候你输入的域名&#xff0c;但会跳转到与期望ip不…

Arduino UNO用L9110 电机驱动模块驱动两个直流电机

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、简介二、工作原理三、使用前准备四、测试方法五、实验现象 一、简介 L9110电机驱动模块采用推挽式功率放大&#xff0c;设有固定安装孔&#xff0c;适合组装&a…

Linux常用命令——grub命令

在线Linux命令查询工具 grub 多重引导程序grub的命令行shell工具 补充说明 grub命令是多重引导程序grub的命令行shell工具。 语法 grub(选项)选项 --batch&#xff1a;打开批处理模式&#xff1b; --boot-drive<驱动器>&#xff1a;指定stage2的引导驱动器&#x…

霸榜第一框架:工业检测,基于差异和共性的半监督方法用于图像表面缺陷检测...

关注并星标 从此不迷路 计算机视觉研究院 公众号ID&#xff5c;ComputerVisionGzq 学习群&#xff5c;扫码在主页获取加入方式 论文地址&#xff1a;https://arxiv.org/ftp/arxiv/papers/2205/2205.00908.pdf 链接: https://pan.baidu.com/s/1ar2BN1p2jJ-cZx1J5dGRLg 密码: 2l…

Learn From Microsoft Build Ⅲ:低代码

点击蓝字 关注我们 编辑&#xff1a;Alan Wang 排版&#xff1a;Rani Sun 微软 Reactor 为帮助广开发者&#xff0c;技术爱好者&#xff0c;更好的学习 .NET Core, C#, Python&#xff0c;数据科学&#xff0c;机器学习&#xff0c;AI&#xff0c;区块链, IoT 等技术&#xff0…

使用神经网络合成数据生成技术实现电力系统无人机自动巡检

使用神经网络合成数据生成技术实现电力系统无人机自动巡检 美国能源公司 Exelon 正在利用神经网络合成数据生成技术&#xff0c;为电力系统无人机自动巡检项目提供支持。这一技术有助于提高巡检效率和准确性&#xff0c;降低人力和时间成本。 1. 电力系统巡检的挑战 电力系统…

基于知识图谱表示学习的谣言早期检测方法

源自&#xff1a;电子学报 作者&#xff1a;皮德常 吴致远 曹建军 摘 要 社交网络谣言是严重危害社会安全的一个重要问题.目前的谣言检测方法基本上都依赖用户评论数据.为了获取可供模型训练的足量评论数据&#xff0c;需要任由谣言在社交平台上传播一段时间&#xff0c;这…

手机安卓Termux安装MySQL数据库【公网远程数据库】

文章目录 前言1.安装MariaDB2.安装cpolar内网穿透工具3. 创建安全隧道映射mysql4. 公网远程连接5. 固定远程连接地址 转载自cpolar极点云的文章&#xff1a;Android Termux安装MySQL数据库 | 公网安全远程连接【Cpolar内网穿透】 前言 Android作为移动设备&#xff0c;尽管最初…

Android 和 ktor 的 HTTP 块请求

Android 和 ktor 的 HTTP 块请求 在这篇非常短的文章中&#xff0c;我将简要解释什么是块或流式 HTTP 请求&#xff0c;使用它有什么好处&#xff0c;以及它在 Android 中的工作原理。 Android 应用程序使用 HTTP 请求从后端下载数据。此信息在应用程序上存储和处理以使其正常…

计算机内存取证之BitLocker恢复密钥提取还原

BitLocker是微软Windows自带的用于加密磁盘分卷的技术。 通常&#xff0c;解开后的加密卷通过Windows自带的命令工具“manage-bde”可以查看其恢复密钥串&#xff0c;如下图所示&#xff1a; 如图&#xff0c;这里的数字密码下面的一长串字符串即是下面要提取恢复密钥。 在计…

chatgpt赋能python:Python编程教程之抽签程序

Python编程教程之抽签程序 介绍 对于喜欢玩抽签、体育彩票等游戏的人来说&#xff0c;抽签程序是一款非常有用的小工具。抽签程序可以用来随机抽取一定数量的幸运儿&#xff0c;而且运行速度快&#xff0c;结果随机性高&#xff0c;不需要人工干预。 那么&#xff0c;Python…

Spring Boot 3.1 中如何整合Spring Security和Keycloak

在今年2月14日的时候&#xff0c;Keycloak 团队宣布他们正在弃用大多数 Keycloak 适配器。其中包括Spring Security和Spring Boot的适配器&#xff0c;这意味着今后Keycloak团队将不再提供针对Spring Security和Spring Boot的集成方案。 但是&#xff0c;如此强大的Keycloak&am…

一文搞懂Android动画

这里写目录标题 前言一、视图动画1. 补间动画---Animation抽象类动画1.1 AlphaAnimation&#xff1a;控制一个对象透明度的动画。1.1.1 xml实现示例1.1.2 java实现示例 1.2 RotateAnimation&#xff1a;控制一个对象旋转的动画。1.1.1 xml实现示例1.1.2 java实现示例 1.3 Scale…

Linux中查看端口被哪个进程占用、进程调用的配置文件、目录等

1.查看被占用的端口的进程&#xff0c;netstat/ss -antulp | grep :端口号 2.通过上面的命令就可以列出&#xff0c;这个端口被哪些应用程序所占用&#xff0c;然后找到对应的进程PID 3.根据PID查询进程。如果想详细查看这个进程&#xff0c;PID具体是哪一个进程&#xff0c;可…

Qt6.5.1+WebRTC学习笔记(八)开发环境搭建(win10+vs2019)

一、准备 1.操作系统win10 64位 2.合理的上网方式&#xff0c;需要正常访问google,最好有20G以上流量 3.安装VS2019&#xff0c;笔者使用的是社区版&#xff0c;并选中C相关&#xff0c;笔者设置如下 注意&#xff0c;win10的sdk需要是10.0.20348.1&#xff0c;其他版本可能…

JavaWeb17(过滤器监听器)

目录 1.过滤器/拦截器-Filter. 1.1过滤器介绍. 1.2过滤器使用步骤. 1.2.1创建过滤器. 1.2.2映射配置. 1.3案例1-编码设置. 1.4案例2-登陆状态验证. 1.4过滤链-略带. 2.监听器-Listener. 2.1监听器介绍. 2.2案例演示1-统计在线人数. 2.3案例演示2-统计历史在线人数.…

太好玩了!用Python做音乐节奏可视化!

大家注意&#xff1a;因为微信最近又改了推送机制&#xff0c;经常有小伙伴说错过了之前被删的文章&#xff0c;比如前阵子冒着风险写的爬虫&#xff0c;再比如一些限时福利&#xff0c;错过了就是错过了。 所以建议大家加个星标&#xff0c;就能第一时间收到推送。&#x1f44…

【Python C扩展】零基础也能轻松掌握的学习路线与参考资料

Python C扩展是将Python语言与C语言相结合&#xff0c;最大限度地利用C语言的高性能和Python语言的灵活性&#xff0c;使Python程序获得更高的运行效率。Python C扩展的学习路线涵盖了C语言基础、Python语言、Python C API等多个方面。下面将详细介绍Python C扩展的学习路线&am…

Python+Yolov5人脸表情特征识别

程序示例精选 PythonYolov5人脸表情特征识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<PythonYolov5人脸表情特征识别>>编写代码&#xff0c;代码整洁&#xff0c;规则&am…

【微服务架构】专家组:在过去十年的微服务中,我们学到了什么?

瓦特&#xff08;Watt&#xff09;&#xff1a;这是微服务专家组。Chris早些时候谈到了最小化微服务中的设计时耦合。他是microservices.io的创建者&#xff0c;《微服务模式》一书的作者。他也是Java冠军&#xff0c;在微服务领域非常有经验。我期待着与你们一起深入研究其中的…