【灵动MM32-DMA传输-GPS解算】 移植NMEA协议库解析GGA数据格式
靈動MM32單片機移植NMEA協(xié)議庫解算GGA數(shù)據格式通過串口dma硬件傳輸
今天使用一款常見的gps模塊,goouuu果云GPS模塊,這款產品可以說是便宜好用,但是這個原版本的例程我覺得不太行,解析庫的時候太浪費資源,而且兼容性不好。所以我就用靈動mm32在果云GPS模塊上移植野火的開發(fā)的GPS庫文件
用到的硬件和庫文件:
①MM32F3277G9P單片機 ②goouuu果云GPS模塊模塊 ③智能車逐飛MM32開源庫 ④野火開源庫
逐飛開源庫鏈接: 逐飛科技MM32F327X_G9P開源庫
野火GPS模塊資料鏈接
- 靈動MM32單片機移植NMEA協(xié)議庫解算GGA數(shù)據格式通過串口dma硬件傳輸
- 一、模塊介紹
- 二、模塊引腳說明及模塊資源
- 三、NMEA-0183 協(xié)議
- 3-1協(xié)議框架
- 3-2 協(xié)議幀格式說明:
- 3-3 GGA數(shù)據格式
- 四、NMEA庫移植過程
- 五、MM32配置代碼
- 5-1 GPS接口初始化
- GPS_USART_Config函數(shù)
- GPS_DMA_Config 函數(shù)
- DMA2_Channel3_IRQHandler中斷函數(shù)
- 5-2 解碼測試函數(shù)nmea_decode_test()
- 5-3 MM32堆??臻g設置
- 六、主函數(shù)調用
一、模塊介紹
ATGM336H-5N系列模塊是小尺寸的高性能BDS/GNSS全星座定位導航模塊系列的總稱。
該系列模塊產品都是基于中科微第四代低功耗GNSS SOC單芯片—AT6558
支持多種衛(wèi)星導航系統(tǒng),包括中國的BDS(北斗衛(wèi)星導航系統(tǒng)),美國的GPS,俄羅斯的GLONASS,歐盟的GALILEO,日本的QZSS 以及衛(wèi)星增強系統(tǒng)SBAS ( WAAS,EGNOS,GAGAN,MSAS )。
AT6558是一款真正意義的六合一多模衛(wèi)星導航定位芯片,包含32個跟蹤通道,可以同時接收六個衛(wèi)星導航系統(tǒng)的GNSS信號,并且實現(xiàn)聯(lián)合定位導航與授時。
二、模塊引腳說明及模塊資源
引腳說明:
VCC :電源線,正常電壓范圍為: 3.3~5V
GND:地線
TXD: 串口數(shù)據發(fā)送信號線,使用 TTL 電平
RXD: 串口數(shù)據接收信號線,使用 TTL 電平
PPS: 時間脈沖信號線,模塊接收到 GPS 時間信息后,輸出可調節(jié)的脈沖信號,默認為 1Hz,脈沖上升沿與 UTC 時間對齊
資源描述:
XH414 法拉電容:
參數(shù)為: 3.3V 0.07F。它的功能和鋰電池一樣,在主電源掉電的時候可以為定位模塊的 RTC 部分供電,以使定位模塊在下次啟動時能快速搜索到衛(wèi)星,一般可持續(xù)供電 1 小時。
有源天線 IPX接口: IPX 接口用于連接有源天線
時間脈沖指示燈:
模塊上電后,時間脈沖指示燈即亮,在定位模塊接收到時間信息后,時間脈沖信號指示燈會默認以 1Hz 的頻率閃爍,該信號頻率可以調節(jié)。
三、NMEA-0183 協(xié)議
NMEA-0183 是一套定義接收機輸出的標準信息,有幾種不同的格式,每種都是獨立相關的 ASCII 格式, 使用逗號隔開數(shù)據,數(shù)據流長度從 30-100 字符不等,通常以每秒間隔選擇輸出。
最常用的格式為"GGA",它包含了定位時間,緯度,經度,高度,定位所用的衛(wèi)星數(shù), DOP 值, 差分狀態(tài)和校正時段等,其他的有速度,跟蹤,日期等。 NMEA 實際上已成為所有的定位接收機中最通用的數(shù)據輸出格式。
3-1協(xié)議框架
NMEA 語句的數(shù)據段為信息主體,不同類型的語句用于傳輸不同類型的定位信息, 其語句類型又分為兩部分, 如 GNZDA 前面兩個字符 GN 用于區(qū)分定位系統(tǒng)
其中 GN 標識符比較特殊,當發(fā)送器具有多模功能時(即同時支持一個以上的定位系統(tǒng)),系統(tǒng)會把各系統(tǒng)的信息整合、 處理后,再把這些綜合信息采用 GN 作為標識符發(fā)送出來,如前面的時間日期信息, 使用 GNZDA 語句, 在這樣的系統(tǒng)中, GP、 BD 等標識符僅用于表示對應系統(tǒng)的衛(wèi)星信息,如 GPGSA 和 BDGSA 語句分別用于表示美國 GPS 系統(tǒng)和北斗系統(tǒng)的衛(wèi)星信息。
NMEA-0183 協(xié)議定義的語句非常多,但是常用的或者說兼容性最廣的語句只有 GGA、RMC、 VTG、 GLL、 ZDA、 GSA、 GSV 等。下面給出這些常用 NMEA-0183 語句的字段定義解釋
3-2 協(xié)議幀格式說明:
該協(xié)議采用 ASCII 碼。 幀格式形如: $ aaccc,ddd,ddd,…,ddd * hh < C R >< LF>
<1> “$”——幀命令起始位
<2> aaccc——地址域,前兩位為識別符,后三位為語句名
<3> ddd…ddd——數(shù)據
<4> “ * ”——校驗和前綴
<5> hh——校驗和(check sum), $與*之間所有字符 ASCII 碼的校驗和(各字節(jié)做異或運算,得到校驗和后,再轉換 16 進制格式的 ASCII 字符。)
<6> < CR>< LF>——CR(Carriage Return) + LF(Line Feed)幀結束,回車和換行
3-3 GGA數(shù)據格式
GPS 固定數(shù)據輸出語句(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(前導位數(shù)不足則補 0)
<3> 緯度半球, N 或 S(北緯或南緯)
<4> 經度,格式為 dddmm.mmmm(前導位數(shù)不足則補 0)
<5> 經度半球, E 或 W(東經或西經)
<6> 定位質量指示, 0=定位無效, 1=標準定位, 2=差分定位, 6=估算
<7> 使用衛(wèi)星數(shù)量,從 00 到 12(前導位數(shù)不足則補 0)
<8> 水平精確度, 0.5 到 99.9
<9> 天線離海平面的高度, -9999.9 到 9999.9 米
<10> 高度單位, M 表示單位米
<11> 大地橢球面相對海平面的高度(-999.9 到 9999.9)
<12> 高度單位, M 表示單位米
<13> 差分 GPS 數(shù)據期限(RTCM SC-104),最后設立 RTCM 傳送的秒數(shù)量
<14> 差分參考基站標號,從 0000 到 1023(前導位數(shù)不足則補 0)
<15> 校驗和。
其他數(shù)據格式這里就不再介紹
四、NMEA庫移植過程
NMEA解碼庫使用純 C 語言編寫,支持解析 GPGGA,GPGSA ,GPGSV, GPRMC, GPVTG 這五種語句(這五種語句已經提供足夠多的 GPS 信息),
解析得的 GPS 數(shù)據信息以結構體存儲,附加了地理學相關功能,可支持導航等數(shù)據工作。
將nmea_decode文件夾復制到工程目錄之下
并在keil的設置添加文件路徑:
將庫文件下的.c文件添加到工程中
五、MM32配置代碼
GPS在傳輸數(shù)據的時候是串口接收的,因此大量的數(shù)據在串口傳輸時候,如果使用mcu來進行循環(huán)處理,這將大大降低CPU的效率
因此這里選擇串口dma,硬件數(shù)據傳輸,但是網上關于靈動單片機的dma配置資源比較少,這里我將我的代碼配置出來。
5-1 GPS接口初始化
主要包括串口初始化和串口dma配置
///** // * @brief GPS_Config gps 初始化 // * @param 無 // * @retval 無 // */ void GPS_Config(void) {GPS_USART_Config();GPS_DMA_Config(); }GPS_USART_Config函數(shù)
主要是對mm32 與定位模塊連接的 USART 串口外設作了基本的初始化,除了要注意把波特率配置為 9600,其它跟普通串口配置無異。
/** 函數(shù)名:GPS_USART_Config* 描述 :USART GPIO 配置,工作模式配置* 輸入 :無* 輸出 : 無* 調用 :外部調用*/ static void GPS_USART_Config(void) { uart_init(GPS_UART, GPS_USART_BAUDRATE, GPS_USART_RX, GPS_USART_TX);uart_rx_irq(GPS_UART, 1); }GPS_DMA_Config 函數(shù)
MM32DMA相關的宏定義:這部分可以查閱芯片手冊得到:
//DMA #define GPS_USART_DMA_STREAM DMA2_Channel3 #define GPS_DMA_IRQn DMA2_Channel3_IRQn //GPS中斷源 #define GPS_USART_DMA_CLK RCC_AHBENR_DMA2 #define GPS_USART_DMA_CHANNEL DMA_Channel_3/* 外設標志 */ #define GPS_DMA_IT_HT DMA2_IT_HT3 //DMA_IT_HTIF5 #define GPS_DMA_IT_TC DMA2_IT_TC3 //DMA_IT_TCIF5#define UART_DMAReq_Rx ((uint16_t)0x0040) /* 中斷函數(shù) */ #define GPS_DMA_IRQHANDLER DMA2_Channel3_IRQHandler //GPS使用的DMA中斷服務函數(shù)串口接收緩沖區(qū)地址宏定義
#define GPS_DATA_ADDR (u32)&UART4->RDR //GPS_DR_Base //GPS使用的串口的數(shù)據寄存器地址 #define GPS_RBUFF_SIZE 512 //串口接收緩沖區(qū)大小 #define HALF_GPS_RBUFF_SIZE (GPS_RBUFF_SIZE/2) //串口接收緩沖區(qū)一半重點在串口 DMA 的配置,代碼如下
/*** @brief GPS_Interrupt_Config 配置GPS使用的DMA中斷 * @param None.* @retval None.*/ static void GPS_Interrupt_Config(void) {exNVIC_Init_TypeDef NVIC_InitStruct;NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitStruct.NVIC_IRQChannel = GPS_DMA_IRQn;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;exNVIC_Init(&NVIC_InitStruct); } /*** @brief GPS_DMA_Config gps dma接收配置* @param 無* @retval 無*/ static void GPS_DMA_Config(void) {DMA_InitTypeDef DMA_InitStructure;/*開啟DMA時鐘*/ RCC_AHBPeriphClockCmd(GPS_USART_DMA_CLK, ENABLE); /* 復位初始化DMA數(shù)據流 */ DMA_DeInit(GPS_USART_DMA_STREAM);DMA_StructInit(&DMA_InitStructure); /*設置DMA源:串口數(shù)據寄存器地址*/DMA_InitStructure.DMA_PeripheralBaseAddr = GPS_DATA_ADDR; /*內存地址(要傳輸?shù)淖兞康闹羔?*/DMA_InitStructure.DMA_MemoryBaseAddr = (u32)gps_rbuff;/*方向:從外設到內存*/ DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; /*傳輸大小DMA_BufferSize=SENDBUFF_SIZE*/ DMA_InitStructure.DMA_BufferSize = GPS_RBUFF_SIZE;/*外設地址不增*/ DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /*內存地址自增*/DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; /*外設數(shù)據單位*/ DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;/*內存數(shù)據單位 8bit*/DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; /*DMA模式:不斷循環(huán)*/DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; /*優(yōu)先級:中*/ DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //M2M mode is disabledDMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // 非內存到內存模式DMA_InitStructure.DMA_Auto_reload = DMA_Auto_Reload_Enable;/*配置DMA2的數(shù)據流3*/ DMA_Init(GPS_USART_DMA_STREAM, &DMA_InitStructure); // Enable UARTy_DMA1_Channel Transfer complete interruptDMA_ITConfig(GPS_USART_DMA_STREAM, DMA_IT_HT|DMA_IT_TC, ENABLE);/* 配置串口 向 DMA發(fā)出請求 */UART_DMACmd(UART4, UART_DMAReq_EN, ENABLE); /*使能DMA*/DMA_Cmd(GPS_USART_DMA_STREAM, ENABLE);/*配置中斷優(yōu)先級*/GPS_Interrupt_Config(); }GPS_DMA_Config 函數(shù)主要工作如下: 設置了外設地址為 USART 的數(shù)據寄存器,并把數(shù)據傳輸方向設置為從USART 數(shù)據寄存器傳輸?shù)絻却孀兞?gps_rbuff 中,該緩沖區(qū)數(shù)組大小為 512 字節(jié)。
DMA2_Channel3_IRQHandler中斷函數(shù)
最關鍵的位置它設置了 DMA 半傳輸結束中斷及全傳輸結束中斷,
//配置 DMA 發(fā)送完成后產生中斷 DMA_ITConfig(GPS_USART_DMA_STREAM,DMA_IT_HT|DMA_IT_TC,ENABLE);所以它實際把緩沖區(qū)分為成了大小相等的 A/B 兩部分,每次 DMA 接收了半個緩沖區(qū)大小的數(shù)據時(本程序為256 字節(jié)),就會引起中斷得益于這個機制。
可以設計程序當 DMA 使用緩沖區(qū) A 存儲數(shù)據時,控制 CPU 使用 B中的數(shù)據進行 GPS 解碼,當 DMA 使用 B 存儲時,控制 CPU 使用 A 進行解碼,只要緩沖區(qū)的大小設置合適,即可避免前面說到的數(shù)據丟失問題,這種處理方式也稱“乒乓緩沖”,得名于它像打乒乓球一樣,你來我往。
當 DMA 的 半 傳 輸 中 斷 或 全 傳 輸 中 斷 產 生 時 , 進 入 的 中 斷 服 務 函 數(shù) 調 用 了DMA2_Channel3_IRQHandler函數(shù)
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);} }在 這 個 函 數(shù) 處 理 中 , 主 要 是 在 半 傳 輸 和 全 傳 輸 結 束 引 起 中 斷 時 , 對GPS_HalfTransferEnd 和 GPS_TransferEnd 標志位進行標記, 在解碼流程中根據這兩個標志使用不同的緩沖區(qū)進行處理。
5-2 解碼測試函數(shù)nmea_decode_test()
解碼函數(shù)主要是調用函數(shù)庫中的函數(shù),并加標志位進行處理。
/*** @brief nmea_decode_test 解碼GPS模塊信息* @param 無* @retval 無*/ int nmea_decode_test(void) {double deg_lat;//轉換成[degree].[degree]格式的緯度double deg_lon;//轉換成[degree].[degree]格式的經度nmeaINFO info; //GPS解碼后得到的信息nmeaPARSER parser; //解碼時使用的數(shù)據結構 uint8_t new_parse=0; //是否有新的解碼數(shù)據標志nmeaTIME beiJingTime; //北京時間 uint8 str_buff[50];/* 設置用于輸出調試信息的函數(shù) */nmea_property()->trace_func = &trace;nmea_property()->error_func = &error;nmea_property()->info_func = &gps_info;/* 初始化GPS數(shù)據結構 */nmea_zero_INFO(&info);nmea_parser_init(&parser);while(1){ // for(int t = 0;t<256;t++){uart_putchar(DEBUG_UART,gps_rbuff[t]);}if(GPS_HalfTransferEnd) /* 接收到GPS_RBUFF_SIZE一半的數(shù)據 */{/* 進行nmea格式解碼 */nmea_parse(&parser, (const char*)&gps_rbuff[0], HALF_GPS_RBUFF_SIZE, &info);GPS_HalfTransferEnd = 0; //清空標志位new_parse = 1; //設置解碼消息標志 }else if(GPS_TransferEnd) /* 接收到另一半數(shù)據 */{nmea_parse(&parser, (const char*)&gps_rbuff[HALF_GPS_RBUFF_SIZE], HALF_GPS_RBUFF_SIZE, &info);GPS_TransferEnd = 0;new_parse =1;}if(new_parse) //有新的解碼消息 { /* 對解碼后的時間進行轉換,轉換成北京時間 */GMTconvert(&info.utc,&beiJingTime,8,1);/* 輸出解碼得到的信息 */// printf("\r\n時間%d-%02d-%02d,%d:%d:%d\r\n", beiJingTime.year+1900, beiJingTime.mon,beiJingTime.day,beiJingTime.hour,beiJingTime.min,beiJingTime.sec);//info.lat lon中的格式為[degree][min].[sec/60],使用以下函數(shù)轉換成[degree].[degree]格式deg_lat = nmea_ndeg2degree(info.lat);deg_lon = nmea_ndeg2degree(info.lon);// printf("\r\n緯度:%f,經度%f\r\n",deg_lat,deg_lon); // printf("\r\n海拔高度:%f 米 ", info.elv); // printf("\r\n速度:%f km/h ", info.speed); // printf("\r\n航向:%f 度", info.direction); // printf("\r\n正在使用的GPS衛(wèi)星:%d,可見GPS衛(wèi)星:%d",info.satinfo.inuse,info.satinfo.inview); // printf("\r\n正在使用的北斗衛(wèi)星:%d,可見北斗衛(wèi)星:%d",info.BDsatinfo.inuse,info.BDsatinfo.inview); // printf("\r\nPDOP:%f,HDOP:%f,VDOP:%f",info.PDOP,info.HDOP,info.VDOP);/* 顯示時間日期 */sprintf(str_buff," Date:%4d/%02d/%02d ", beiJingTime.year+1900, beiJingTime.mon,beiJingTime.day);LCD_ShowString(0,2,str_buff,BLACK);sprintf(str_buff," Time:%02d:%02d:%02d", beiJingTime.hour,beiJingTime.min,beiJingTime.sec);LCD_ShowString(0,20,str_buff,BLACK);/* 正在使用的衛(wèi)星 可見的衛(wèi)星*/sprintf(str_buff," GPS:%2d ", info.satinfo.inuse);LCD_ShowString(150,0,str_buff,BLACK);/* 正在使用的衛(wèi)星 可見的衛(wèi)星*/sprintf(str_buff," BDS:%2d ", info.BDsatinfo.inuse);LCD_ShowString(150,20,str_buff,BLACK);/* 緯度 經度*/sprintf(str_buff," lat:%.6f ", deg_lat);LCD_ShowString(0,40,str_buff,BLUE);sprintf(str_buff," lon:%.6f",deg_lon);LCD_ShowString(115,40,str_buff,BLUE);/* 速度 */sprintf(str_buff," speed:%4.2f km/h", info.speed);LCD_ShowString(0,60,str_buff,RED);/* 航向 */sprintf(str_buff," Angle:%3.2f deg", info.direction);LCD_ShowString(0,80,str_buff,MAGENTA);new_parse = 0;}} }根據串口的內容,緊接著調用 NMEA庫函數(shù) nmea_parse 進行解碼,解碼的結果存放在數(shù)據結構變量 info 中,由于解碼結果得到的時間信息是格林威治時間,所以在輸出解碼結果前,調用了 GMTconvert 函數(shù)把它轉化成北京時間。
使用了 nmea_ndeg2degree 函數(shù)把 info.lat 及 info.lon 參數(shù)轉化到了 deg_lat 和 deg_lon 變量中。
info.lat 及 info.lon 存儲的就是緯度、 經度信息,但它們的單位是[degree][min].[sec/60]格式,即 NMEA 語句解碼后的原始 ddmm.mmmm 表示的數(shù)據,通常在地圖上使用的格式都是[degree].[degree], 所以一定要經過這樣轉換后再使用。
5-3 MM32堆棧空間設置
由于 NMEA 解碼庫在進行解碼時需要動態(tài)分配較大的空間,所以我們需要在 MM32的啟動文件 startup_mm32f327x_keil.s 文件中對堆棧空間進行修改,本工程中設置的棧空間大小設置為 0x00002000, 堆空間大小設置為 0x0000 0800。
Stack_Size EQU 0x00002000 ;這時候為8kb棧空間 實測最大調用為3264bAREA STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem SPACE Stack_Size __initial_sp; <h> Heap Configuration ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> ; </h> Heap_Size EQU 0x00000800AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base Heap_Mem SPACE Heap_Size __heap_limitPRESERVE8THUMB關于如何查看工程中調用的最大??臻g,可以查看我之前寫的博客鏈接:
Keil STM32查看堆棧使用量及調用鏈.htm文件
六、主函數(shù)調用
#include "headfile.h" // **************************** 代碼區(qū)域 ****************************int main(void) {board_init(true); //初始化 UART1 DEBUG輸出串口GPS_Config(); //初始化 GPS DMA加大容量棧空間Lcd_Init(); LCD_ShowString(0,0,"GPS_TEST",MAGENTA); //初始化 IPS ST7789屏幕 nmea_decode_test(); //GPS解碼測試while(1){}}在全部模塊初始化結束之后,就可以在屏幕上邊參數(shù)信息了,包括經緯度和時間等等。
另外:確認天線處在衛(wèi)星信號良好的位置中, 天線需要正面朝上,并置于室外無遮擋物處。 若把天線置于室內,是無法定位的,衛(wèi)星信號在室內基本無信號
最后工程文件我將放置在百度網盤鏈接: https://pan.baidu.com/s/1QRNr87n38MjN-yKf30Uiww提取碼:jr2t
不足之處多多指教,工程的我是根據野火的NMEA庫移植而來。dma配置部分花費的時間比較多,后來看了靈動官網的一些配置,參考而來。參數(shù)手冊很重要,一些dma的通道都是從中看來的。
總結
以上是生活随笔為你收集整理的【灵动MM32-DMA传输-GPS解算】 移植NMEA协议库解析GGA数据格式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BCC(Borland C++ Comp
- 下一篇: 【教程】广联达装饰设计(Deco Des