# Oshonsoft programs with INTERRUPTS and PARSE

#### sagor

Joined Mar 10, 2019
212
C, the golden rule for any interrupt routine is to make it as short as possible. An Interrupt is to process whatever triggered it, save the bit of information (like a byte that triggered a UART receive interrupt), set a flag or status, then exit the interrupt. Your main program then checks the "flag" and then checks the saved data and only then determines if it is time to process all of it, or wait for more data.
An interrupt routine should never have things like HSEROUT, as that call alone takes more time than the interrupt itself. That is, if receiving characters at 9600 baud, an interrupt processed on byte at a time, and sending a string out simply locks up the next received byte (it is missed while sending output).
Things like string outputs should only be done in the main program. It can be interrupted as it sends, which is ok, provided the interrupt routine gets the trigger event and saves it, then sets a flag.

#### nsaspook

Joined Aug 27, 2009
7,355
C, the golden rule for any interrupt routine is to make it as short as possible. An Interrupt is to process whatever triggered it, save the bit of information (like a byte that triggered a UART receive interrupt), set a flag or status, then exit the interrupt. Your main program then checks the "flag" and then checks the saved data and only then determines if it is time to process all of it, or wait for more data.
An interrupt routine should never have things like HSEROUT, as that call alone takes more time than the interrupt itself. That is, if receiving characters at 9600 baud, an interrupt processed on byte at a time, and sending a string out simply locks up the next received byte (it is missed while sending output).
Things like string outputs should only be done in the main program. It can be interrupted as it sends, which is ok, provided the interrupt routine gets the trigger event and saves it, then sets a flag.
That's really only a golden rule for limited controllers (and limited programming languages) with a single interrupt vector. If you have at least high/low vectors it's fine to have the no-delay I/O functions in the high priority interrupt while having extensive processing in the low priority interrupt code as a task in addition to main loop processing. Proper locking for atomic operations and synchronization is necessary but that's in the game with only a single interrupt vector.

Modern controllers, even at the 8-bit level now have Vectored Interrupt Controllers for even better interrupt control with higher speed and lower I/O latency with each interrupt source having a dedicated interrupt vector address.

#### camerart

Joined Feb 25, 2013
2,049
That's really only a golden rule for limited controllers (and limited programming languages) with a single interrupt vector. If you have at least high/low vectors it's fine to have the no-delay I/O functions in the high priority interrupt while having extensive processing in the low priority interrupt code as a task in addition to main loop processing. Proper locking for atomic operations and synchronization is necessary but that's in the game with only a single interrupt vector.

Modern controllers, even at the 8-bit level now have Vectored Interrupt Controllers for even better interrupt control with higher speed and lower I/O latency with each interrupt source having a dedicated interrupt vector address.
Hi N,
You're reply is on a much higher level, than I am capable of, I don't even know what a Vector is, but thanks for it.
C.

#### camerart

Joined Feb 25, 2013
2,049
C, the golden rule for any interrupt routine is to make it as short as possible. An Interrupt is to process whatever triggered it, save the bit of information (like a byte that triggered a UART receive interrupt), set a flag or status, then exit the interrupt. Your main program then checks the "flag" and then checks the saved data and only then determines if it is time to process all of it, or wait for more data.
An interrupt routine should never have things like HSEROUT, as that call alone takes more time than the interrupt itself. That is, if receiving characters at 9600 baud, an interrupt processed on byte at a time, and sending a string out simply locks up the next received byte (it is missed while sending output).
Things like string outputs should only be done in the main program. It can be interrupted as it sends, which is ok, provided the interrupt routine gets the trigger event and saves it, then sets a flag.
Hi S,
I can see what you mean, and understand that the INTERRUPT must only be used as a trigger, then fly back to the MAIN LOOP.

Actually the INTERRUPT concept is a bit hard to understand, as it's kind of magic. I also found the hardware of a PIC, similar, in that it goes on inside the PIC, but isn't seen.

I think I can find the actual INTERRUPT trigger, by watching a simulation. I'll have a go at writing a new routine and post each step here, hoping for corrections.

At the moment the DATA is placed in a VARIABLE STRING, and I've also got to figure out how to put it in a BUFFER instead. I noticed that in the Simulation, with the UART HARDWARE box open, it shows the incoming DATA going through a BUFFER, and I also saw a reference to BUFFER + 1, which must count through it.

Thanks, C.

#### MrChips

Joined Oct 2, 2009
21,326
Hi C, why do you dumb down yourself so much?
AAC is a learning site, of members helping each other.
Take a little time and effort to understand the little bits thrown your way and you will reap the rewards.

#### camerart

Joined Feb 25, 2013
2,049
Hi MrC,
It's a habit, I've got into, when I'm frustrated, that I should know something, especially, when I may have been shown before.

Actually I was teaching a mate of mine a radio menu, and he has the same habit

I've been checking and I think I've found the INTERRUPT TRIGGER:
-----------------------------------------
If PIR1.RCIF = 1 Then nxt_rxin:
-------------------------------------
Next to figure out how to jump back to the MAIN LOOP.
C.

#### ericgibbs

Joined Jan 29, 2010
10,194
hi C,
On the PIC's you are using, the only vectors are as per this d/s clip.
E

#### Attachments

• 36.2 KB Views: 9

#### camerart

Joined Feb 25, 2013
2,049
Hi,
I've just tried cutting the whole CODE of the INTERRUPT routine apart from, what I think is the trigger.

It compiles, and when a $Sentence is put in the Hardware UART it interrupts and jumps into the MAIN LOOP, and fills the STR1 VARIABLE with all of the digits. Somethings not right, but am I on the right lines? C Thread Starter #### camerart Joined Feb 25, 2013 2,049 hi C, On the PIC's you are using, the only vectors are as per this d/s clip. E Hi E, I'll need to investigate farther, as it crops up elseware, and I would like to understand what they are. C Thread Starter #### camerart Joined Feb 25, 2013 2,049 Hi, With my limited programming skills, instead of what has been suggested, that is too difficult for me, I've made most of #3 INTERRUPT into a SUBROUTINE, apart from the trigger. Does this look ok for now? C. Here: --------------------------------------------------------------------------- Goto main (LOOP) End On High Interrupt Save System 'Await GPS RXD -----------------GPS GNRMC ---------------------- ''''''PASTE:$GNRMC,123519,A,4807.038,N,01131.000,W,022.4,084.4,230394,003.1,W*6A?
'''OR PASTE: $REMOTE,1100,1200,1300,1400,1500,1600W,? (Nonsense, just for testing) '''Or paste:$QEIDEG,123,?

If RCSTA.OERR = 1 Then 'BIT1..if over run error then flush RXD buffer
RCSTA.CREN = 0
char = RCREG '1
char = RCREG '2
PIR1.RCIF = 0 '0 = The EUSART receive buffer is empty
Hserout "RX Err!", CrLf
Goto skip1
Endif

If PIR1.RCIF = 1 Then
Gosub nxt_rxin
Endif

skip1:
Resume
-----------------------------------------------------------------------------------------------
nxt_rxin:
'Add timed wait and DATASWITCH here, needs thought!!!!!!!!!!!!!!
If PIR1.RCIF = 0 Then Goto nxt_rxin
Hserin char
If char = "?" Or char = 0x0a Then Goto msg_eol
If char = "$" Then str1(0) = "$" 'CHAR = $rxi = 0 Endif If str1(0) = "$" Then
str1(rxi) = char
rxi = rxi + 1
Goto nxt_rxin
If rxi > 79 Then
Hserout " OVER STR1 LIMIT", CrLf 'Over STR1() limit
rxi = 0
Endif
Endif
Goto nxt_rxin
Goto skip1

''Hserout CrLf

msg_eol:
'strtp = ""
'strpr = ""
strtx1 = ""
strtx2 = ""
strtx3 = ""
strtx4 = ""
strtx5 = ""
'str1(0) = 0 '????????????????????
strtim = ""
strlat = ""
strlong = ""
msg1 = ""

'For x = 0 To rxi 'REMOVE
'Hserout str1(x)
'Next x

Hserout CrLf, CrLf
csv = 0
'If BAUDCON.RCIDL = 1 Then'NEVER 1! in SIM
'SET DATASWITCH: 'S0=1 S1=0 =TX4431, S0=0 S1=H =HC-12, S0=0 S1=0 =GPS
For txi = 1 To rxi
char = str1(txi)
strchr = Chr(char)
If strchr <> "," Then
msg1 = msg1 + strchr
Else
csv = csv + 1
If str1(5) = "C" Then '1 in DATASWITCH sequence
Gosub get_valg 'GPS
datasw = 2 'Set to next in DATASWITCH sequence
Endif
If str1(1) = "R" Then '2 in sequence
Gosub get_valr 'REMOTE
datasw = 3 'Set to next in sequence
Endif
If str1(1) = "Q" Then '3 in DATASWITCH sequence
Gosub get_valq 'QEIDEG
datasw = 1 'Set to next in sequence
Endif
Endif
Next txi
'Endif
Return
'This subr extracts the main values from the REMOTE string, into named value messages
'also converts the string to a named numeric value, for maths
'----------------------------------------------------------------------
'To avoid a framing error, you are best to switch DATASWITCH between Ascii characters
'by checking BAUDCON.RCIDL=1. If it is =1, Then the UART is idle And Not receiving any
'serial data stream.
'----------------------------------------------------------------------
''This subr extracts the main values from the QEIDEG string, into named value messages.
''also converts the string to a named numeric value, for maths.
get_valq: '$QEIDEG,359,? 'Break '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ qeideg = MidStr(msg1, 7, 3) Return msg1 = "" Return get_valr: 'REMOTE= ,12,20,50,? [remvolt,remalt,remdist] ''Break '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 'If BAUDCON.RCIDL = 1 Then 'SET DATASWITCH: 'S0=1 S1=0 =TX4431, S0=0 S1=H =HC-12, S0=0 S1=0 =GPS 'Endif Select Case csv 'COMMA POSITION VALUES Case 2 strremvolt = LeftStr(msg1, 3) Case 3 strremalt = LeftStr(msg1, 3) Case 4 strremdist = LeftStr(msg1, 3) EndSelect msg1 = "" Return 'This subr extracts the main values from the GPS string, into named value messages 'also converts the string to a named numeric value, for maths get_valg: 'GPS$GNRMC,123519,A,4807.038,N,01131.000,W,022.4,084.4,230394,003.1,W*6A?
''Break '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
'If BAUDCON.RCIDL = 1 Then
'SET DATASWITCH: 'S0=1 S1=0 =TX4431, S0=0 S1=H =HC-12, S0=0 S1=0 =GPS
'Endif
Select Case csv 'COMMA POSITION VALUES
Case 2
strtim = LeftStr(msg1, 2) + ":" + MidStr(msg1, 3, 2) + ":" + MidStr(msg1, 5, 2)
Case 4
strlat = LeftStr(msg1, 10) 'Number of digits in the STRING
sinlat = StrValS(strlat)
Case 6
strlong = RightStr(msg1, 11)
sinlong = StrValS(strlong)
Case Else
EndSelect
msg1 = ""
Return

#### jjw

Joined Dec 24, 2013
540
Hi,
With my limited programming skills, instead of what has been suggested, that is too difficult for me, I've made most of #3 INTERRUPT into a SUBROUTINE, apart from the trigger.
Does this look ok for now?
C.
Here:
---------------------------------------------------------------------------

Goto main (LOOP)
End

On High Interrupt
Save System

'Await GPS RXD -----------------GPS GNRMC ----------------------
''''''PASTE: $GNRMC,123519,A,4807.038,N,01131.000,W,022.4,084.4,230394,003.1,W*6A? '''OR PASTE:$REMOTE,1100,1200,1300,1400,1500,1600W,? (Nonsense, just for testing)
'''Or paste: $QEIDEG,123,? If RCSTA.OERR = 1 Then 'BIT1..if over run error then flush RXD buffer RCSTA.CREN = 0 RCSTA.CREN = 1 'ENABLES RECEIVER char = RCREG '1 char = RCREG '2 PIR1.RCIF = 0 '0 = The EUSART receive buffer is empty Hserout "RX Err!", CrLf Goto skip1 Endif If PIR1.RCIF = 1 Then Gosub nxt_rxin Endif skip1: Resume ----------------------------------------------------------------------------------------------- nxt_rxin: 'Add timed wait and DATASWITCH here, needs thought!!!!!!!!!!!!!! If PIR1.RCIF = 0 Then Goto nxt_rxin Hserin char If char = "?" Or char = 0x0a Then Goto msg_eol If char = "$" Then
str1(0) = "$" 'CHAR =$
rxi = 0
Endif
If str1(0) = "$" Then str1(rxi) = char rxi = rxi + 1 Goto nxt_rxin If rxi > 79 Then Hserout " OVER STR1 LIMIT", CrLf 'Over STR1() limit rxi = 0 Endif Endif Goto nxt_rxin Goto skip1 ''Hserout CrLf msg_eol: 'strtp = "" 'strpr = "" strtx1 = "" strtx2 = "" strtx3 = "" strtx4 = "" strtx5 = "" 'str1(0) = 0 '???????????????????? strtim = "" strlat = "" strlong = "" msg1 = "" 'For x = 0 To rxi 'REMOVE 'Hserout str1(x) 'Next x Hserout CrLf, CrLf csv = 0 'If BAUDCON.RCIDL = 1 Then'NEVER 1! in SIM 'SET DATASWITCH: 'S0=1 S1=0 =TX4431, S0=0 S1=H =HC-12, S0=0 S1=0 =GPS For txi = 1 To rxi char = str1(txi) strchr = Chr(char) If strchr <> "," Then msg1 = msg1 + strchr Else csv = csv + 1 If str1(5) = "C" Then '1 in DATASWITCH sequence Gosub get_valg 'GPS datasw = 2 'Set to next in DATASWITCH sequence Endif If str1(1) = "R" Then '2 in sequence Gosub get_valr 'REMOTE datasw = 3 'Set to next in sequence Endif If str1(1) = "Q" Then '3 in DATASWITCH sequence Gosub get_valq 'QEIDEG datasw = 1 'Set to next in sequence Endif Endif Next txi 'Endif Return 'This subr extracts the main values from the REMOTE string, into named value messages 'also converts the string to a named numeric value, for maths '---------------------------------------------------------------------- 'To avoid a framing error, you are best to switch DATASWITCH between Ascii characters 'by checking BAUDCON.RCIDL=1. If it is =1, Then the UART is idle And Not receiving any 'serial data stream. '---------------------------------------------------------------------- ''This subr extracts the main values from the QEIDEG string, into named value messages. ''also converts the string to a named numeric value, for maths. get_valq: '$QEIDEG,359,?
'Break '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
qeideg = MidStr(msg1, 7, 3)
Return
msg1 = ""
Return
get_valr: 'REMOTE= ,12,20,50,? [remvolt,remalt,remdist]
''Break '@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
'If BAUDCON.RCIDL = 1 Then
'SET DATASWITCH: 'S0=1 S1=0 =TX4431, S0=0 S1=H =HC-12, S0=0 S1=0 =GPS
'Endif
Select Case csv 'COMMA POSITION VALUES
Case 2
strremvolt = LeftStr(msg1, 3)
Case 3
strremalt = LeftStr(msg1, 3)
Case 4
strremdist = LeftStr(msg1, 3)
EndSelect
msg1 = ""
Return
'This subr extracts the main values from the GPS string, into named value messages
'also converts the string to a named numeric value, for maths
Thanks, C.

#### joeyd999

Joined Jun 6, 2011
4,401
In the interrupt:
- read a char from USART
- check a start and end characters and errors.
- msg_buf ( index) = char
- index=index +1
- if end of message then msg_rdy=1
_ resume
I have no idea how this language works, but your pseudocode will clearly result in a buffer overflow.

A circular buffer must be used for async reception -- you never know when the world will send more data than the size of your buffer.

I use head and tail pointers (indices). I write to the head upon character reception, and read from the tail in the main userspace code.

It's easy to tell two conditions: data available to main and buffer full by comparing the two pointers.

I also would not look for EOM in the interrupt routine. A USART driver should be protocol agnostic. Let the main code parse the received characters.

#### camerart

Joined Feb 25, 2013
2,049
I have no idea how this language works, but your pseudocode will clearly result in a buffer overflow.

A circular buffer must be used for async reception -- you never know when the world will send more data than the size of your buffer.

I use head and tail pointers (indices). I write to the head upon character reception, and read from the tail in the main userspace code.

It's easy to tell two conditions: data available to main and buffer full by comparing the two pointers.

I also would not look for EOM in the interrupt routine. A USART driver should be protocol agnostic. Let the main code parse the received characters.
Hi J3,
I thought there was buffer overflow protection in my CODE, but no matter.

I can almost understand what you suggest, but it difficult for me.

I'll try the suggestions and post them.

Thanks. c.

#### jjw

Joined Dec 24, 2013
540
Hi J,
I just tried my method live, and it didn't work any better than before, so I'll have a go at your method, if I can.

How is a BUFFER different from a VARIABLE?
Does a buffer have varying length depending on the $Sentence? Thanks, C. Buffer is here just a term for an array of bytes. Can be anything, message_buffer etc. Dim msg_buf(100) as Byte The length is fixed, but can be made for the longest message. #### ericgibbs Joined Jan 29, 2010 10,194 How is a BUFFER different from a VARIABLE? Does a buffer have varying length depending on the$Sentence?
Hi C,
We were using an array buffer back in 2015.!
Clip:
from a One GPS message capture, no Interrupt used

Dim str1(80) As Byte 'UART RXD buffer array

sync1: 'wait for a $start of string If PIR1.RCIF = 0 Then Goto sync1 char = RCREG If char <> 0x24 Then Goto sync1 '$'
str1(1) = char
rxi = 2

getmsg: 'read and save GPGGA msg
If PIR1.RCIF = 0 Then Goto getmsg
char = RCREG
str1(rxi) = char
rxi = rxi + 1
If rxi > 80 Then Goto get_neo 'msg bfr over run
If char = 0x0a Then Goto eomsg ''''''''''''''''''''''''''
Goto getmsg

eomsg:
If rxi < 60 Then 'invalid msg
Goto get_neo
Endif

#### camerart

Joined Feb 25, 2013
2,049
Buffer is here just a term for an array of bytes.
Can be anything, message_buffer etc.
Dim msg_buf(100) as Byte
The length is fixed, but can be made for the longest message.
Hi J,
As you will recall, I didn't write the INTERRUPT CODE, and wonder why STRINGS were used instead of BUFFERS e,g, (STR1) and likewise it needed the max length setting up.

I'll try to re-write it and substitute STR1 with msg_buf, but why? To me it seems the same.

Am I correct that as with a STRING, with a BUFFER there are as many INTERRUPTs and $Sentence digits? and if another$Sentence is behind the first then it just get's added on the end inside the BUFFER?
C.

#### camerart

Joined Feb 25, 2013
2,049
Hi C,
We were using an array buffer back in 2015.!
Clip:
from a One GPS message capture, no Interrupt used

Dim str1(80) As Byte 'UART RXD buffer array

sync1: 'wait for a $start of string If PIR1.RCIF = 0 Then Goto sync1 char = RCREG If char <> 0x24 Then Goto sync1 '$'
str1(1) = char
rxi = 2

getmsg: 'read and save GPGGA msg
If PIR1.RCIF = 0 Then Goto getmsg
char = RCREG
str1(rxi) = char
rxi = rxi + 1
If rxi > 80 Then Goto get_neo 'msg bfr over run
If char = 0x0a Then Goto eomsg ''''''''''''''''''''''''''
Goto getmsg

eomsg:
If rxi < 60 Then 'invalid msg
Goto get_neo
Endif
Hi E,
Ah so! Is the fact that STRINGS are being used a result of my programming speed and it was kind of left over and?
C.

#### jjw

Joined Dec 24, 2013
540
Hi J,
As you will recall, I didn't write the INTERRUPT CODE, and wonder why STRINGS were used instead of BUFFERS e,g, (STR1) and likewise it needed the max length setting up.

I'll try to re-write it and substitute STR1 with msg_buf, but why? To me it seems the same.

Am I correct that as with a STRING, with a BUFFER there are as many INTERRUPTs and $Sentence digits? and if another$Sentence is behind the first then it just get's added on the end inside the BUFFER?
C.
Actually Str1 in#36 is not a String variable, but an array of bytes (buffer)
You can copy the message when it is complete from the buffer to the main program ( another buffer ) and start receiving the next message with interrupts and start parsing the first message or do whatever is needed.