[其他] 移植NMEA协议库解析GGA数据格式

[复制链接]
3895|24
 楼主| 鱿鱼丝 发表于 2023-6-28 14:19 | 显示全部楼层 |阅读模式
模块介绍
ATGM336H-5N系列模块是小尺寸的高性能BDS/GNSS全星座定位导航模块系列的总称。

该系列模块产品都是基于中科微第四代低功耗GNSS SOC单芯片—AT6558
支持多种卫星导航系统,包括中国的BDS(北斗卫星导航系统),美国的GPS,俄罗斯的GLONASS,欧盟的GALILEO,日本的QZSS 以及卫星增强系统SBAS ( WAAS,EGNOS,GAGAN,MSAS )。

AT6558是一款真正意义的六合一多模卫星导航定位芯片,包含32个跟踪通道,可以同时接收六个卫星导航系统的GNSS信号,并且实现联合定位导航与授时。
2330649bd1077aa44.png

 楼主| 鱿鱼丝 发表于 2023-6-28 14:20 | 显示全部楼层
二、模块引脚说明及模块资源
引脚说明:
VCC :电源线,正常电压范围为: 3.3~5V
GND:地线
TXD: 串口数据发送信号线,使用 TTL 电平
RXD: 串口数据接收信号线,使用 TTL 电平
PPS: 时间脉冲信号线,模块接收到 GPS 时间信息后,输出可调节的脉冲信号,默认为 1Hz,脉冲上升沿与 UTC 时间对齐

资源描述:
XH414 法拉电容:
参数为: 3.3V 0.07F。它的功能和锂电池一样,在主电源掉电的时候可以为定位模块的 RTC 部分供电,以使定位模块在下次启动时能快速搜索到卫星,一般可持续供电 1 小时。

有源天线 IPX接口: IPX 接口用于连接有源天线

时间脉冲指示灯:
模块上电后,时间脉冲指示灯即亮,在定位模块接收到时间信息后,时间脉冲信号指示灯会默认以 1Hz 的频率闪烁,该信号频率可以调节。
 楼主| 鱿鱼丝 发表于 2023-6-28 14:24 | 显示全部楼层
三、NMEA-0183 协议
NMEA-0183 是一套定义接收机输出的标准信息,有几种不同的格式,每种都是独立相关的 ASCII 格式, 使用逗号隔开数据,数据流长度从 30-100 字符不等,通常以每秒间隔选择输出。

最常用的格式为"GGA",它包含了定位时间,纬度,经度,高度,定位所用的卫星数, DOP 值, 差分状态和校正时段等,其他的有速度,跟踪,日期等。 NMEA 实际上已成为所有的定位接收机中最通用的数据输出格式。
 楼主| 鱿鱼丝 发表于 2023-6-28 14:26 | 显示全部楼层
3-1协议框架
96908649bd27ad81f1.png
 楼主| 鱿鱼丝 发表于 2023-6-28 14:26 | 显示全部楼层
NMEA 语句的数据段为信息主体,不同类型的语句用于传输不同类型的定位信息, 其语句类型又分为两部分, 如 GNZDA 前面两个字符 GN 用于区分定位系统 17608649bd28b1c089.png 其中 GN 标识符比较特殊,当发送器具有多模功能时(即同时支持一个以上的定位系统),系统会把各系统的信息整合、 处理后,再把这些综合信息采用 GN 作为标识符发送出来,如前面的时间日期信息, 使用 GNZDA 语句, 在这样的系统中, GP、 BD 等标识符仅用于表示对应系统的卫星信息,如 GPGSA 和 BDGSA 语句分别用于表示美国 GPS 系统和北斗系统的卫星信息。
 楼主| 鱿鱼丝 发表于 2023-6-28 14:26 | 显示全部楼层
NMEA-0183 协议定义的语句非常多,但是常用的或者说兼容性最广的语句只有 GGA、RMC、 VTG、 GLL、 ZDA、 GSA、 GSV 等。下面给出这些常用 NMEA-0183 语句的字段定义解释
79772649bd2a8749b0.png
 楼主| 鱿鱼丝 发表于 2023-6-28 14:27 | 显示全部楼层
3-2 协议帧格式说明:
该协议采用 ASCII 码。 帧格式形如: $ aaccc,ddd,ddd,…,ddd * hh < C R >< LF>
<1> “$”——帧命令起始位
<2> aaccc——地址域,前两位为识别符,后三位为语句名
<3> ddd…ddd——数据
<4> “ * ”——校验和前缀
<5> hh——校验和(check sum), $与*之间所有字符 ASCII 码的校验和(各字节做异或运算,得到校验和后,再转换 16 进制格式的 ASCII 字符。)
<6> < CR>< LF>——CR(Carriage Return) + LF(Line Feed)帧结束,回车和换行
 楼主| 鱿鱼丝 发表于 2023-6-28 14:27 | 显示全部楼层
3-3 GGA数据格式
GPS 固定数据输出语句(Global positioning system fix data)。

格式:$GNGGA,<1>,<2>,<3>,<4>,<5>,<6>,<7>,<8>,<9>,<10>,<11>,<12>,<13>,<14>*<15>< CR>< LF >

$GNGGA,012842.000,2253.7220,N,11350.7025,E,1,11,1.5,44.8,M,0.0,M,*44

<1> UTC 时间,格式为 hhmmss.sss
<2> 纬度,格式为 ddmm.mmmm(前导位数不足则补 0)
<3> 纬度半球, N 或 S(北纬或南纬)
<4> 经度,格式为 dddmm.mmmm(前导位数不足则补 0)
<5> 经度半球, E 或 W(东经或西经)
<6> 定位质量指示, 0=定位无效, 1=标准定位, 2=差分定位, 6=估算
<7> 使用卫星数量,从 00 到 12(前导位数不足则补 0)
<8> 水平精确度, 0.5 到 99.9
<9> 天线离海平面的高度, -9999.9 到 9999.9 米
<10> 高度单位, M 表示单位米
<11> 大地椭球面相对海平面的高度(-999.9 到 9999.9)
<12> 高度单位, M 表示单位米
<13> 差分 GPS 数据期限(RTCM SC-104),最后设立 RTCM 传送的秒数量
<14> 差分参考基站标号,从 0000 到 1023(前导位数不足则补 0)
<15> 校验和。

其他数据格式这里就不再介绍
 楼主| 鱿鱼丝 发表于 2023-6-28 14:27 | 显示全部楼层
四、NMEA库移植过程
NMEA解码库使用纯 C 语言编写,支持解析 GPGGA,GPGSA ,GPGSV, GPRMC, GPVTG 这五种语句(这五种语句已经提供足够多的 GPS 信息),

解析得的 GPS 数据信息以结构体存储,附加了地理学相关功能,可支持导航等数据工作。

将nmea_decode文件夹复制到工程目录之下 57479649bd2ce1e3c3.png
 楼主| 鱿鱼丝 发表于 2023-6-28 14:27 | 显示全部楼层
并在keil的设置添加文件路径: 50611649bd2dc5f01e.png
 楼主| 鱿鱼丝 发表于 2023-6-28 14:27 | 显示全部楼层
将库文件下的.c文件添加到工程中 54817649bd2ea7fe64.png
 楼主| 鱿鱼丝 发表于 2023-6-28 14:28 | 显示全部楼层
五、MM32配置代码
GPS在传输数据的时候是串口接收的,因此大量的数据在串口传输时候,如果使用mcu来进行循环处理,这将大大降低CPU的效率

因此这里选择串口dma,硬件数据传输,但是网上关于灵动单片机的dma配置资源比较少,这里我将我的代码配置出来。
 楼主| 鱿鱼丝 发表于 2023-6-28 14:28 | 显示全部楼层
5-1 GPS接口初始化
主要包括串口初始化和串口dma配置
///**
//  * @brief  GPS_Config gps 初始化
//  * @param  无
//  * @retval 无
//  */
void GPS_Config(void)
{
  GPS_USART_Config();
  GPS_DMA_Config();   
}
 楼主| 鱿鱼丝 发表于 2023-6-28 14:28 | 显示全部楼层
GPS_USART_Config函数
主要是对mm32 与定位模块连接的 USART 串口外设作了基本的初始化,除了要注意把波特率配置为 9600,其它跟普通串口配置无异。
  1. /*
  2. * 函数名:GPS_USART_Config
  3. * 描述  :USART GPIO 配置,工作模式配置
  4. * 输入  :无
  5. * 输出  : 无
  6. * 调用  :外部调用
  7. */
  8. static void GPS_USART_Config(void)
  9. {       
  10.         uart_init(GPS_UART, GPS_USART_BAUDRATE, GPS_USART_RX, GPS_USART_TX);
  11.         uart_rx_irq(GPS_UART, 1);
  12. }
 楼主| 鱿鱼丝 发表于 2023-6-28 14:28 | 显示全部楼层
GPS_DMA_Config 函数
MM32DMA相关的宏定义:这部分可以查阅芯片手册得到:
  1. //DMA
  2. #define GPS_USART_DMA_STREAM            DMA2_Channel3
  3. #define GPS_DMA_IRQn                     DMA2_Channel3_IRQn         //GPS中断源
  4. #define GPS_USART_DMA_CLK                RCC_AHBENR_DMA2
  5. #define GPS_USART_DMA_CHANNEL            DMA_Channel_3

  6. /* 外设标志 */
  7. #define GPS_DMA_IT_HT               DMA2_IT_HT3 //DMA_IT_HTIF5
  8. #define GPS_DMA_IT_TC               DMA2_IT_TC3 //DMA_IT_TCIF5

  9. #define UART_DMAReq_Rx                      ((uint16_t)0x0040)
  10. /* 中断函数 */
  11. #define GPS_DMA_IRQHANDLER           DMA2_Channel3_IRQHandler   //GPS使用的DMA中断服务函数

 楼主| 鱿鱼丝 发表于 2023-6-28 14:31 | 显示全部楼层
串口接收缓冲区地址宏定义
  1. #define GPS_DATA_ADDR             (u32)&UART4->RDR //GPS_DR_Base        //GPS使用的串口的数据寄存器地址
  2. #define GPS_RBUFF_SIZE            512                   //串口接收缓冲区大小
  3. #define HALF_GPS_RBUFF_SIZE       (GPS_RBUFF_SIZE/2)    //串口接收缓冲区一半  
 楼主| 鱿鱼丝 发表于 2023-6-28 14:31 | 显示全部楼层
重点在串口 DMA 的配置,代码如下
  1. /**
  2.   * @brief  GPS_Interrupt_Config 配置GPS使用的DMA中断
  3.   * @param  None.
  4.   * @retval None.
  5.   */
  6. static void GPS_Interrupt_Config(void)
  7. {
  8.         exNVIC_Init_TypeDef  NVIC_InitStruct;
  9.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

  10.     NVIC_InitStruct.NVIC_IRQChannel = GPS_DMA_IRQn;
  11.     NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
  12.     NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
  13.     NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
  14.        
  15.     exNVIC_Init(&NVIC_InitStruct);
  16. }
  17. /**
  18.   * @brief  GPS_DMA_Config gps dma接收配置
  19.   * @param  无
  20.   * @retval 无
  21.   */
  22. static void GPS_DMA_Config(void)
  23. {
  24.        
  25.         DMA_InitTypeDef DMA_InitStructure;

  26.         /*开启DMA时钟*/
  27.         RCC_AHBPeriphClockCmd(GPS_USART_DMA_CLK, ENABLE);

  28.         /* 复位初始化DMA数据流 */
  29.         DMA_DeInit(GPS_USART_DMA_STREAM);
  30.         DMA_StructInit(&DMA_InitStructure);

  31.         /*设置DMA源:串口数据寄存器地址*/
  32.         DMA_InitStructure.DMA_PeripheralBaseAddr = GPS_DATA_ADDR;         
  33.         /*内存地址(要传输的变量的指针)*/
  34.         DMA_InitStructure.DMA_MemoryBaseAddr = (u32)gps_rbuff;
  35.         /*方向:从外设到内存*/               
  36.         DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;       
  37.         /*传输大小DMA_BufferSize=SENDBUFF_SIZE*/       
  38.         DMA_InitStructure.DMA_BufferSize = GPS_RBUFF_SIZE;
  39.         /*外设地址不增*/            
  40.         DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  41.         /*内存地址自增*/
  42.         DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;       
  43.         /*外设数据单位*/       
  44.         DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  45.         /*内存数据单位 8bit*/
  46.         DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;       
  47.         /*DMA模式:不断循环*/
  48.         DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;         
  49.         /*优先级:中*/       
  50.         DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;   
  51.                        
  52.         //M2M mode is disabled
  53.         DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                                                                                                // 非内存到内存模式
  54.         DMA_InitStructure.DMA_Auto_reload = DMA_Auto_Reload_Enable;
  55.         /*配置DMA2的数据流3*/                  
  56.         DMA_Init(GPS_USART_DMA_STREAM, &DMA_InitStructure);
  57.        
  58.         // Enable UARTy_DMA1_Channel Transfer complete interrupt
  59.         DMA_ITConfig(GPS_USART_DMA_STREAM, DMA_IT_HT|DMA_IT_TC, ENABLE);
  60.         /* 配置串口 向 DMA发出请求 */
  61.         UART_DMACmd(UART4, UART_DMAReq_EN, ENABLE);
  62.         /*使能DMA*/
  63.         DMA_Cmd(GPS_USART_DMA_STREAM, ENABLE);

  64.         /*配置中断优先级*/
  65.         GPS_Interrupt_Config();
  66. }
 楼主| 鱿鱼丝 发表于 2023-6-28 14:32 | 显示全部楼层
GPS_DMA_Config 函数主要工作如下: 设置了外设地址为 USART 的数据寄存器,并把数据传输方向设置为从USART 数据寄存器传输到内存变量 gps_rbuff 中,该缓冲区数组大小为 512 字节。
 楼主| 鱿鱼丝 发表于 2023-6-28 14:34 | 显示全部楼层
DMA2_Channel3_IRQHandler中断函数
最关键的位置它设置了 DMA 半传输结束中断及全传输结束中断,

//配置 DMA 发送完成后产生中断
DMA_ITConfig(GPS_USART_DMA_STREAM,DMA_IT_HT|DMA_IT_TC,ENABLE);
1
2
所以它实际把缓冲区分为成了大小相等的 A/B 两部分,每次 DMA 接收了半个缓冲区大小的数据时(本程序为256 字节),就会引起中断得益于这个机制。

可以设计程序当 DMA 使用缓冲区 A 存储数据时,控制 CPU 使用 B中的数据进行 GPS 解码,当 DMA 使用 B 存储时,控制 CPU 使用 A 进行解码,只要缓冲区的大小设置合适,即可避免前面说到的数据丢失问题,这种处理方式也称“乒乓缓冲”,得名于它像打乒乓球一样,你来我往。
 楼主| 鱿鱼丝 发表于 2023-6-28 14:35 | 显示全部楼层
当 DMA 的 半 传 输 中 断 或 全 传 输 中 断 产 生 时 , 进 入 的 中 断 服 务 函 数 调 用 了DMA2_Channel3_IRQHandler函数

void DMA2_Channel3_IRQHandler(void)
{
if(DMA_GetITStatus(GPS_DMA_IT_HT) )         /* DMA 半传输完成 */
  {
    GPS_HalfTransferEnd = 1;                //设置半传输完成标志位
    DMA_ClearITPendingBit (GPS_DMA_IT_HT);
  }
  else if(DMA_GetITStatus(GPS_DMA_IT_TC))     /* DMA 传输完成 */
  {
    GPS_TransferEnd = 1;                    //设置传输完成标志位
    DMA_ClearITPendingBit(GPS_DMA_IT_TC);

   }
}

在 这 个 函 数 处 理 中 , 主 要 是 在 半 传 输 和 全 传 输 结 束 引 起 中 断 时 , 对GPS_HalfTransferEnd 和 GPS_TransferEnd 标志位进行标记, 在解码流程中根据这两个标志使用不同的缓冲区进行处理。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

47

主题

480

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部