I need to gather data from both serial ports in succession. I also need high-priority interrupts for other operations, so I combined the ISRRX and NMEA libraries together and am using it as the low priority interrupt. It works - I can run each EUSART together or in succession. Problem is, sometimes one of the buffers is written too far or in the wrong place and ends up clobbering variables and program code. It's tough to debug, I'm using ICD2 and I can only see watches on single locations and not read back whole blocks of code.
It seems that the error occurs after I stop and restart one of the serial port devices. The interrupts are still enabled, the ports are still enabled, just the device stops sending data. I've tried enabling and disabling the ports, and re-initialized the module, no change.
EUSART1 runs at 19200 bps while EUSART2 runs at 9600 bps (it's a GPS). EUSART1 only has to run occasionally.
Here's a copy of the code. If anyone has any suggestions on the code below or debugging in ICD2, please post!
73's,
de Tom.
Code: Select all
{
*****************************************************************************
* Name : NMEA_USARTS.BAS *
* Author : David John Barker *
* Notice : Copyright (c) 2007 Mecanique *
* : All Rights Reserved *
* Date : 24/08/2007 *
* Version : 1.1 *
* : 1.1 - 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_USARTS
// #option to set the NMEA buffer size...
#option NMEA_BUFFER_SIZE = 100 //changed from 200 bytes since the longest string is
#if Not (NMEA_BUFFER_SIZE in (100 to 250))
#error NMEA_BUFFER_SIZE, "Invalid option. Buffer size must be bteween 100 and 250"
#endif
// size of the RX buffer...
#if IsOption(RX_BUFFER_SIZE) And Not (RX_BUFFER_SIZE in (1 to 255))
#error RX_BUFFER_SIZE, "Invalid option. Buffer size must be between 1 and 255 (bytes)."
#endif
#option RX_BUFFER_SIZE = 64
Public Const BufferSize = RX_BUFFER_SIZE
// Import conversion module...
Include "convert.bas"
// import USART library...
Include "USART.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
// local variables and aliases for USART1...
Dim
FUSART1_Buffer(BufferSize) As Byte,
FUSART1_IndexIn As Byte,
FUSART1_IndexOut As Byte,
FUSART1_ByteRead As Byte,
FUSART1_ProcessByte As Boolean,
FUSART1_MaybeOverrun As Boolean
// FUSART1_OnDataEvent As TEvent
// public variables and aliases...
Public Dim
USART1_USARTOverrun As USART.Overrun,
USART1_BufferOverrun As Boolean,
USART1_DataByte As FUSART1_ByteRead,
USART1_DataChar As FUSART1_ByteRead.AsChar,
USART1_ProcessByte As FUSART1_ProcessByte
{
****************************************************************************
* 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 *
* : also contains B0 interrupt vector since it is only high priority*
****************************************************************************
}
Public Interrupt OnNMEAData(1) // (1) so it's a low priority interrupt
Dim Data As Char
// proceed...
// If a byte receive has triggered the interrupt, then context save
// FSR0 and process the data byte...
If PIR3.5 = 1 Then //changed to PIR3 for USART2 used for GPS - EUSART2 flag is set
Save(FSR0, PRODL)
Data = RCREG2 //changed to RCREG2 for USART2 used for GPS
// buffer overflow has occurred, abort...
If FOverflow Then
Exit
EndIf
// stop calculating checksum...
If Data = "*" Then
FCalculatingChecksum = False
EndIf
// calculate checksum...
If FCalculatingChecksum Then
FChecksum = FChecksum Xor RCREG2
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
If PIR1.5 = 1 Then // EUSART1 flag is set
Save(FSR0, PRODL)
USART1_BufferOverrun = FUSART1_MaybeOverrun
If Not USART.Overrun Then
FUSART1_ByteRead = USART.RCRegister
If Not USART1_BufferOverrun Then
FUSART1_ProcessByte = true
If FUSART1_ProcessByte Then
FUSART1_Buffer(FUSART1_IndexIn) = FUSART1_ByteRead
Inc(FUSART1_IndexIn)
If FUSART1_IndexIn > Bound(FUSART1_Buffer) Then
FUSART1_IndexIn = 0
EndIf
FUSART1_MaybeOverrun = (FUSART1_IndexIn = FUSART1_IndexOut)
EndIf
EndIf
EndIf
Restore
EndIf
End Interrupt
{
****************************************************************************
* Name : Initialise (PRIVATE) *
* Notes : Initialise local module variables and configure the USART for *
* : interrupts based receive *
****************************************************************************
}
Public Sub Initialize()
FOverflow = false
FReadingSentence = False
FCalculatingChecksum = False
FChecksum = 0
FCount = 0
FFieldCount = 0
FIndexIn = 0
FIndexOut = 0
PIE3.5 = 1 // enable interrupt on USART2 receive
//EUSART1 variables initialize
FUSART1_IndexIn = 0
FUSART1_IndexOut = 0
FUSART1_MaybeOverrun = false
USART1_BufferOverrun = false
USART.ClearOverrun
USART.RCIEnable = true
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
// *************************************************************************
// EUSART1 subroutines
// *************************************************************************
{
****************************************************************************
* Name : DataAvailable *
* Purpose : Check to see if there is data in the buffer *
****************************************************************************
}
Public Function DataAvailable() As Boolean
Disable(OnNMEAData)
Result = FUSART1_IndexIn <> FUSART1_IndexOut
Enable(OnNMEAData)
End Function
{
****************************************************************************
* Name : Overrun *
* Purpose : Returns true if RC register or buffer has overrun, false *
* : otherwise *
****************************************************************************
}
Public Function Overrun() As Boolean
Result = USART.Overrun Or USART1_BufferOverrun
End Function
{
****************************************************************************
* Name : GetByte (PRIVATE) *
* Purpose : Get a single byte from the buffer *
****************************************************************************
}
Function GetByte() As Byte
FUSART1_MaybeOverrun = false
Result = FUSART1_Buffer(FUSART1_IndexOut)
Inc(FUSART1_IndexOut)
If FUSART1_IndexOut > Bound(FUSART1_Buffer) Then
FUSART1_IndexOut = 0
EndIf
End Function
{
****************************************************************************
* Name : ReadByte *
* Purpose : Read a single byte from the buffer *
****************************************************************************
}
Public Function ReadByte() As Byte
Disable(OnNMEAData)
Result = GetByte
Enable(OnNMEAData)
End Function
{
****************************************************************************
* Name : ReadWord *
* Purpose : Read a word from the buffer *
****************************************************************************
}
Public Function ReadWord() As Word
Disable(OnNMEAData)
Result.Bytes(0) = GetByte
Result.Bytes(1) = GetByte
Enable(OnNMEAData)
End Function
{
****************************************************************************
* Name : ReadLongWord *
* Purpose : Read a long word from the buffer *
****************************************************************************
}
Public Function ReadLongWord() As LongWord
Disable(OnNMEAData)
Result.Bytes(0) = GetByte
Result.Bytes(1) = GetByte
Result.Bytes(2) = GetByte
Result.Bytes(3) = GetByte
Enable(OnNMEAData)
End Function
{
****************************************************************************
* Name : ReadFloat *
* Purpose : Read a floating point number from the buffer *
****************************************************************************
}
Public Function ReadFloat() As Float
Disable(OnNMEAData)
Result.Bytes(0) = GetByte
Result.Bytes(1) = GetByte
Result.Bytes(2) = GetByte
Result.Bytes(3) = GetByte
Enable(OnNMEAData)
End Function
{
****************************************************************************
* Name : ReadStr *
* Purpose : Read a string from the buffer. Optional parameter pTerminator *
* : to specify the input string terminator character. The function *
* : returns the number of characters read *
****************************************************************************
}
Public Function ReadStr(ByRef pText As String, pTerminator As Char = null) As Byte
Dim Ch As Char
Dim Text As POSTINC0
Disable(OnNMEAData)
FSR0 = AddressOf(pText)
Result = 0
Repeat
Ch = GetByte
If Ch <> pTerminator Then
Text = Ch
Inc(Result)
EndIf
Until Ch = pTerminator
Text = 0
Enable(OnNMEAData)
End Function
// ****************************************************************************
// initialise module - from original NMEA module
// ****************************************************************************
Initialize