Serial input using USART - interrupt driven?

We are using Uno and/or Mega and want to be able to write an interrupt handler (in the Arduino programming environment) that gets called when the USART has received a frame. In the interrupt handler we would like to use Serial.read() to put the data in our own-defined buffer.

Of course one can manually set the ‘Receive complete interrupt enable’ flag on the USART, but then how do we vector the interrupt to a named method (our handler)?

So, the ‘wishful thinking’ is something like
// in Setup: enable USART interrupt
attachInterrupt(, myHandler, …) // an extension of attachInterrupt - hides all the details of interrupt vectrors, etc.

void myHandler()
{
myData= Serial.read()
// put MyData in my buffer

}

Is this completely wishful thinking - i.e. that we could still operate in the Arduino programming environment and handle USART interrupts?

We have looked at NewSoftSerial but can’t find out how to break in to the interrupt handler and put the data in our own buffer. The public methods don’t mention the buffer that’s used (only overflow()). Also, it seems NewSoftSerial uses software to do all the USART functions, which seems unnecessary use of CPU time in our application.

We would be grateful of any ideas.

To create an ISR you do the following

ISR (USART_RX_vect) { }

I assume that if you do that for the UART vector the Serial functions use you will get a compiler error though. However I thought I saw someone doing this and it worked.

If you look in HardwareSerial.c in your install you will see the ISRs (they use the old format SIGNAL(SIG_USART0_RECV)), they all call store_char() so you could modify that function.

NSS does use software so that's no good for you.


Rob

Ok I just tried adding a UART ISR to a sketch using the old style syntax

SIGNAL(SIG_USART0_RECV) { }

and it compiled (ISR (USART_RX_vect) does not compile). Whether it works and replaces the standard ISR I have no idea.


Rob

Thanks, Rob. First, where do I find doc. about the ISR() function, you mention. It's not in the Language Reference.

We will try what you suggest, but our first experience with using Serial.read() in (a hardware-triggered) ISR is that the data is lost. The Language Reference for attachInterrupt says in fact:

Inside the attached function, delay() won't work and the value returned by millis() will not increment. Serial data received while in the function may be lost. You should declare as volatile any variables that you modify within the attached function.

I don't know if this means we can't use Serial.read() in an ISR, as we are not actually receiving data "while in the function".

Roger.

attachInterrupt doesn't really have anything to do with serial unless you want to trigger an interrupt on the start bit if a character. I don't think that is appropriate here.

can't use Serial.read() in an ISR

Very bad practice even if it does work.

where do I find doc. about the ISR() function, you mention

No idea I'm afraid, it's way out of the Arduino reference scope, I know this sort of thing from using AVR outside of the Arduino world. If you look in

C:\Program Files\Arduino\arduino-00xx\hardware\tools\avr\avr\include\avr\iom328p.h

(or wherever your install is) starting at on line 789 you'll see the standard names for all the vectors. There will be an equivalent file for the 2560 as well although I can't find it with a quick look.

EDIT: Found it C:\Program Files\Arduino\arduino-0021\hardware\tools\avr\avr\include\avr\iomxx0_1.h

Normally you do like I said

ISR (USART_RX_vect) { }

but I tried that and it doesn't compile. However

SIGNAL(SIG_USART0_RECV) { }

Is the AVR's old style that is not supposed to be used any more AFAIK but the definition is still there and that did work. Just try this

SIGNAL(SIG_USART0_RECV) { Serial.print ('x'); }

Yes I know I said not to do a print inside an ISR but just for a quick test. Send characters to the Arduino and see if it echos xxxxx.

If it doesn't work then

SIGNAL(SIG_USART0_RECV) { digitalWrite (13, HIGH); }

and see if the LED lights up.


Rob

Just swapped to a Mega board and

ISR (USART3_RX_vect) { }

compiles. So you can try the above with the other serial ports.


Rob

Thanks. In the meantime, why is it so bad practice to use Serial.read() in an ISR? That's exactly what one wants to do: grab the data when it arrives!

Also, do you know how to communicate data beween main program and ISR - shared variables? (When we eventualy get the data, we want to store it in our own, accessible buffer).

Roger.

why is it so bad practice to use Serial.read() in an ISR?

ISRs should be short and sweet because while in one nothing else can work. Normally you just set a flag and have the main program deal with things. There are exceptions, I have a program that spends quite a lot of time in an ISR, but I had a good reason.

do you know how to communicate data beween main program and ISR - shared variables?

Any variable used by both an ISR and normal code should be declared volatile, as follows

volatile int myFlag;

ISR (SOME_vector) {
   myFlag = true;
}


loop () {
   if (myFlag) {
       do_stuff();
       myFlag = false;
   }
}

When we eventualy get the data, we want to store it in our own, accessible buffer

Then something like this

volatile int newFrameAvailable;
volatile byte buffer [100];
volatile int index;

ISR (USART3_RX_vect) {
   buffer [index++] = get the character from the UART
   if (this is the end of the frame)
         newFrameAvailable = true;
}


loop () {
   if (newFrameAvailable) {
       handle_frame();
       newFrameAvailable = false;
       index = 0;
   }
}

This assumes that "if (this is the end of the frame)" is a simple thing like comparing a character or some such. If it's complicated then set a "charAvailable" flag instead and do it outside the ISR.


Rob

One thing though, Serial.read() is already interrupt driven, why do you need to re invent it?


Rob