Hello I have been using the NMEA module for some time on a product I sell a few units per year, I'm looking to redesign this board using Q41 Microcontrollers, I had a lot of trouble trying to convert the module to be able to use XV...
I somewhat have it working using the xvISRRX2, but is not as eficient and streamlined as the original NMEA Module...
Any one has done the conversion?
Best Regards
Sergio
is there a XV18 version of the NMEA module
Moderators: David Barker, Jerry Messina
-
- Swordfish Developer
- Posts: 1486
- Joined: Fri Jan 30, 2009 6:27 pm
- Location: US
Re: is there a XV18 version of the NMEA module
With a few minor changes to NMEA.bas to remove the hardcoded references to the interrupt and RCREG definitions and use the generic ones defined in USART.bas, you should be able to changed the module to work with any device without having to restructure it to use the rx isr.
This also adds a new '#option NMEA_USART' to allow using any of the device uarts
NMEA.bas (v1.2):
Here's a modified version of the wiki example showing its usage for USART1:
Here's another example showing use of different USARTs using the new '#option NMEA_USART':
I didn't test it on a real device, so if you have problems...
This also adds a new '#option NMEA_USART' to allow using any of the device uarts
NMEA.bas (v1.2):
Code: Select all
{
*****************************************************************************
* Name : NMEA.BAS *
* Author : David John Barker *
* Notice : Copyright (c) 2007 Mecanique *
* : All Rights Reserved *
* Date : 06 JUN 2024 *
* Version : 1.2 *
* : 1.2 06 JUN 2024 *
* : - Add '#option NMEA_USART' to allow using USART 1-5 *
* : - Added importing USARTx.bas module for usart RCIF/RCIE and *
* : ReadByte() definitions *
* : 1.1 24/08/2007 *
* : - Added overflow flag to prevent overwriting of the ring *
* : buffer - this ensures that if the main program takes a large *
* : amount of time on a task, the interrupt will re-sync with any *
* : NMEA sentence *
* : 1.0 - Initial release
* Notes : All NMEA data is transmitted in the form of sentences. Only *
* : printable ASCII characters are allowed, plus CR (carriage *
* : return) And LF (line feed). Each sentence starts With a "$" *
* : sign and ends With <CR><LF>. *
* : The format For a talker sentence is: $ttsss,D1,D2,....<CR><LF> *
* : The first two letters following the "$" are the talker *
* : identifier. The next three characters (sss) are the sentence *
* : identifier, followed by a number of data fields separated by *
* : commas, followed by an optional checksum, and terminated by *
* : carriage return/line feed. The data fields are uniquely *
* : defined for each sentence type. *
*****************************************************************************
}
Module NMEA
// #option to set the NMEA buffer size...
#option NMEA_BUFFER_SIZE = 200
#if Not (NMEA_BUFFER_SIZE in (100 to 250))
#error NMEA_BUFFER_SIZE, "Invalid option. Buffer size must be bteween 100 and 250"
#endif
// import usart module - added v1.2
// #option to specify which USART to use (1-5)
#option NMEA_USART = 1
#if not (NMEA_USART in (1 to _usart))
#error "invalid NMEA_USART option for " + _device
#endif
#if (NMEA_USART = 1)
include "USART.bas"
#elseif (NMEA_USART = 2)
include "USART2.bas"
#elseif (NMEA_USART = 3)
include "USART3.bas"
#elseif (NMEA_USART = 4)
include "USART4.bas"
#elseif (NMEA_USART = 5)
include "USART5.bas"
#endif
// Import conversion module...
Include "convert.bas"
// An NMEA structure - this holds the NMEA data line, the number
// of data fields in the sentence and finally the cumputed NMEA checksum
Public Structure TNMEA
Line As String(80)
Count As Byte
Valid As Boolean
End Structure
// These local constants and variables are used by the NMEA interrupt handler
Const NMEABufferSize = NMEA_BUFFER_SIZE // Size of the buffer
Dim FBuffer(NMEABufferSize) As Byte // Array for holding received characters
Dim FIndexIn As Byte // Pointer to the next empty location in the buffer
Dim FIndexOut As Byte // Pointer to the location of the oldest character in the buffer
Dim FCount As Byte // Number of NMEA items in the buffer
Dim FFieldCount As Byte // Number of fields in the NMEA sentence
Dim FReadingSentence As Boolean // Are we reading a NMEA sentence
Dim FCalculatingChecksum As Boolean // Are we calculating checksum
Dim FChecksum As Byte // NMEA checksum
Dim FOverflow As Boolean // Buffer overflow flag
{
****************************************************************************
* Name : SetBufferData (PRIVATE) *
* Purpose : Local helper function to set buffer data *
****************************************************************************
}
Sub SetBufferData(pData As PRODL)
Inc(FIndexIn)
If FIndexIn >= NMEABufferSize Then
FIndexIn = 0
EndIf
FSR0 = @FBuffer
Inc(FSR0, FIndexIn)
If FIndexIn = FIndexOut Then
FOverflow = true
Else
INDF0 = pData
EndIf
End Sub
{
****************************************************************************
* Name : GetBufferData (PRIVATE) *
* Purpose : Local helper function to get a buffer byte *
****************************************************************************
}
Function GetBufferData() As INDF1
Inc(FIndexOut)
If FIndexOut >= NMEABufferSize Then
FIndexOut = 0
EndIf
FSR1 = @FBuffer
Inc(FSR1,FIndexOut)
End Function
{
****************************************************************************
* Name : OnNMEAData (PRIVATE) *
* Notes : Interrupt based ring buffer that reads characters from the *
* : USART. The routine also calculates a NMEA checksum and counts *
* : the number of data fields as data arrives *
* V1.2 - changed to use USART.bas definitions for RCIF and ReadByte() *
****************************************************************************
}
Interrupt OnNMEAData()
Dim Data As Char
// If a byte receive has triggered the interrupt, then context save
// FSR0 and process the data byte...
If RCIF = 1 Then // changed v1.2 to use USARTx.bas
Data = ReadByte() // changed v1.2 to use USARTx.bas
// buffer overflow has occurred, abort...
If FOverflow Then
Exit
EndIf
// proceed...
Save(FSR0, PRODL)
// stop calculating checksum...
If Data = "*" Then
FCalculatingChecksum = False
EndIf
// calculate checksum...
If FCalculatingChecksum Then
FChecksum = FChecksum Xor Byte(Data)
EndIf
// start reading a sentence, start calculating checksum and
// initialise the number of NMEA fields to zero...
If Data = "$" Then
FReadingSentence = True
FCalculatingChecksum = True
FChecksum = 0
FFieldCount = 0
EndIf
// are we reading a sentence? If so, set the buffer data...
If FReadingSentence Then
SetBufferData(Data)
// if the character is a non-whitespace, then look to see
// if it is a field comma. If it is, increment field count...
If Data >= " " Then
If Data = "," Then
Inc(FFieldCount)
EndIf
// its a whitespace character - terminate sentence reading
// and save checksum and field count to the ring buffer...
Else
Inc(FFieldCount)
FReadingSentence = False
INDF0 = 0 ' null terminator
SetBufferData(FChecksum)
SetBufferData(FFieldCount)
Inc(FCount)
EndIf
EndIf
Restore
EndIf
End Interrupt
{
****************************************************************************
* Name : Initialise (PRIVATE) *
* Notes : Initialise local module variables and configure the USART for *
* : interrupts based receive *
* V1.2 - changed to use USART.bas definitions for RCIE *
****************************************************************************
}
Sub Initialize()
FOverflow = false
FReadingSentence = False
FCalculatingChecksum = False
FChecksum = 0
FCount = 0
FFieldCount = 0
FIndexIn = 0
FIndexOut = 0
RCIE = 1 // changed v1.2 - enable interrupt on USARTx receive
Enable(OnNMEAData) // enable handler
End Sub
{
****************************************************************************
* Name : GetItem *
* Purpose : Get a NMEA item structure held in the buffer. Function returns *
* : true if item found, false otherwise *
****************************************************************************
}
Public Function GetItem(ByRef pNMEA As TNMEA) As Boolean
Dim ChecksumStart, Checksum, Index As Byte
Dim Sum As String
// no NMEA data in the buffer...
If FCount = 0 Then
result = False
// NMEA data is held in the buffer, start processing...
Else
Result = True
Dec(FCount)
// Copy the NMEA line stored in the buffer into the NMEA.Line - also
// note the where the checksum index of the NMEA string is located
ChecksumStart = 0
Index = 0
FSR0 = @pNMEA.Line
Repeat
POSTINC0 = GetBufferData
If INDF1 = byte("*") Then
ChecksumStart = Index + 1
EndIf
Inc(Index)
Until INDF1 = 0
// We have read the string from the buffer, the next two bytes
// in the buffer hold the checksum that was computed when the string
// was loaded into the buffer and also the number of data fields the
// NMEA sentence has...
Checksum = GetBufferData
pNMEA.Count = GetBufferData
// The NMEA sentence has a checksum of the form *xx, where xx is a
// two byte HEX number. We need to extract the two digit string data...
If ChecksumStart > 0 Then
FSR0 = @Sum
FSR1 = @pNMEA.Line
Inc(FSR1, ChecksumStart)
Repeat
POSTINC0 = INDF1
Until POSTINC1 = 0
EndIf
// Now we validate the checksum and set the NMEA.Valid flag as needed...
If HexToStr(Checksum,2) = Sum Then
pNMEA.Valid = True
Else
pNMEA.Valid = False
EndIf
EndIf
// if a buffer overflow has occurred, wait until all items have been
// read out of the buffer before clearing the overflow error...
If FOverflow And FCount = 0 Then
FOverflow = false
EndIf
End Function
{
****************************************************************************
* Name : GetField *
* Purpose : Find a NMEA field from within the NMEA line. Pass a NMEA *
* : structure, togther with the field index you want to retreive. *
* : Returns true if a field found, false otherwise *
****************************************************************************
}
Public Function GetField(ByRef pNMEA As TNMEA, pItemIndex As Byte, ByRef pItem As String) As Boolean
// default is not found...
Result = False
// set line address and loop though line until we find 'ItemIndex' occurance
// of the seperator char...
FSR1 = @pNMEA.Line
While pItemIndex > 0 And INDF1 <> 0
If POSTINC1 = byte(",") Then
Dec(pItemIndex)
EndIf
Wend
// load item address and load characters from line until next seperator
// or line terminator found...
FSR0 = @pItem
INDF0 = 0
If pItemIndex = 0 Then
Result = True
While INDF1 <> byte(",") And INDF1 <> 0
POSTINC0 = POSTINC1
Wend
INDF0 = 0 // set item line terminator
EndIf
End Function
// initialise module
Initialize()
Code: Select all
// NMEA example 1
device=18F16Q41
clock=32
include "intosc.bas" // set to use intosc
#option DIGITALIO_INIT = true // call setalldigital at startup
include "setdigitalio.bas" // may be required for usart pins
// import modules...
Include "NMEA.bas"
Include "Usart.bas"
Include "Convert.bas"
// local variables
Dim NMEAItem As TNMEA
Dim NMEAField As String
// this program with display the sentence identifier and the number
// of data fields the sentence has...
USART.SetBaudrate(br4800)
While True
// get and NMEA item from the buffer, if one is available - check to
// see if checksum is valid before proceeding....
If NMEA.GetItem(NMEAItem) And NMEAItem.Valid Then
NMEA.GetField(NMEAItem,0,NMEAField)
USART.Write(NMEAField, " : ", DecToStr(NMEAItem.Count), 13, 10)
EndIf
Wend
Code: Select all
// NMEA example 1 with usart1-5
device=18F16Q41
clock=32
include "intosc.bas" // set to use intosc
#option DIGITALIO_INIT = true // call setalldigital at startup
include "setdigitalio.bas" // may be required for usart pins
// import modules...
#option NMEA_USART = 2 // NMEA usart (default = 1)
Include "NMEA.bas"
#if (NMEA_USART = 1)
include "USART.bas"
#elseif (NMEA_USART = 2)
include "USART2.bas"
#elseif (NMEA_USART = 3)
include "USART3.bas"
#elseif (NMEA_USART = 4)
include "USART4.bas"
#elseif (NMEA_USART = 5)
include "USART5.bas"
#endif
Include "Convert.bas"
// local variables
Dim NMEAItem As TNMEA
Dim NMEAField As String
// this program with display the sentence identifier and the number
// of data fields the sentence has...
SetBaudrate(br4800) // USARTx
While True
// get and NMEA item from the buffer, if one is available - check to
// see if checksum is valid before proceeding....
If NMEA.GetItem(NMEAItem) And NMEAItem.Valid Then
NMEA.GetField(NMEAItem,0,NMEAField)
Write(NMEAField, " : ", DecToStr(NMEAItem.Count), 13, 10) // USARTx
EndIf
Wend
Re: is there a XV18 version of the NMEA module
Thanks a lot Jerry, I will try it this week...
Greetings
Sergio
Greetings
Sergio
-
- Swordfish Developer
- Posts: 1486
- Joined: Fri Jan 30, 2009 6:27 pm
- Location: US
Re: is there a XV18 version of the NMEA module
The attached zip file contains an updated NMEA module (v1.3) that has a few new features:
- can use either high or low priority interrupts (set via #option)
- context save optimized when used with xv18 devices
- NMEA.bas contains aliases for various USARTx routines like SetBaudrate so you don't have to change the main program when switching usarts
- interrupts are no longer automatically enabled at startup as part of NMEA.Initialize (set via #option)
The last one is important. It allows you to set up all the hardware prior to enabling the NMEA usart interrupt.
The downside is you now have to call NMEA.EnableInterrupt() when you're ready to start (typically after NMEA.SetBaudrate)
Here's an updated example for the Q41 that shows usage, including PPS setup
- can use either high or low priority interrupts (set via #option)
- context save optimized when used with xv18 devices
- NMEA.bas contains aliases for various USARTx routines like SetBaudrate so you don't have to change the main program when switching usarts
- interrupts are no longer automatically enabled at startup as part of NMEA.Initialize (set via #option)
The last one is important. It allows you to set up all the hardware prior to enabling the NMEA usart interrupt.
The downside is you now have to call NMEA.EnableInterrupt() when you're ready to start (typically after NMEA.SetBaudrate)
Here's an updated example for the Q41 that shows usage, including PPS setup
Code: Select all
// NMEA example 1 for xv18 and PPS
device = 18F16Q41
clock = 64
// enable hdw shadow register use for _xv18 intr
#if defined(_xv18)
#option ISR_SHADOW_HIGH = true
#option ISR_SHADOW_LOW = true
#endif
// import modules...
include "intosc.bas" // set to use intosc
#option DIGITALIO_INIT = true // call setalldigital at startup
include "setdigitalio.bas" // may be required for usart pins
include "pps.bas" // pps mappable pins
// NMEA module setup for USART2 with RC4=TX and RC5=RX
// you must define the USART2 pin options (used by USART2.bas) before including the NMEA module
// and then assign the pins using PPS. NMEA will include the USARTx.bas file based on the NMEA_USART option
#option USART2_TX = PORTC.4
#option USART2_RX = PORTC.5
#option NMEA_USART = 2 // NMEA uses usart2 (default=1)
Include "NMEA.bas"
Include "USART2.bas" // for usart baudrate constants
Include "Convert.bas"
// local variables
Dim NMEAItem As TNMEA
Dim NMEAField As String
// setup pps (must match USART2 pin options above)
pps.unlock()
pps.assign_input(U2RXPPS, PPS_PORTC, 5) // assigns usart RX2 input to RC5
pps.assign_output(RC4PPS, PPS_UART2_TX) // assigns usart TX2 output to RC4
// this program with display the sentence identifier and the number
// of data fields the sentence has...
NMEA.SetBaudrate(br4800) // USARTx
NMEA.EnableInterrupt() // new - user must enable NMEA interrupt
While True
// get and NMEA item from the buffer, if one is available - check to
// see if checksum is valid before proceeding....
If NMEA.GetItem(NMEAItem) And NMEAItem.Valid Then
NMEA.GetField(NMEAItem,0,NMEAField)
NMEA.Write(NMEAField, " : ", DecToStr(NMEAItem.Count), 13, 10) // USARTx
EndIf
Wend
- Attachments
-
- nmea_v13.zip
- (5.46 KiB) Downloaded 32 times
Re: is there a XV18 version of the NMEA module
Sorry for the late update, but everything worked like a charm!
Worked First try, just updated the module and compiled with my old software on the Q41 chip...
Jerry you are the man!
Thanks for keeping this comunity alive!
Best regards
Sergio
Worked First try, just updated the module and compiled with my old software on the Q41 chip...
Jerry you are the man!
Thanks for keeping this comunity alive!
Best regards
Sergio