' ========================================================================= ' ' File...... GIS_GPS.SXB ' Purpose... Polstar PMB-248 Controller ' Author.... (c) Grand Idea Studio, Inc. [www.grandideastudio.com] ' E-mail.... support@parallax.com ' Updated... 20 FEB 2006 ' ' ========================================================================= ' ------------------------------------------------------------------------- ' Program Description ' ------------------------------------------------------------------------- ' ' The main control code for the Parallax GPS Module. ' ' This code communicates with the Polstar PMB-248 GPS receiver unit via a ' 4800bps serial interface. The standard NMEA0183-formatted data is parsed ' and returned to the user via a single, bi-directional serial pin (SIO) ' based on the data that was requested by the user. Optionally, the raw ' NMEA0183 data can be transmitted by pulling the /RAW pin LOW. ' ' Commands: ' ' "!GPS", 0x00 - GetInfo: GPS Receiver Module version (2 bytes) ' "!GPS", 0x01 - GetValid: Check validity of received data (1 byte) ' "!GPS", 0x02 - GetSats: Number of acquired satellites (1 byte) ' "!GPS", 0x03 - GetTime: Time (UTC/Greenwich Mean Time) (3 bytes) ' "!GPS", 0x04 - GetDate: Date (UTC/Greenwich Mean Time) (3 bytes) ' "!GPS", 0x05 - GetLat: Latitude (5 bytes) ' "!GPS", 0x06 - GetLong: Longitude (5 bytes) ' "!GPS", 0x07 - GetAlt: Altitude above mean-sea-level (2 bytes) ' "!GPS", 0x08 - GetSpeed: Speed over ground (2 bytes) ' "!GPS", 0x09 - GetHead: Heading/direction of travel over ground (2 bytes) ' ' This code currently parses data from the NMEA0183 $GPRMC and $GPGGA ' strings on an as-requested basis: ' ' $GPRMC,POS_UTC,POS_STAT,LAT,LAT_D,LON,LON_D,SPD,HDG,DATE,MAG_VAR,MAG_REF,*CC ' ' POS_UTC - UTC of position. Hours, minutes and seconds. (hhmmss) ' POS_STAT - Position status. (A = Data valid, V = Data invalid) ' LAT - Latitude (ddmm.ffff) ' LAT_D - Latitude direction. (N = North, S = South) ' LON - Longitude (dddmm.ffff) ' LON_D - Longitude direction (E = East, W = West) ' SPD - Speed over ground. (knots) (000.0 - 999.9) ' HDG - Heading/track made good (degrees True) (xxx.x) ' DATE - Date (ddmmyy) ' MAG_VAR - Magnetic variation (degrees) (x.x) ' MAG_REF - Magnetic variation (E = East, W = West) ' *CC - Checksum ' ' $GPGGA,POS_UTC,LAT,LAT_D,LON,LON_D,GPS_QUAL,SATS,HDOP,ALT,ALT_U,GEO,GEO_U,DGPS_AGE,DGPS_ID,*CC ' ' POS_UTC - UTC of position. Hours, minutes and seconds. (hhmmss) ' LAT - Latitude (ddmm.ffff) ' LAT_D - Latitude direction. (N = North, S = South) ' LON - Longitude (dddmm.ffff) ' LON_D - Longitude direction (E = East, W = West) ' GPS_QUAL - GPS Quality Indicator (0 = Disabled, 1 = GPS positioning, 2 = D-GPS positioning) ' SATS - Number of acquired satellites used in position calculations (00 - 12) ' HDOP - Horizontal dilution of position (00.0 - 99.9) ' ALT - Altitude (00000.0 - 99999.9) ' ALT_U - Altitude unit (M = meters) ' GEO - Geoidal separation (000.0 - 999.9) ' GEO_U - Geoidal separation unit (M = meters) ' DGPS_AGE - Age of DGPS data (time elapsed since DGPS reception) (seconds) (00 - 99) ' DGPS_ID - DGPS Reference Station ID (xxxx) ' *CC - Checksum ' ' This code is released as open-source and can be fully customized by users ' wanting additional information, a different command structure, for use ' with a different GPS receiver, or for any other purpose, provided that ' the changes and/or modifications also remain open-source and are made ' publicly available for others to learn from and use. ' ------------------------------------------------------------------------- ' Notes ' ------------------------------------------------------------------------- ' It is possible for this code to enter an infinite loop if we are waiting ' for specific GPS data that never comes (e.g., if the GPS unit fails) ' ------------------------------------------------------------------------- ' Device Settings ' ------------------------------------------------------------------------- ' for breadboard 'DEVICE SX28, OSCXT2, TURBO, STACKX, OPTIONX ' for production DEVICE SX20, OSCXT2, TURBO, STACKX, OPTIONX FREQ 20_000_000 ' ------------------------------------------------------------------------- ' IO Pins ' ------------------------------------------------------------------------- ' for breadboard 'Raw VAR RA.0 ' pull-up via 4.7K (active low) 'Sio VAR RA.1 ' pull-up via 4.7K 'Gps_rx VAR RA.2 ' RX from PGM-248 'Gps_tx VAR RA.3 ' TX to PGM-248 ' for production Raw VAR RA.2 ' pull-up via 4.7K (active low) Sio VAR RA.3 ' pull-up via 4.7K Gps_rx VAR RB.2 ' RX from PGM-248 Gps_tx VAR RB.3 ' TX to PGM-248 ' ------------------------------------------------------------------------- ' Constants ' ------------------------------------------------------------------------- VER_HW CON $10 ' hardware version (x.y) VER_FW CON $10 ' firmware version (x.y) Baud CON "T4800" CR CON 13 ' carriage return LF CON 10 ' line feed ' ------------------------------------------------------------------------- ' Variables ' ------------------------------------------------------------------------- serByte VAR Byte ' serial IO byte rcvBuf VAR Byte(12) ' buffer for received GPS data idx VAR Byte ' loop control ' temporary work variables for main routines tmp1 VAR Byte tmp2 VAR Byte tmp3 VAR Byte tmpWord VAR Byte(2) ' temporary work variables for subroutines temp1 VAR Byte temp2 VAR Byte temp3 VAR Byte temp4 VAR Byte temp5 VAR Byte temp6 VAR Byte ' ========================================================================= PROGRAM Start ' ========================================================================= Pgm_ID: DATA "Parallax GPS Receiver Module", 0 ' ------------------------------------------------------------------------- ' Subroutine Declarations ' ------------------------------------------------------------------------- WAIT_FOR_GPS SUB 1 ' wait for the specified byte to be received from GPS WAIT_MS SUB 1, 2 ' delay in milliseconds TX_BYTE SUB 1, 2 ' transmit byte to serial interface RX_BYTE SUB ' receive byte from serial interface RX_BYTE_GPS SUB ' receive byte from GPS interface WORD_ADD_BYTE SUB 2 '@Result16, Value8 WORD_MULT_BYTE SUB 2 '@Result16, Value8 ' ------------------------------------------------------------------------- ' Program Code ' ------------------------------------------------------------------------- Start: ' initialization code here CMP_B = 1 ' disable comparator TRIS_B = %11110100 ' RB.7-4 & RB.2 is input, RB.3 & RB.1-0 is output WKEN_B = %11111111 ' disable interrupts ST_B = %11111111 ' disable schmitt trigger inputs Main: IF Raw = 0 THEN ' /RAW pin is low, so enter "raw" mode INPUT Gps_rx ' ensure Gps_rx is set to an input to avoid any contention GOTO Main ' and let the external analog switch handle the rest ELSE ' /RAW pin is high, so enter "smart" mode ' wait for header from user serByte = RX_BYTE IF serByte <> "!" THEN Main serByte = RX_BYTE IF serByte <> "G" THEN Main serByte = RX_BYTE IF serByte <> "P" THEN Main serByte = RX_BYTE IF serByte <> "S" THEN Main Get_Cmd: ' get command from user (specifies desired data to receive from the GPS) serByte = RX_BYTE IF serByte = $00 THEN Get_Info IF serByte = $01 THEN Get_Valid IF serByte = $02 THEN Get_Sats IF serByte = $03 THEN Get_Time IF serByte = $04 THEN Get_Date IF serByte = $05 THEN Get_Lat IF serByte = $06 THEN Get_Long IF serByte = $07 THEN Get_Alt IF serByte = $08 THEN Get_Speed IF serByte = $09 THEN Get_Head GOTO Main ENDIF ' ---------------------------------------------------- Get_Info: ' command = $00 WAIT_MS 3 ' let host get ready TX_BYTE VER_HW ' hardware version TX_BYTE VER_FW ' firmware version GOTO Main ' ---------------------------------------------------- Get_Valid: ' command = $01 ' wait for $GPRMC header from GPS module WAIT_FOR_GPS "G" WAIT_FOR_GPS "P" WAIT_FOR_GPS "R" WAIT_FOR_GPS "M" WAIT_FOR_GPS "C" WAIT_FOR_GPS "," WAIT_FOR_GPS "," ' receive byte from GPS stream rcvBuf(0) = RX_BYTE_GPS tmp1 = 0 IF rcvBuf(0) = "A" THEN ' any other character must be invalid data tmp1 = 1 ENDIF TX_BYTE tmp1 GOTO Main ' ---------------------------------------------------- Get_Sats: ' command = $02 ' wait for $GPGGA header from GPS module WAIT_FOR_GPS "G" WAIT_FOR_GPS "P" WAIT_FOR_GPS "G" WAIT_FOR_GPS "G" WAIT_FOR_GPS "A" FOR idx = 0 TO 6 ' jump to (n+1)th field WAIT_FOR_GPS "," NEXT ' receive string from GPS stream rcvBuf(0) = RX_BYTE_GPS rcvBuf(1) = RX_BYTE_GPS ' extract values from buffer rcvBuf(0) = rcvBuf(0) - "0" rcvBuf(1) = rcvBuf(1) - "0" tmp1 = rcvBuf(0) * 10 tmp1 = tmp1 + rcvBuf(1) TX_BYTE tmp1 GOTO Main ' ---------------------------------------------------- Get_Time: ' command = $03 ' wait for $GPRMC header from GPS module WAIT_FOR_GPS "G" WAIT_FOR_GPS "P" WAIT_FOR_GPS "R" WAIT_FOR_GPS "M" WAIT_FOR_GPS "C" WAIT_FOR_GPS "," FOR idx = 0 TO 5 ' receive string from GPS stream rcvBuf(idx) = RX_BYTE_GPS NEXT ' extract values from buffer FOR idx = 0 TO 5 rcvBuf(idx) = rcvBuf(idx) - "0" NEXT tmp1 = rcvBuf(0) * 10 tmp1 = tmp1 + rcvBuf(1) tmp2 = rcvBuf(2) * 10 tmp2 = tmp2 + rcvBuf(3) tmp3 = rcvBuf(4) * 10 tmp3 = tmp3 + rcvBuf(5) TX_BYTE tmp1 ' hours TX_BYTE tmp2 ' minutes TX_BYTE tmp3 ' seconds GOTO Main ' ---------------------------------------------------- Get_Date: ' command = $04 ' wait for $GPRMC header from GPS module WAIT_FOR_GPS "G" WAIT_FOR_GPS "P" WAIT_FOR_GPS "R" WAIT_FOR_GPS "M" WAIT_FOR_GPS "C" FOR idx = 0 TO 8 ' jump to (n+1)th field WAIT_FOR_GPS "," NEXT FOR idx = 0 TO 5 ' receive string from GPS stream rcvBuf(idx) = RX_BYTE_GPS NEXT ' extract values from buffer FOR idx = 0 TO 5 rcvBuf(idx) = rcvBuf(idx) - "0" NEXT tmp1 = rcvBuf(0) * 10 tmp1 = tmp1 + rcvBuf(1) tmp2 = rcvBuf(2) * 10 tmp2 = tmp2 + rcvBuf(3) tmp3 = rcvBuf(4) * 10 tmp3 = tmp3 + rcvBuf(5) TX_BYTE tmp1 ' day TX_BYTE tmp2 ' month TX_BYTE tmp3 ' year GOTO Main ' ---------------------------------------------------- Get_Lat: ' command = $05 ' wait for $GPRMC header from GPS module WAIT_FOR_GPS "G" WAIT_FOR_GPS "P" WAIT_FOR_GPS "R" WAIT_FOR_GPS "M" WAIT_FOR_GPS "C" FOR idx = 0 TO 2 ' jump to (n+1)th field WAIT_FOR_GPS "," NEXT FOR idx = 0 TO 10 ' receive string from GPS stream rcvBuf(idx) = RX_BYTE_GPS NEXT ' extract values from buffer ' degrees rcvBuf(0) = rcvBuf(0) - "0" rcvBuf(1) = rcvBuf(1) - "0" tmp1 = rcvBuf(0) * 10 tmp1 = tmp1 + rcvBuf(1) ' minutes rcvBuf(2) = rcvBuf(2) - "0" rcvBuf(3) = rcvBuf(3) - "0" tmp2 = rcvBuf(2) * 10 tmp2 = tmp2 + rcvBuf(3) ' fractional minutes tmpWord = 0 FOR idx = 5 TO 8 rcvBuf(idx) = rcvBuf(idx) - "0" WORD_MULT_BYTE tmpWord, 10 ' tmpWord = tmpWord * 10 WORD_ADD_BYTE tmpWord, rcvBuf(idx) ' tmpWord = tmpWord + rcvBuf(idx) NEXT tmp3 = 0 ' default to "N" IF rcvBuf(10) = "S" THEN tmp3 = 1 ENDIF TX_BYTE tmp1 TX_BYTE tmp2 TX_BYTE tmpWord(1) TX_BYTE tmpWord(0) TX_BYTE tmp3 GOTO Main ' ---------------------------------------------------- Get_Long: ' command = $06 ' wait for $GPRMC header from GPS module WAIT_FOR_GPS "G" WAIT_FOR_GPS "P" WAIT_FOR_GPS "R" WAIT_FOR_GPS "M" WAIT_FOR_GPS "C" FOR idx = 0 TO 4 ' jump to (n+1)th field WAIT_FOR_GPS "," NEXT FOR idx = 0 TO 11 ' receive string from GPS stream rcvBuf(idx) = RX_BYTE_GPS NEXT ' extract values from buffer ' degrees tmp1 = 0 FOR idx = 0 TO 2 rcvBuf(idx) = rcvBuf(idx) - "0" tmp1 = tmp1 * 10 tmp1 = tmp1 + rcvBuf(idx) NEXT ' minutes rcvBuf(3) = rcvBuf(3) - "0" rcvBuf(4) = rcvBuf(4) - "0" tmp2 = rcvBuf(3) * 10 tmp2 = tmp2 + rcvBuf(4) ' fractional minutes tmpWord = 0 FOR idx = 6 TO 9 rcvBuf(idx) = rcvBuf(idx) - "0" WORD_MULT_BYTE tmpWord, 10 ' tmpWord = tmpWord * 10 WORD_ADD_BYTE tmpWord, rcvBuf(idx) ' tmpWord = tmpWord + rcvBuf(idx) NEXT tmp3 = 0 ' default to "E" IF rcvBuf(11) = "W" THEN tmp3 = 1 ENDIF TX_BYTE tmp1 TX_BYTE tmp2 TX_BYTE tmpWord(1) TX_BYTE tmpWord(0) TX_BYTE tmp3 GOTO Main ' ---------------------------------------------------- Get_Alt: ' command = $07 ' wait for $GPGGA header from GPS module WAIT_FOR_GPS "G" WAIT_FOR_GPS "P" WAIT_FOR_GPS "G" WAIT_FOR_GPS "G" WAIT_FOR_GPS "A" FOR idx = 0 TO 8 ' jump to (n+1)th field WAIT_FOR_GPS "," NEXT FOR idx = 0 TO 6 ' receive string from GPS stream rcvBuf(idx) = RX_BYTE_GPS NEXT ' set maximum height to 6553.5m to prevent overflow of word variable rcvBuf(0) = "0" IF rcvBuf(1) >= "6" THEN rcvBuf(1) = "6" IF rcvBuf(2) >= "5" THEN rcvBuf(2) = "5" IF rcvBuf(3) >= "5" THEN rcvBuf(3) = "5" IF rcvBuf(4) >= "3" THEN rcvBuf(4) = "3" IF rcvBuf(6) >= "5" THEN rcvBuf(6) = "5" ENDIF ENDIF ENDIF ENDIF ENDIF ' extract values from buffer tmpWord = 0 FOR idx = 0 TO 6 IF rcvBuf(idx) <> "." THEN rcvBuf(idx) = rcvBuf(idx) - "0" WORD_MULT_BYTE tmpWord, 10 ' tmpWord = tmpWord * 10 WORD_ADD_BYTE tmpWord, rcvBuf(idx) ' tmpWord = tmpWord + rcvBuf(idx) ENDIF NEXT TX_BYTE tmpWord(1) TX_BYTE tmpWord(0) GOTO Main ' ---------------------------------------------------- Get_Speed: ' command = $08 ' wait for $GPRMC header from GPS module WAIT_FOR_GPS "G" WAIT_FOR_GPS "P" WAIT_FOR_GPS "R" WAIT_FOR_GPS "M" WAIT_FOR_GPS "C" FOR idx = 0 TO 6 ' jump to (n+1)th field WAIT_FOR_GPS "," NEXT FOR idx = 0 TO 4 ' receive string from GPS stream rcvBuf(idx) = RX_BYTE_GPS NEXT ' extract values from buffer tmpWord = 0 FOR idx = 0 TO 4 IF rcvBuf(idx) <> "." THEN rcvBuf(idx) = rcvBuf(idx) - "0" WORD_MULT_BYTE tmpWord, 10 ' tmpWord = tmpWord * 10 WORD_ADD_BYTE tmpWord, rcvBuf(idx) ' tmpWord = tmpWord + rcvBuf(idx) ENDIF NEXT TX_BYTE tmpWord(1) TX_BYTE tmpWord(0) GOTO Main ' ---------------------------------------------------- Get_Head: ' command = $09 ' wait for $GPRMC header from GPS module WAIT_FOR_GPS "G" WAIT_FOR_GPS "P" WAIT_FOR_GPS "R" WAIT_FOR_GPS "M" WAIT_FOR_GPS "C" FOR idx = 0 TO 7 ' jump to (n+1)th field WAIT_FOR_GPS "," NEXT FOR idx = 0 TO 4 ' receive string from GPS stream rcvBuf(idx) = RX_BYTE_GPS NEXT ' extract values from buffer tmpWord = 0 FOR idx = 0 TO 4 IF rcvBuf(idx) <> "." THEN rcvBuf(idx) = rcvBuf(idx) - "0" WORD_MULT_BYTE tmpWord, 10 ' tmpWord = tmpWord * 10 WORD_ADD_BYTE tmpWord, rcvBuf(idx) ' tmpWord = tmpWord + rcvBuf(idx) ENDIF NEXT TX_BYTE tmpWord(1) TX_BYTE tmpWord(0) GOTO Main ' ---------------------------------------------------- ' ------------------------------------------------------------------------- ' Subroutine Code ' ------------------------------------------------------------------------- ' ---------------------------------------------------- ' Wait for the specified character to be received ' from the GPS interface WAIT_FOR_GPS: temp1 = __PARAM1 DO SERIN Gps_rx, Baud, temp2 LOOP UNTIL temp2 = temp1 RETURN ' ---------------------------------------------------- ' Use: WAIT_MS milliseconds {, multiplier } ' -- multiplier is optional WAIT_MS: temp1 = __PARAM1 ' get milliseconds IF __PARAMCNT = 1 THEN ' if no multiplier temp2 = 1 ' set to 1 ELSE ' else temp2 = __PARAM2 ' get multiplier ENDIF IF temp1 > 0 THEN ' no delay if either 0 IF temp2 > 0 THEN PAUSE temp1 * temp2 ' do the delay ENDIF ENDIF RETURN ' ---------------------------------------------------- ' Use: char = RX_BYTE ' -- reads byte from serial input and places in 'char' RX_BYTE: SERIN Sio, Baud, temp1 ' receive a byte RETURN temp1 ' return to caller ' ---------------------------------------------------- ' Use: char = GPS_RX_BYTE ' -- reads byte from serial input and places in 'char' RX_BYTE_GPS: SERIN Gps_rx, Baud, temp1 ' receive a byte RETURN temp1 ' return to caller ' ---------------------------------------------------- ' Use: TX_BYTE theByte {, count} ' -- transmit "theByte" at "Baud" on "Sio" ' -- optional "count" may be specified (must be > 0) TX_BYTE: temp1 = __PARAM1 ' save byte IF __PARAMCNT = 1 THEN ' if no count temp2 = 1 ' set to 1 ELSE ' otherwise temp2 = __PARAM2 ' get count IF temp2 = 0 THEN ' do not allow 0 temp2 = 1 ENDIF ENDIF DO WHILE temp2 > 0 ' loop through count SEROUT Sio, Baud, temp1 ' send the byte DEC temp2 ' decrement count LOOP RETURN ' ---------------------------------------------------- ' MATH16 routines courtesy of Hitt Consulting WORD_ADD_BYTE: ASM MOV FSR,__PARAM1 ADD IND,__PARAM2 INC FSR ADDB IND,C ENDASM BANK ' Restore FSR RETURN ' ---------------------------------------------------- WORD_MULT_BYTE: ASM ' PARAM1=8 BIT VALUE ' PARAM2=RESULT LSB ' PARAM3=RESULT MSB ' PARAM4=COUNTER MOV FSR,__PARAM1 MOV __PARAM1,__PARAM2 CLR __PARAM2 CLR __PARAM3 MOV __PARAM4,#8 :MULT_LOOP CLC RL __PARAM2 RL __PARAM3 RL __PARAM1 JNC @:MULT_DONE ADD __PARAM2,IND ADDB __PARAM3,C INC FSR ADD __PARAM3,IND DEC FSR :MULT_DONE DJNZ __PARAM4,@:MULT_LOOP MOV IND,__PARAM2 INC FSR MOV IND,__PARAM3 ENDASM BANK ' Restore FSR RETURN ' ----------------------------------------------------