| 一、官方代码分析 
 正点原子的官方测试例程,测试代码的逻辑还是比较简单的,主要就是先调用函数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
 
 
 |