打印
[研电赛技术支持]

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

[复制链接]
517|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
aoyi|  楼主 | 2023-7-19 10:48 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
一、官方代码分析

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



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的开发板,测试如下:



获取打印的是utc时间,utc时间是8点56, 加上8则为本地时间,本地时间时16点56,和实际一致符合预期。
————————————————
版权声明:本文为CSDN博主「嵌入式学习和实践」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_46158019/article/details/130998729

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

100

主题

3306

帖子

3

粉丝