GD32開発ボードに基づくGPS測位モジュールの動作

前の章の紹介に基づいて、この章では gd32 開発ボードに基づく gps 測位モジュールの使用方法を紹介します。

1. 公式コード解析

    時間厳守アトムの公式テスト ルーチン、テスト コードのロジックは比較的単純です。主なことは、関数 atk_mo1218_init() を呼び出して初期化してから、SkyTraq バイナリ プロトコルの 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);                          
    }
}

2. ハードウェア

        開発ボードは GD32F450 を使用し、シリアル ポートは USART5 を使用し、ピンは PC6 と PC7 です。機器の要件に応じて DuPont ラインを接続できます。

      4 本の Dupont ワイヤを使用して、開発ボードの電源 3.3V、GND、および PC6、PC7 ピンに接続します。
 

3. ソフトウェア

        NEMA プロトコルは、GPS デバイスの通信プロトコルであり、GPS デバイスの位置、速度、時間などの情報を送信するための一連の標準メッセージ形式を定義します。NEMA プロトコル分析に gd32 を使用する場合は、次の手順を実行できます。

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

おすすめ

転載: blog.csdn.net/weixin_46158019/article/details/130998729