Let's talk about serial interrupt handling!

This forum is dedicated to software development related to MultiWii.
It is not the right place to submit a setup problem.
Software download
Post Reply
Danal
Posts: 137
Joined: Tue Oct 18, 2011 5:15 pm

Let's talk about serial interrupt handling!

Post by Danal »

The newer releases of MultiWii contain interrupt handlers for receiving serial data. They look like this:

Code: Select all

SIGNAL(USARTn_RX_vect){
  uint8_t i = (serialHeadRX[n] + 1) % SERIAL_RX_BUFFER_SIZE;
  if (i != serialTailRX[n]) {serialBufferRX[serialHeadRX[n]][n] = UDRn; serialHeadRX[n] = i;}
}

Where all the lowercase 'n' characters are replaced by the USART number.


This code is very much like the Arduino Core "HardwareSerial.cpp" library. The library uses some 'inline' routines; the resulting code is nearly, but not quite, identical. In both places, the code is intentionally written to stop when the buffer is about to 'wrap' or 'overflow. To quote the library:

// if we should be storing the received character into the location
// just before the tail (meaning that the head would advance to the
// current location of the tail), we're about to overflow the buffer
// and so we don't write the character or advance the head.

However, there is a crucial difference. The library routing ALWAYS reads the UDRn register. The code in MultiWii does not, if an overflow is about to occur. Unfortunately, there is an negative interaction with ATmega hardware if UDRn is not read. The read is required to clear the interrupt. If the read does not occur, as soon as the interrupt handler exits, the interrupt will re-interrupt. Since the buffer is still full, the sketch ends up doing nothing but taking that interrupt over and over. From an outside view, everything just dies.

I believe this is an undesired behavior, but this serial stuff is not my code. There are two ways we could change it. First, we could follow the Arduino serial library and discard data once the buffer is full. This leaves the oldest data in the buffer, and discards the newest:

Code: Select all

SIGNAL(USARTn_RX_vect){
  uint8_t i = (serialHeadRX[n] + 1) % SERIAL_RX_BUFFER_SIZE;
  uint8_t c = UDRn;
  if (i != serialTailRX[n]) {serialBufferRX[serialHeadRX[n]][n] = c; serialHeadRX[n] = i;}
}


Or, we could read the freshest data, and advance the buffer tail to discard the oldest unread:

Code: Select all

SIGNAL(USARTn_RX_vect){
  uint8_t i = (serialHeadRX[n] + 1) % SERIAL_RX_BUFFER_SIZE;
  serialBufferRX[serialHeadRX[n]][n] = UDRn; serialHeadRX[n] = i;
  if (i = serialTailRX[n]) serialTailRX[n] = (serialTailRX[n] + 1) % SERIAL_RX_BUFFER_SIZE;
}


Which do you prefer?




I believe, given the kinds of things we are reading most of the time, like Spektrum or SBUS receivers, or GUIs, or pushbuttons on LCDs for configuration, that the freshest data is the most desirable. I therefore vote for the second fix.

But, again, which do you prefer?

ziss_dm
Posts: 529
Joined: Tue Mar 08, 2011 5:26 am

Re: Let's talk about serial interrupt handling!

Post by ziss_dm »

Hi Danal,
It is actually does not matter. In case of buffer overrun you would have problems in both cases. ;)
The first case is standard behavior of all libraries on most platforms.

BTW: I think, it is better to read UDRn as first operation in the ISR, to free hw FIFO ASAP.

regards,
ziss_dm

Danal
Posts: 137
Joined: Tue Oct 18, 2011 5:15 pm

Re: Let's talk about serial interrupt handling!

Post by Danal »

ziss_dm wrote:Hi Danal,
It is actually does not matter. In case of buffer overrun you would have problems in both cases. ;)
Agreed... but surely those problems are better than locking up the whole sketch?
The first case is standard behavior of all libraries on most platforms.
Agreed. But that doesn't make it right for what we are doing. Or wrong either. ;)

BTW: I think, it is better to read UDRn as first operation in the ISR, to free hw FIFO ASAP.
Even at 115200 baud, there are 1389 ticks of the system clock between bytes. Given a big handful of clock ticks to enter the ISR, push things, and so forth, a couple of instructions plus or minus doesn't make a realistic difference. Nonetheless, if I am the person who ends up changing this, I will move the read to the front. :ugeek:

regards,
ziss_dm



So, how are you voting? Does your comment regarding "standard behavior" indicate you prefer that on MultiWii?

Cheers,

Danal

Alexinparis
Posts: 1630
Joined: Wed Jan 19, 2011 9:07 pm

Re: Let's talk about serial interrupt handling!

Post by Alexinparis »

Hi,

I think also the first case is preferable.
It's better to loose future data if it can help to handle the current one. otherwise, it would always fail.
It's basically the same thing as the Arduino HardwareSerial implementation, but with 8 bit pointers (much more efficient to my mind)

Code: Select all

ISR(USARTn_RX_vect) {
   uint8_t c = UDRn;
   uint8_t i = (serialHeadRX[n] + 1) % SERIAL_RX_BUFFER_SIZE;
   if (i != serialTailRX[n]) {serialBufferRX[serialHeadRX[n]][n] = c; serialHeadRX[n] = i;}
}


I think we can also implement this function after each receiver frame if it can help to make the buffer clean between 2 frames:

Code: Select all

void SerialFlushRX(uint8_t port) {
    serialHeadRX[port] = serialTailRX[port];
}

User avatar
Hamburger
Posts: 2578
Joined: Tue Mar 01, 2011 2:14 pm
Location: air
Contact:

Re: Let's talk about serial interrupt handling!

Post by Hamburger »

not sure if this relevant here, but I just found some strange side effects when additionally having LCD_ETPP configured in. It does add some more write activity, I presume. More info here viewtopic.php?f=8&t=990&start=70#p7010.

If it applies to this topic as well it might have influence on preferred choice?

Danal
Posts: 137
Joined: Tue Oct 18, 2011 5:15 pm

Re: Let's talk about serial interrupt handling!

Post by Danal »

So far, 2 to 1 votes to discard the incoming data if the buffer is full. That's the direction I'll head for the moment.

Others?

User avatar
Hamburger
Posts: 2578
Joined: Tue Mar 01, 2011 2:14 pm
Location: air
Contact:

Re: Let's talk about serial interrupt handling!

Post by Hamburger »

+r because it is common strategy and no pressing reason to deviate was given,imho

Post Reply