Fastest interrupt handler for background serial receive

MStory

New Member
I'm receiving GPS data at 9600 Baud using hardware serial reception with a 28X2 running at 64 MHz. It's working, but I want the interrupt to process as quick as possible in order to maximize the processor time available for the main program. I pasted my interrupt code below.

So, my question to the community... Is there any way to do this with fewer clock cycles?

Any comments or suggestions would be much appreciated!

CODE:
interrupt:
ptr = hserptr-1
RxByte = @ptr
if RxByte = "$" then
EOM_Flag=false ;end of message flag
Label_Location=ptr
elseif RxByte = 0x0A then
EOM_Flag=true
hserptr=0 ;reset hserptr in order to avoid buffer wrap-around
endif
hserinflag = 0
setintflags %00100000,%00100000
return ;end of interrupt
End CODE:
 

inglewoodpete

Senior Member
I can see that your interrupt routine will execute when every character arrives in the scratchpad buffer. At 9600 baud, that will occur every 1.04mS. Your interrupt code gobbles up a significant amount of that time every character.

I suggest you dispense with interrupts and have your main loop do the searching for key characters. From memory, GPS time packets are typically output by a GPS module once every second. You don't mention the application you are developing, therefore what overheads you have to process in between each data packet, so I don't know if my code referenced below would offer a suitable basis for your project.

A few years ago, I developed some PICAXE software for a 'clock'. The PICAXE did a satisfactory job of decoding GPGGA packets while running at 16MHz, although there is no reason why you shouldn't run the PICAXE at 64MHz if you see the need.

While the demo code I provided in -this post- does use interrupts, they are not essential for background serial data reception. The code I posted may provide a more efficient algorythm that suits your application.

Note that the SerTxd command is bit-banged and will interfere with background serial interrupts, potentially resulting in corruption of hSerial data.
 
Last edited:

MStory

New Member
Thanks for your quick reply and suggestions. I will study the code you posted and attempt to improve my project accordingly.

FYI: My 28X2 is also an i2c slave. A second PICAXE (i2c master) communicates with the slave's scratchpad at ptr locations beyond the GPS messages. It's for that reason I reset the hserptr to zero after each GPS sentence is received. I need to keep the GPS data from overwriting the i2c data locations.
 

bpowell

Senior Member
We need some PICAXE built on the new PIC-18 chips that have Direct Memory Access...then you can just slam GPS sentences directly from the UART buffer to memory without bothering the CPU at all!
 

papaof2

Senior Member
Does that sound like "one core, two threads" to anyone else? Will the base PIC chips have a dual core version soon? ;-)
 

bpowell

Senior Member
Does that sound like "one core, two threads" to anyone else? Will the base PIC chips have a dual core version soon? ;-)
The DMA Controller (DMAC) could be considered a "second core" ... it's just a core that only does one task: Move bytes from A to B based on certain criteria. Once you configure it, it'll do its thing without needing input from the CPU
 

MStory

New Member
After 11 years of reading Forum threads, this is the first question I've posted. I'm just happy to see that I got a response and started a discussion!
 

papaof2

Senior Member
People here are generally very good about answering any question they feel they're qualified to answer and a lot of them either have eidetic memory or are very good with the search tools because they can refer you to something posted X years ago that's very close to what you asked. I find myself going back and reading a lot of those ;-)
 

Flenser

Senior Member
MStory,

What GPS message, or messages are you trying to handle?

I don't understand why you need an interrupt handler triggered as each character is received when you can't process the whole GPS message until after the complete message has been received.

If all you need to do is detect that the end of the message has been received then can't you do that in the main program along these lines:
Code:
main:
ptr = hserptr-1
RxByte = @ptr
if RxByte = "$" then
    EOM_Flag=false        ;end of message flag
    Label_Location=ptr
elseif RxByte = 0x0A then
    EOM_Flag=true
    hserptr=0                 ;reset hserptr in order to avoid buffer wrap-around
endif

if EOM_Flag=true then
< process the message >
endif

goto main
 

Flenser

Senior Member
The DMA Controller (DMAC) could be considered a "second core" ... it's just a core that only does one task: Move bytes from A to B based on certain criteria
That sounds like an answer to serial receive on the PICAXE
bpowell & papaof2,
I have to stand up for Rev-Ed.

I've had a look at the datasheet for the PIC18-Q43 chip, which has DMA controllers and:
1) The UART peripheral has the same 2 byte receive buffer that I think all the PICAXE chips have, and
2) the DMA controller does use CPU cycles through one of two methods
- Stalling the CPU execution until it has completed its transfers (DMA has higher priority over the CPU in this mode of operation)
- Utilizing unused CPU cycles for DMA transfers (CPU has higher priority over the DMA in this mode of operation). Unused CPU cycles are referred to as bubbles, which are instruction cycles available for use by the DMA to perform read and write operations..

So while the DMA controller could be used to move bytes from A to B this seems to me to be a function that Rev-Ed already provide with their X2 background receive feature.
 

bpowell

Senior Member
bpowell & papaof2,
I have to stand up for Rev-Ed.
I'm not bashing or talking down Rev-Ed / PICAXE ...

So while the DMA controller could be used to move bytes from A to B this seems to me to be a function that Rev-Ed already provide with their X2 background receive feature.
I suspect the X2 "Background Receive" is just the hardware UART RX buffer ... which is a core-independent-peripheral ... that is, the UART can receive 2 bytes of data and latch them into the buffer without ever having to bother the core CPU.

However, if you want to move those two bytes somewhere else, then you're going to need CPU intervention ... using a hardware interrupt for instance, would work great...you could react to the RXBuffer Full interrupt, and then pause current code, push all the key registers to the stack, move to the interrupt, execute the interrupt, pop all the registers back from the stack, move back to where the code was interrupted, and resume execution. (That's the process for any interrupt).

Or, you could configure the DMA to notice, "Hey, a byte has arrived at location A, and my job is to move xx number of bytes from A to B ..." the DMA would then wait for access to the data bus (or, take access of the bus, based on the mode) and move from A to B and then release the bus.

So, while the DMA takes CPU "Cycles", it doesn't utilize the CPU ... it simply needs access to the data bus to move bytes around...and it can't do that while the CPU is using the same bus at the same time. So, as you probably noted further down in the datasheet, the DMAC can utilize time that would otherwise be wasted .... e.g.
Code:
1. Access to the Program Flash Memory, then the peripheral waits for an instruction cycle in which the CPU does
not need to access the PFM (such as a branch instruction) and uses that cycle to do its own Program Flash
Memory access, unless a PFM Read/Write operation is in progress.
2. Access to the SFR/GPR, then the peripheral waits for an instruction cycle in which the CPU does not need to
access the SFR/GPR (such as MOVLW, CALL, NOP) and uses that cycle to do its own SFR/GPR access.
So, DMA is different than the "Background Receive" feature from PICAXE ... in my mind, it's more efficient and faster than utilizing interrupts...but, it's not yet available in PICAXE, so it's a moot point.

But as an example: I'm using DMA on a PIC18 to receive two GPS sentences at 115,200 baud and at 2 (or 5?) hz frequency ... the chip happily runs other code while the DMAC receives bytes and stores them into an array, and only takes a break when it's time to parse those sentences and do things with the received data. I could probably go faster on the baud rate, but it's a chore to re-program the UBLOX GPS, so I haven't tried to up the bps on it.
 

bpowell

Senior Member
I don't understand why you need an interrupt handler triggered as each character is received when you can't process the whole GPS message until after the complete message has been received.
The example you provided would make the PICAXE do nothing BUT receive bytes, check for 0x0A, and of not there, receive the next byte ... the use of interrupts allows the chip to do other things while data is being received.
 

Flenser

Senior Member
The example you provided would make the PICAXE do nothing BUT receive bytes, check for 0x0A, and of not there, receive the next byte
You are absolutely correct. My example is intended to do nothing but receive bytes between the end of processing the last message received and the 0x0A at the end of the next message sent.

This is why I've asked MStory what GPS message(s) he wants to process.
For example, if he just wants to process the GPGGA message that has time, longitude, latitude, nbr of satellites and Altitude then that message is about 68 characters long and at 9600 baud it will take about 68ms to receive.

If the GPS is sending it's messages once per second my example was intended to illustrate the following timings:
1) Wait until we detect the start of the first GPGGA message
2) Loop until we have received the whole GPGGA message. This step will take about 68ms@9600 baud.
3) Process the received GPGGA message. You could have around 900ms to process each GPGGA message plus any other processing your program needs to do before you have to loop back to step 1 to wait for the start of the next GPGGA message.
 

Flenser

Senior Member
I suspect the X2 "Background Receive" is just the hardware UART RX buffer
The X2 background receive puts received characters into the scratchpad circular buffer so my guess is that it could be a hardware interrupt that reads the just received byte from the hardware UART RX buffer, writes it to the next location in the scratchpad buffer, updates the circular buffer pointer and clears the interrupt.
As you point out this will consume CPU cycles but I'm not convinced that the CPU it consumes is so high that it makes DMA copying dramatically better.

My comment about standing up for Rev-Ed could have been poorly phrased because I did not intend to imply either you or papaof2 were bashing Rev-Ed or PICAXE.

I should have said something more like "the Rev-Ed's X2 background receive feature already provides most of what the DMA background serial receive you describe would provide."
 

bpowell

Senior Member
My comment about standing up for Rev-Ed could have been poorly phrased because I did not intend to imply either you or papaof2 were bashing Rev-Ed or PICAXE.

I should have said something more like "the Rev-Ed's X2 background receive feature already provides most of what the DMA background serial receive you describe would provide."
No worries! I just wanted to be clear that I wasn't bashing on Rev or PICAXE. :)

As you point out this will consume CPU cycles but I'm not convinced that the CPU it consumes is so high that it makes DMA copying dramatically better.
Ug ... these are exactly the kind of questions that keep me up at night, and for no good reason! I'm taking a long flight today, and will probably spend it noodling on how this could be measured!

Maybe just have a program that toggles a pin at 10hz ... then enable the background receive and see if there is any impact to the 10hz waveform? Do the same w/ a PIC and DMA?

I don't know why, but I enjoy investigating things like that. I'll have to "favorite" this thread!
 

papaof2

Senior Member
Sounds like me getting "hooked" by how some piece of equipment works and then writing my own detailed manual about how it works ;-)
If it weren't for the curiosity of many of the members here, the questions posted would not generate nearly as much response.
 

inglewoodpete

Senior Member
Maybe just have a program that toggles a pin at 10hz ... then enable the background receive and see if there is any impact to the 10hz waveform? Do the same w/ a PIC and DMA?
Background serial data (and i2c, background timer) use the underlying chip's interupts to store received data (and perform other functions). The overheads of performing this are not particularly high but would affect some critically timed foreground commands.

Several legacy bit-banged foreground commands (like SerTxd, SerRxd, SerIn, SerOut, OWxx, IRIn...) that use strict timing are known to disable the chip's background interrupts while executing. This can potentially cause data corruption/distortion of any background timer, hSerial and hi2c operation.
 

hippy

Ex-Staff (retired)
The X2 background receive puts received characters into the scratchpad circular buffer so my guess is that it could be a hardware interrupt that reads the just received byte from the hardware UART RX buffer, writes it to the next location in the scratchpad buffer, updates the circular buffer pointer and clears the interrupt.
Yup; exactly that. It's all done pretty quickly so will have minimal impact on an executing PICAXE program.

This internal interrupt will also set the PICAXE firmware flag to indicate a received byte was placed in Scratchpad which means, soon after the PICAXE program resumes execution, there will be a call into the user's 'Interrupt:' routine if enabled for that.

It can be challenging to use Scratchpad for two things especially as background serial and I2C slave both want to use locations zero upwards.

My approach, though not sure if I have ever tested it, would be to interrupt on every serial byte received, check if it's nearing the end of Scratchpad, and if so set the 'hSerinPtr' to where the serial buffer starts in Scratchpad. One would probably need to add a marker so everything else knows there was a wrap-back and received doesn't continue to the end of Scratchpad.

Then I'd simply run through the serial buffer as I would without interrupts .

Untested and incomplete, but something like -
Code:
Symbol SLAVE_START    = 0
Symbol SLAVE_END      = 255

Symbol SERIAL_START   = 256
Symbol SERIAL_MAX     = 1000
Symbol SERIAL_END     = 1023

Symbol SERIAL_WRAPPED = 0xFE

Main:
  hSerInPtr = SERIAL_START
  Gosub Initialise
  ptr = SERIAL_START
  Do
    Gosub GetSerialByte
    SerTxd("Got ", #b0, " ", b0, CR, LF)
  Loop

GetSerialByte:
  Do
    Do : Loop While ptr = hSerInPtr
    b0 = @ptrInc
    If b0 != SERIAL_WRAPPED Then
      Return
    End If
    ptr = SERIAL_START
  Loop

Interrupt:
  If hSerInPtr > SERIAL_MAX Then
    Put hSerInPtr, SERIAL_WRAPPED
    hSerInPtr = SERIAL_START
  End If
  Return
 
Top