​ ​ ​ ​ GPS Data receiver using X2's background serial data reception
Results 1 to 3 of 3

Thread: GPS Data receiver using X2's background serial data reception

  1. #1
    Senior Member
    Join Date
    Jan 1970
    Location
    Perth, Western Australia
    Posts
    4,466

    Default GPS Data receiver using X2's background serial data reception

    During the development of Little Ben, the need arose for a clock timer that did not require adjusting or periodical replacement of a backup battery. Little Ben 'clock' was to be installed in a public recreation park and needed to thrive on neglect and withstand power outages while maintaining accurate time.

    I had a couple of surplus GPS data receiver modules, originally intended for use in a car navigation device. A check of the datasheet indicated that the module required 3.3 volts at 45mA and the default data output should be 9600 baud. On-line research showed that the data packet with a "$GPGGA" header would probably give me that data I wanted for a clock.

    I initially placed the GPS receiver on a window sill, with an extension cable connected to an AXE027 into my computer. The PICAXE terminal showed the data at 9600 baud was garbled. The default output format for my module idled high. Sequences of data packets were being sent every second.

    The attached code demonstrates a PICAXE 28X2 GPS data receiver, using the background receive mode for its hardware UART 'hSerial' port. The PICAXE's background timer is also used, to provide a backup should GPS data be corrupted.

    Hardware for the demonstration consisted of a PICAXE 28X2 running off a (LM7805-based) 5v supply. This is fed into a LD1117AV33 low dropout 3.3v regulator, used to supply the GPS module. Data from the GPS receiver is fed directly into PICAXE hSerIn/Pin C.7/Leg 18. A LED & current limiting resistor are connected to pin A.1/Leg 3, used as a digital output.

    The demonstration software receives data from the GPS module, which needs reasonable access to satellite data (Eg. placed near a window - my home has double brick walls and a steel roof). The PICAXE firmware receives data from the hardware UART and logs it sequentially in the scratchpad RAM, used as a circular buffer. Subroutine hReceive searches the logged data for a "$GPGGA" header (in subroutine Check_GPS_Header) and waits for the entire packet to be received before building the checksum in subroutine hRecCheckSum. If the rebuilt checksum matches the received checksum, the master time variables are overwritten in subroutine hReadGPSData

    The time is output to the PICAXE Terminal window at 19200 baud every 10 seconds. If "#Define Show_Packet_Diags" is specified, every validly received "$GPGGA" packet is logged as well. A working version would not have "#Define Show_Packet_Diags" specified. Note that "#Define Show_Packet_Diags" places a considerable load on the PICAXE and it does not always receive and validate every "$GPGGA" data packet. If the packet's checksum fails validation, the packet is discarded. When this happens, the background timer's 1-second interrupt keeps time, with minutes and hours incrementing where appropriate.

    If you are adding your own code to the main loop of the program, be aware that blocking commands could interfere with the operation of background serial data reception. Blocking commands could also prevent interrupts, stopping the software backup clock from 'ticking' every second.

    I have included a lot of comments to help explain the operation of the code. As a result, the code is quite large and has to occupy two posts: Declarations/Initialisation/Main_Loop/Interrupts and All Subroutine Code.

  2. #2
    Senior Member
    Join Date
    Jan 1970
    Location
    Perth, Western Australia
    Posts
    4,466

    Default

    Code part 1: Declarations/Initialisation/Main_Loop/Interrupts
    Code:
    'GPS Signal receiver decoder by inglewoodpete
    Symbol Version = 4  '09-Jul-2018  952 bytes Added checksum handler
    '
    Symbol Major = 0    ' Major revision ID
    '
    ' Note that when Show_Packet_Diags is defined, PICAXE will not process every GPS time packet
    #Define Show_Packet_Diags
    '
    #PICAXE 28X2
    '#COM xx
    #Terminal 19200
    '
    ' **** Hardware Pins Definitions - i prefix for inputs; o for outputs; b for bothway pins
    '
    Symbol oLED          = A.1       ' 3 A.1 After bootup, Toggles on every vaid GPS data packet received
    Symbol oLEDValue     = outpinA.1 ' 3 A.1
    '
    Symbol ohSerOut      = C.6       ' 17 Background Serial Async Out
    Symbol ihSerIn       = C.7       ' 18 Background Serial Async In
    '
    ' **** Variables - t prefix: bit variable; b: byte; w: word; r: other RAM; s: scratchpad; e: EEPROM
    '
    Symbol tMismatch     = bit0
    Symbol tNoGPSDataYet = bit1   'b0, w0  Set to True at bootup until first valid GPS packet received
    Symbol tRecError     = bit2   'b0, w0  Used to flag bad checksum byte rec'd Eg 2F is ok; DJ would be bad
    Symbol tGoodPacket   = bit3   'b0, w0  GPGGA packet with checksum confirmed correct
    '
    Symbol bSeconds	     = b2     'w1      System time (regularly updated by GPS Data)
    Symbol bMinutes      = b3 	  'w1      System time (regularly updated by GPS Data)
    Symbol bHours12      = b4     'w2      System time (regularly updated by GPS Data) Hours in 12-hour format
    Symbol bHours24      = b5     'w2      Hours in 24-hour format, taken from GPS data
    Symbol bPtrMem       = b6     'w3      Copy of Ptr variable for later reuse
    Symbol bEEPROMPtr    = b7     'w3      Used to validate GPS data packet header
    '
    Symbol tmpSeconds    = b20	  'w10    As received in valid GPS data packet
    Symbol tmpMinutes    = b21	  'w10
    Symbol tmpHours      = b22	  'w11
    Symbol bLoop         = b23    'w11
    Symbol bLastSecond   = b24    'w12  The last second where time was printed
    Symbol bAMPM         = b25    'w12
    Symbol bTemp         = b26    'w13
    Symbol bData         = b27    'w13
    Symbol bPktLen       = b28    'w14
    Symbol bCalcCSum     = b29    'w14  Locally reconstructed Checksum
    Symbol bRecdCSum     = b30    'w15  Checksum received in GPS data packet
    Symbol bNibble       = b31    'w15   RecHex
    Symbol bError        = b32    'w16   RecHex
    Symbol bByteToShow   = b33    'w16  Used to transfer a value to ShowHex
    '
    Symbol wBufferedDataLen = w21 'b42/43  Byte count for current GPS data packet
    Symbol wPktStartPtr  = w22    'b44/45  Points to last $ sign
    Symbol wPktDataPtr   = w24    'b46/47  Points to Start-of-Data, after the "$GPGGA," header
    '
    ' **** Constants - Prefix = c; msk (mask); flg (flag)
    '
    Symbol False         = 0
    Symbol True          = 1
    '
    Symbol cTimeOffset   = 8            'Western Australia is 8 hours ahead of UTC
    '
    Symbol mskSerInRange = %1111111111  'Bytes 0-1023 (10-bit mask)
    '
    Symbol mskLoNibble   = %00001111
    Symbol mskHiNibble   = %11110000
    'Interrupt masks
    Symbol mskTmrAndSer  = %10100000
    Symbol flgTmrAndSer  = %10100000
    'Timer constants
    Symbol tmrIntOn1stTick = 65535   'Interrupt to be caused by roll over on first major tick
    Symbol cOneMinute    = 60        'Seconds
    Symbol cOneHour      = 60        'Minutes, not seconds
    '
    'Pause periods at 16MHz (PICAXE X2 Models)
    Symbol c100mS        =  200      ' 100mS
    Symbol c1S           = 2000      '1000mS
    '
    ' **** Scratchpad - Prefix = s (28X2 - Bytes 0 to 1023d)
    '
    Symbol sSerInBuffStart  = 0
    '
    ' **** EEPROM  - Prefix = e (256 Bytes - 0 to 255d)
    '
    Symbol eGPSHeader = 0
    EEPROM eGPSHeader, ("$GPGGA,")      'Header for data packet containing time data
    Symbol eEOData = 6
    '
    ' ***** C O D E *********************************************************************
    '
    Init: SetFreq m16
          Pause c1S
          '
          For bLoop =  1 to 16          '8 flashes
             Pause c100mS               'Put Pause first to allow SetFreq to settle
             Toggle oLED
          Next bLoop
          SerTxd (CR, LF, CR, LF, "Booted: GPS Data Receiver v", #Major, ".", #Version, CR, LF)
          '
    		'Initialise serial Downlink for GPS comms
    		Ptr = sSerInBuffStart
          hSerPtr = sSerInBuffStart
    		hSerSetup B9600_16, %00001  '9600 baud @ 16MHz, Background, no inversion
          '
          'Background timer provides reasonably accurate backup timekeeping if GPS data is intermittent
          'Start the background timer (runs continuously)
          Timer = tmrIntOn1stTick
          SetTimer t1S_16            'Expires after 1/8 second @ 16 MHz
    		Flags = 0						'Reset serial reception flag
          SetIntFlags Or flgTmrAndSer, mskTmrAndSer 'Set timer 0 or hSerial to interrupt
          '
          tNoGPSDataYet = True       'Log GPS data until first valid packet received
          SerTxd ("Initialisation Complete: Enter Main Loop", CR, LF)
          '
          ' ------ MAIN LOOP ---------------
          '
          Do
             'Insert (add) your own code here.  Avoid blocking commands!
             '
             GoSub hReceive
             'The following code displays the time once every 10 seconds
             If bLastSecond <> bSeconds Then     'Don't log multiple times within one second
                tmpSeconds = bSeconds // 10      'Get the remainder (returns a value 0 to 9)
                If tmpSeconds = 0 Then           'Log once every 10 seconds
                   GoSub ShowTime                'Show the current time
                   bLastSecond = bSeconds        'For comparison, next loop
                EndIf
             EndIf
          Loop
    '
    ' ************************************************************
    '  Interrupt handler
    ' ************************************************************
    '  
    Interrupt:If hSerInFlag = True then
    				hSerInFlag = False
    			 EndIf
              If TOFlag = True Then
                TOFlag = False                         'Reset (clear) the flag first
                Inc bSeconds
                If bSeconds = cOneMinute Then          '/--- System Timer
                   bSeconds = 0                        '|
                   Inc bMinutes                        '|
                   If bMinutes = cOneHour Then         '|
                      bMinutes = 0                     '|
                      Inc bHours12                     '|
                      If bHours12 = 13 Then            '|
                         bHours12 = 1                  '|
                         bAMPM = " "                   '| Don't know if it is AM or PM
                      EndIf                            '|
                   EndIf                               '|
                EndIf                                  '|
                '
                Timer = tmrIntOn1stTick                   'Then reset the timer
              EndIf    'Timer has ticked
              SetIntFlags Or flgTmrAndSer, mskTmrAndSer   'Set timer 0 or hSerial to interrupt
    			 Return
    '
    ' ************************************************************

  3. #3
    Senior Member
    Join Date
    Jan 1970
    Location
    Perth, Western Australia
    Posts
    4,466

    Default

    Code part 2: Subroutines
    Code:
    '
    ' ********       S U B R O U T I N E S       ********
    '
    ' ***** hReceive: Receive serial data via the background serial port and process
    '
    ' Transfers received data from Scratchpad RAM into an area of main RAM starting at bRecCount
    '
    ' Entry: hSerPtr          Points to next byte BEYOND the last received byte
    '        Ptr              Points to next byte of received data IF data has been received
    '        bPktLen          = 0
    '  Uses: wSearchPtr       Source pointer for scratchpad data
    '        wBufferedDataLen Byte count for current GPS data packet
    '        wPktDataPtr      Points to Start-of-Data, after the "$GPGGA," header
    '  Exit: bPktLen          Number of bytes received
    '        Ptr              Points to location of the next byte to be processed
    '        
    hReceive:Do Until Ptr = hSerPtr
                If @Ptr = "$" Then
                   wPktStartPtr = Ptr
                   wBufferedDataLen = hSerPtr - Ptr And mskSerInRange  'Restrict to max value 1023
                   If wBufferedDataLen > 13 Then '14 or more chars in buffer
                      GoSub Check_GPS_Header     'Compare header with "$GPGGA,"
                      If tMismatch = False Then  'Have start of a good packet, with Time data
                         wBufferedDataLen = hSerPtr - Ptr And mskSerInRange  'Restrict to max 1023
                         wPktDataPtr = Ptr       'Save location of packet start "$" pointer
                         If wBufferedDataLen >= 82 Then   'Don't log until entire GPS packet received
                                                 'Bit-banged SerTxd interferes with background receive
                            bPktLen = 6          'Length of "$GPGGA,"
                            GoSub hRecCheckSum   'Confirm packet validity
                            If tGoodPacket = True Then
                               If tNoGPSDataYet = True Then
                                  tNoGPSDataYet = False
                                  Low oLED       'Turn off LED after first valid GPS pkt received
                                  SerTxd("   Checksum Rec'd=", #bRecdCSum, ", Calc'd=", #bCalcCSum)
                                  SerTxd(" First valid pkt.", CR, LF)
                               EndIf
                               Ptr = wPktDataPtr 'Restore pointer to start of packet's data
                               GoSub hReadGPSData'Receive and synchonise to GPS time
                            Else
                               SerTxd("   Checksum Rec'd=", #bRecdCSum, ", Calc'd=", #bCalcCSum)
                               SerTxd(" **Bad pkt**", CR, LF)
                            EndIf
                         Else                    'Less than required data buffered
                            Ptr = wPktStartPtr   'Reset Ptr to start of packet
                         EndIf 'wBufferedDataLen >= 82
                         Exit                    'Exit Do loop after (good or bad) full packet received
                      EndIf 'tMismatch = False
                   EndIf 'wBufferedDataLen > 13
                EndIf '@Ptr = "$" Then
                Inc Ptr                          'Continue search for Start-of-packet marker ($)
             Loop
             Return
    '
    '
    ' ***** hReadGPSData: Interpret time data
    '
    '           Header has been found previously, marking start-of-packet
    '           Checksum has been confirmed, so data should be valid
    '           Packet is referenced by Ptr
    '           GPS Time is in 24-hour format and is converted to 12-hour + AM/PM
    '           Time reference variables are updated (overwritten) on exit
    '           Change +/- cTimeOffset depending on locality (+ for east; - for west)
    '
    hReadGPSData:  tmpHours = @ptrInc - $30 * 10 + @ptrInc - $30 + cTimeOffset  ' + or - cTimeOffset
                   tmpMinutes = @ptrInc - $30 * 10 + @ptrInc - $30
                   tmpSeconds = @ptrInc - $30 * 10 + @ptrInc - $30
                   If oLEDValue = On Then           'Toggle LED on every valid GPS packet
                      Low oLED
                   Else
                      High oLED
                   EndIf
                   If tmpHours > 243 Then           'After subtracting up to 12 hours
                      tmpHours = tmpHours + 24
                   EndIf
                   If tmpHours > 23 Then            'After adding up to 12 hours
                      tmpHours = tmpHours - 24
                   EndIf
                   bHours24 = tmpHours
                   Select Case tmpHours
                   Case 0                           '0 is 12 (Midnight)
                      tmpHours = 12
                      bAMPM = "A"
                   Case < 12                        'Morning
                      bAMPM = "A"
                   Case = 12                        '12 noon
                      bAMPM = "P"
                   Else                             '1PM to 11PM Afternoon/Evening
                      tmpHours = tmpHours - 12      'bHours24 - 12
                      bAMPM = "P"
                   End Select
                   '
                   SetIntFlags Off                  'Stop interrupts while time master variables are being updated
                   bHours12 = tmpHours
                   bMinutes = tmpMinutes
                   bSeconds = tmpSeconds
                   SetIntFlags Or flgTmrAndSer, mskTmrAndSer 'Set timer 0 or hSerial to interrupt
                   Return
    '
    ' **** hRecCheckSum: Search Data Packet for EOP marker '*' and checksum value, then validate data with checksum
    '
    'GPS Packet example:
    '    $GPGGA,235917.000,4025.6301,N,08654.7184,W,2,07,1.1,186.1,M,-33.8,M,0.8,0000*4Ecl   (cl = <cr><lf>)
    'Messages have a maximum length of 82 characters, including the $ starting character and the ending <LF>
    ' Checksun is 'built' by XORing every character between '$' and '*'
    '  Entry:   wPktStartPtr      Points to '$' character
    '           bPktLen = 6       Gets Reset
    '  Used:    bTemp
    '           Ptr
    '  Exit:    bCalcCSum
    '           bRecdCSum
    '           bPktLen           Packet length
    '
    hRecCheckSum:  Ptr = wPktStartPtr
                   bCalcCSum = 0
                   tGoodPacket = False
                   bTemp = @PtrInc
                   #IfDef Show_Packet_Diags
                      SerTxd(bTemp, @Ptr)              '$ sign + 'G'
                   #EndIf
                   bCalcCSum = @PtrInc
                   bPktLen = 2
                   Do Until @Ptr = "*" Or bPktLen > 82 'Create "Recalculated" checksum
                      #IfDef Show_Packet_Diags
                         SerTxd(@Ptr)
                      #EndIf
                      bCalcCSum = bCalcCSum Xor @PtrInc
                      Inc bPktLen
                   Loop
                   #IfDef Show_Packet_Diags
                      SerTxd(@Ptr)                     'Should be *'
                   #EndIf
                   Inc Ptr
                   GoSub RecHex                        'Returns bRecdCSum, "Received" checksum
                   If bCalcCSum = bRecdCSum Then       'Compare "Recalculated" with "Received" checksum
                      tGoodPacket = True
                   EndIf
                   #IfDef Show_Packet_Diags
                      SerTxd(CR, LF)
                   #EndIf
                   Return
    '
    ' **** RecHex: Read two ASCII Hex bytes representing nibbles
    '
    '        First character is high nibble; Second is Low
    ' Entry: Ptr         Points to 1st Byte (Hex nibble - should be ASCII value "0"-"9","A"-"F")
    ' Used:  bNibble     
    ' Exit:  bRecdCSum   Binary value 0-255
    '        Ptr         Points to the next Byte to be interpretted
    '        tRecError   Set if bad byte value received
    '
    RecHex:  
             #IfDef Show_Packet_Diags
                SerTxd("[$", @Ptr)
             #EndIf
             tRecError = False
             If @Ptr >= "0" and @Ptr <= "9" Then
                bNibble = @Ptr - "0"    'Convert to 4 bits 0000 - 1001
             ElseIf @Ptr >= "A" and @Ptr <= "F" Then   
                bNibble = @Ptr - 55     'Convert to 4 bits 1010 - 1111
             Else
                bError = @ptr
                tRecError = True
             EndIf
             If tRecError = False Then
                Inc Ptr
                bRecdCSum = bNibble << 4
                #IfDef Show_Packet_Diags
                   SerTxd(@Ptr, "]")
                #EndIf
                If @Ptr >= "0" and @Ptr <= "9" Then
                   bNibble = @Ptr - "0"    'Convert to 4 bits 0000 - 1001
                ElseIf @Ptr >= "A" and @Ptr <= "F" Then   
                   bNibble = @Ptr - 55     'Convert to 4 bits 1010 - 1111
                Else
                   bError = @ptr
                   tRecError = True
                EndIf
                If tRecError = False Then
                   Inc Ptr
                   bRecdCSum = bRecdCSum Or bNibble
                EndIf
             EndIf
             Return
    '
    ' **** Check_GPS Header: Confirm Data with EEPROM
    '
    '    Entry: Ptr           Points to a "$" sign: the start of a GPS Data Packet
    '     Used: bEEPROMPtr
    '           bData
    '           tMismatch
    '
    Check_GPS_Header: tMismatch = False
                      bEEPROMPtr = eGPSHeader
                      Read bEEPROMPtr, bData
                      Do
                         If @ptrInc <> bData Then
                            tMismatch = True           'Exit to resend on data mismatch
                            Exit                       'Exit 'Do' loop
                         EndIf
                         Inc bEEPROMPtr
                         Read bEEPROMPtr, bData
                      Loop Until bData = 0             'Exits on second word comparison.
                      Return
    '
    ' ***** ShowTime: Show received wireless data packet time
    '
    ' Entry: bHours12, bMinutes, bSeconds, bAMPM
    '
    ShowTime:   SerTxd ("Local time is ", #bHours12, ":")
                If bMinutes < 10 Then
                   SerTxd ("0")
                EndIf
                SerTxd (#bMinutes, ":")
                If bSeconds < 10 Then
                   SerTxd ("0")
                EndIf
                SerTxd (#bSeconds)
                If bAMPM > " " Then
                   SerTxd (" ", bAMPM, "M")
                EndIf
                SerTxd (CR, LF)
                '
                Return

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •