受朋友所托,調試一款GPS模塊,該模塊是UBLOX的NEO-6M GPS模組。想到用這款GPS的人較多,自己日后也有可能在用到這個模塊,就寫下這份筆記。
1. 介紹
基本信息如下:
1, 模塊采用U-BLOX NEO-6M模組,體積小巧,性能優異。
2, 模塊增加放大電路,有利於無緣陶瓷天線快速搜星。
3, 模塊可通過串口進行各種參數設置,並可保存在EEPROM,使用方便。
4, 模塊自帶SMA接口,可以連接各種有源天線,適應能力強。
5, 模塊兼容3.3V/5V電平,方便連接各種單片機系統。
6, 模塊自帶可充電后備電池,可以掉電保持星歷數據。
看到這里,就可以知道,這個模塊是高度集成的,有點類似於西門子華為等公司的GPRS模塊,基本上就是一個小的系統,用戶只需要用AT命令通過串口通信就可以完成所有工作。那么這款模塊的使用,其實就是字符串的解析工作了。
2. 通信協議
GPS模塊采用NMEA 0183協議,NMEA 0183是美國國家海洋電子協會(National Marine Electronics Association)為海用電子設備制定的標准格式。目前業已成了GPS導航設備統一的RTCM(Radio Technical Commission for Maritime services)標准協議。
NMEA-0183協議采用ASCII碼來傳遞GPS定位信息,我們稱之為幀。
幀格式形如:$aaccc,ddd,ddd,„,ddd*hh(CR)(LF)
1、“$”:幀命令起始位
2、aaccc:地址域,前兩位為識別符(aa),后三位為語句名(ccc)
3、ddd„ddd:數據
4、“*”:校驗和前綴(也可以作為語句數據結束的標志)
5、hh:校驗和,$與*之間所有字符ASCII碼的校驗和(各字節做異或運算,得到
校驗和后,再轉換16進制格式的ASCII字符)
6、(CR)(LF):幀結束,回車和換行符
在一般的項目中,最常用的指令是第4個,即$GPRMC ,推薦定位信息,長度70字節。$GPRMC(推薦定位信息,Recommended Minimum Specific GPS/Transit Data),$GPRMC語句的基本格式如下:
$GPRMC,(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)*hh(CR)(LF)
(1) UTC時間,hhmmss(時分秒)
(2) 定位狀態,A=有效定位,V=無效定位
(3) 緯度ddmm.mmmmm(度分)
(4) 緯度半球N(北半球)或S(南半球)
(5) 經度dddmm.mmmmm(度分)
(6) 經度半球E(東經)或W(西經)
(7) 地面速率(000.0~999.9節)
(8) 地面航向(000.0~359.9度,以真北方為參考基准)
(9) UTC日期,ddmmyy(日月年)
(10)磁偏角(000.0~180.0度,前導位數不足則補0)
(11) 磁偏角方向,E(東)或W(西)
(12) 模式指示(A=自主定位,D=差分,E=估算,N=數據無效)
舉例如下:
$GPRMC,023543.00,A,2308.28715,N,11322.09875,E,0.195,,240213,,,A*78
3. PC端顯示數據
GPS模塊有一個PC配置軟件,叫做u-Center,可以對模塊進行參數設置,然后保存到EEPROM,其實也可以通過單片機串口通信進行設置,但是PC端設置更加人性化,可以立刻看到結果。
打開GPS模塊之后,接上u-Center軟件,可以看到如下數據。
1 $GPGSV,2,2,08,21,15,076,,23,52,270,,26,50,050,,27,52,179,*7D 2 $GPRMC,132043.00,V,,,,,,,120116,,,N*7F 3 $GPVTG,,,,,,,,,N*30 4 $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30 5 $GPGSV,2,1,08,03,00,230,,07,02,301,,08,24,198,,16,72,000,*7B 6 $GPGSV,2,2,08,21,15,076,,23,52,270,,26,50,050,,27,52,179,*7D 7 $GPRMC,132044.00,V,,,,,,,120116,,,N*78 8 $GPVTG,,,,,,,,,N*30 9 $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30 10 $GPGSV,2,1,08,03,00,230,,07,02,301,,08,24,198,,16,72,000,*7B 11 $GPGSV,2,2,08,21,15,076,,23,52,270,25,26,50,050,,27,52,179,*7A 12 $GPRMC,132045.00,V,,,,,,,120116,,,N*79 13 $GPVTG,,,,,,,,,N*30 14 $GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30 15 $GPGSV,2,1,08,03,00,230,,07,02,301,,08,24,198,,16,72,000,*7B 16 $GPGSV,2,2,08,21,15,076,,23,52,270,,26,50,050,,27,52,179,*7D 17 $GPRMC,132046.00,V,,,,,,,120116,,,N*7A 18 $GPVTG,,,,,,,,,N*30 19 $GPGSV,2,1,08,03,00,230,,07,02,301,,08,24,198,,16,72,000,*7B 20 $GPGSV,2,2,08,21,15,076,07,23,52,270,20,26,50,050,,27,52,179,*78 21 $GPRMC,132049.00,V,,,,,,,120116,,,N*75 22 $GPRMC,133300.00,V,,,,,,,120116,,,N*7A 23 $GPRMC,133301.00,V,,,,,,,120116,,,N*7B 24 $GPRMC,133302.00,V,,,,,,,120116,,,N*78 25 $GPRMC,133303.00,V,,,,,,,120116,,,N*79 26 $GPRMC,133304.00,V,,,,,,,120116,,,N*7E 27 $GPRMC,133305.00,V,,,,,,,120116,,,N*7F 28 $GPRMC,133306.00,A,3949.63075,N,11616.48616,E,0.513,,120116,,,A*7A 29 $GPRMC,133307.00,A,3949.63025,N,11616.48614,E,1.053,,120116,,,A*7C 30 $GPRMC,133308.00,A,3949.63002,N,11616.48641,E,1.101,,120116,,,A*70 31 …………
4. PC端軟件配置
GPS有7種指令數據輸出,如果我們只需要當前經緯度的話,可以屏蔽其他的數據。如果不屏蔽的話,MCU處理的時候,會不停的收到不需要的指令,降低MCU效率。
使用軟件配置,[Config]-->[Configuration], 可以選擇顯示哪些項目,這里只保留GPRMC指令信息輸出,然后保存。界面顯示如下:
5. 數據解析
相對於MCU的處理速度,GPS定位數據更新幾乎可以認為是緩慢變化的信號,每秒都會輸出推薦位置信息,但是即使丟掉幾個也不會影響定位准確性。因此可以使用中斷循環buffer來接收GPS輸出的串口數據,然后在需要的地方讀取buffer,對數據實現一次檢索,找到一個有用的GPS定位數據。
/******************************************************************************* 1.中斷負責把GPS串口數據保存到GPS_Uart_Rcv_Buf,在合適的地方調用此函數開始解析。 2.調用本函數會自動關閉接收,然后處理,期間的GPS數據可以忽略。 3.在合適的地方打開接收。 *******************************************************************************/ uint8_t get_gps_useful_data(uint8_t *weidu, uint8_t *jingdu) { uint8_t *p_frame_start = NULL; uint8_t *p_useful_frame_start = NULL; uint8_t *p_useful_frame_end = NULL; int16_t frame_len = 0; int16_t rcv_buf_data_len = 0; int16_t index = 0; int16_t frame_start_point = 0; int16_t remain_data_len = 0; uint8_t rtn =0; gps_rcv_enable(FALSE); rcv_buf_data_len = GPS_Rev_Buf_Size; p_frame_start = &GPS_Uart_Rcv_Buf[0]; /* data example: $GPVTG,,,,,,,,,N*30 $GPRMC,132234.00,V,,,,,,,120116,,,N*7D $GPRMC,133735.00,A,3949.63893,N,11616.48419,E,0.296,,120116,,,A*79 */ for(index = 0; index < rcv_buf_data_len; ) { p_frame_start=strstr(GPS_Uart_Rcv_Buf + index, "$GPRMC,"); //1. find start, "$GPRMC," //$GPRMC,132234.00,V,,,,,,,120116,,,N*7D $GPRMC,133735.00,A,3949.63893,N,11616.48419,E,0.296,,120116,,,A*79 if(p_frame_start) { index = p_frame_start-GPS_Uart_Rcv_Buf; if(index>0) { memset(GPS_Uart_Rcv_Buf, 0 , index); } index = index + 7; p_useful_frame_start = strstr(GPS_Uart_Rcv_Buf + index, ",A,");//2 find useful data, ",A," frame_start_point = p_useful_frame_start - p_frame_start; if((frame_start_point > 20)||(frame_start_point <= 0))//not find useful data { continue; } frame_start_point = p_useful_frame_start-GPS_Uart_Rcv_Buf; p_useful_frame_end = strstr(GPS_Uart_Rcv_Buf + frame_start_point, ",A*");//3 find useful data end, ",A*" frame_len = p_useful_frame_end - p_frame_start + 1; if((frame_len > GPS_Frame_Buf_Size)||(frame_len <= 50)) // not found frame end { continue; } //$GPRMC,133735.00,A,3949.63893,N,11616.48419,E,0.296,,120116,,,A*79 frame_start_point = p_frame_start - GPS_Uart_Rcv_Buf + 1; memset(GPS_Frame_Buf, 0, GPS_Frame_Buf_Size); memcpy(GPS_Frame_Buf, GPS_Uart_Rcv_Buf + frame_start_point , frame_len); // if(check_frame_xor(GPS_Frame_Buf) != TRUE) // { // continue; // } //the frame is correct rtn = read_gps_data(GPS_Frame_Buf, remain_data_len, weidu, jingdu); if(1 == rtn) { break; //found weidu,jingdu data } else { continue; } } else { break; } } g_gps_data_cnt = 0; memset(GPS_Uart_Rcv_Buf, 0, GPS_Rev_Buf_Size); return rtn; }
上面處理的基本思想是:找到"$GPRMC,"幀頭,然后找到代表定位信息有效的字符",A,",最后找到幀尾",A*"。
因為實際收到的數據有不完整的,或者無效的定位信息,也有串口接收循環buffer造成的覆蓋數據,因此上面還校驗了數據長度,以及一個標志字符串到另一個字符串直接的長度。
實際使用中,發現沒有加上校驗也可以很有效的工作,但是為了保證數據安全,最后的異或校驗還有應該有的。
/******************************************************************************* * Function Name : * Description : 對GPRMC數據包進行解析,找到經緯度數據 * Input : * Output : * Return : *******************************************************************************/ uint8_t read_gps_data(uint8_t *gps_buf, uint8_t frame_len, uint8_t *weidu, uint8_t *jindu) { uint8_t *weidu_s = NULL; uint8_t *weidu_o = NULL; uint8_t *jingdu_o = NULL; uint8_t rtn =0; //GPRMC,133735.00,A,3949.63893,N,11616.48419,E,0.296,,120116,,,A*79 weidu_s = strstr(GPS_Frame_Buf, ",A,"); weidu_o = strstr(GPS_Frame_Buf, ",N,"); jingdu_o = strstr(GPS_Frame_Buf, ",E,"); if((weidu_s == NULL) || (weidu_o == NULL) ||(jingdu_o == NULL) ) { rtn = FALSE; } else { memset(weidu_buf,0,sizeof(weidu_buf)); memset(jindu_buf,0,sizeof(jindu_buf)); memcpy(weidu_buf, weidu_s+3, (weidu_o-weidu_s-3)); memcpy(jindu_buf, weidu_o+3, (jingdu_o-weidu_o-3)); printf("\r\n---------------------------------------"); printf("\r\nGet GPS Frame:\r\n%s\r\n", GPS_Frame_Buf); printf("\r\n---------------------------------------"); rtn = TRUE; } return rtn; }
6. 總結
本文總結了GPS模塊的基本使用方法,可以得到經度和緯度信息。對於此類的模塊產品,主要工作有兩大部分:
1:模塊的熟悉,包括配置和指令的使用,一般可以一邊讀文檔一邊跑下demo體驗效果;
2:字符串的解析。
同樣的經驗可以應用於很多串口模塊。
例如GPRS模塊,藍牙模塊,zigbee模塊,TCP模塊,CAN模塊,485模塊。
這幾種產品我都使用過,其基本思路是一樣的,調試時候可以先用串口助手模擬MCU來發數據,其實大部分模塊都有自己的PC端工具,可以很快的看到效果。
使用模塊可以極大地提升項目效率,但是靈活性和成本上會有所犧牲。另外,由於模塊的封閉性,一般都要寫很多的異常保護處理來保證產品的正常工作。