Home \ Help \ Search \ Login RSS

Welcome, Guest. Please Login

This forum is now READ-ONLY! Please visit http://forum.wiring.co/ for the new forum.

Wiring ForumBugsSoftware Bugs › High interrupt latencies when using Serial

Page Index Toggle Pages: 1
High interrupt latencies when using Serial (Read 6226 times)
09/20/09 at 05:46:49

Pierre   Offline
YaBB Newbies
Phoenix, AZ, USA

Posts: 2
*
 
I am developing an alternative library to control servos with high precision (the Wiring Servo library is, in particular, too latency-sensitive for my needs as it depends on interrupt handlers to flip output pins in real time).
My library provides fully stable servos as long as interrupt latencies stay below 500 microseconds, which should be achievable in the absence of excessive int-disabled-busy-waits in other code.

In one application, I also use Serial to transmit at 9600bps and I observed that doing multiple Serial.print calls in sequence can cause large interrupt latencies of up to 1 millisecond (roughly the transmission time of a byte at 9600bps). I have traced the problem to the way uart_write() works:
After appending the new text to the buffer, it systematically tries to prime the data pump by feeding the head character from the buffer into the USART. Since it busy-waits (with interrupts disabled) for the USART transmit buffer to be empty, it can end up, in the worst case, waiting for a full character to be pushed through the serial line. Instead, it should leave things to the interrupt handler if it finds the USART buffer non-empty since that means that the data pump is already primed.
Once I applied that change (and an associated one in the interrupt handler to keep the USART buffer as filled as possible, thereby minimizing dead times between characters in high interrupt latency environments), measured interrupt latencies fall well below 10 microseconds!

Another problem I encountered is that characters get dropped when the 128 bytes buffer overflows. This seldom happened with the original code due to the busy-waits slowing down the Serial calls, but with the corrected uart_write, it becomes frequent. To avoid this, I added a wait loop up front, but with interrupts enabled so it does not degrade latencies.

Here is the modified code (I only corrected the USART0 code, not USART1):
Code:
void uart_write(uint8_t uart, char *buf, uint8_t len)
{
  uint8_t i;
  uint8_t ints;
  // return if nothing to send
  if (len == 0){
    return;
  }
#if 1  // ### Pierre 090919
  // Make sure we don't overflow the buffer
  while ((buffer_available((uart == 0) ? &uart0_txBuffer : &uart1_txBuffer) + len) > UART_BUFFER_LENGTH) {
    if (len > UART_BUFFER_LENGTH) len = UART_BUFFER_LENGTH;
  }
#endif
  // record interrupts
  ints = SREG & 0x80;
  // clear interrupts
  cli();
  // prepare for interrupt handler
  if(0 == uart){
    // append to tx buffer
    for(i = 0; i < len; ++i){
      buffer_put(&uart0_txBuffer, buf[i]);
    }
#if 0 // ### original 0.19 code
    // transmit first byte
    UCSR0A |= _BV(TXC0);
    while (bit_is_clear(UCSR0A, UDRE)){
      continue;
    }
    UDR0 = buffer_get(&uart0_txBuffer);
#else // ### Pierre 090919 (applied to UART0 only for the moment)
    if (bit_is_set(UCSR0A, UDRE)) { // xmit buffer is empty (shift-register may be busy though)
      // feed one byte into buffer (moves into shift-register if the latter is idle)
      UDR0 = buffer_get(&uart0_txBuffer);
      if (0 != buffer_available(&uart0_txBuffer)) if (bit_is_set(UCSR0A, UDRE)) {
        // feed another byte in the buffer if it is empty again
        UDR0 = buffer_get(&uart0_txBuffer);
      }
    }
#endif
  }else{
    // append to tx buffer
    for(i = 0; i < len; ++i){
      buffer_put(&uart1_txBuffer, buf[i]);
    }
    // transmit first byte
    UCSR1A |= _BV(TXC1);
    while (bit_is_clear(UCSR1A, UDRE)){
      continue;
    }
    UDR1 = buffer_get(&uart1_txBuffer);
  }
  // reenable interrupts
  SREG |= ints;
}

// UART0 transmit complete interrupt
SIGNAL(SIG_UART0_TRANS)
{
  // return if nothing left to send
  if (0 == buffer_available(&uart0_txBuffer)){
    return;
  }
  // grab character from TX buffer
  UDR0 = buffer_get(&uart0_txBuffer);
#if 1 // ### Pierre 090919 (applied to UART0 only for the moment)
  if (0 == buffer_available(&uart0_txBuffer)) return;
  if (bit_is_set(UCSR0A, UDRE)) {
    // feed another byte into buffer
    UDR0 = buffer_get(&uart0_txBuffer);
  }
#endif
} 



By the way, I also noted a small bug in buffer.c:
Code:
uint8_t buffer_available(buffer_t* b)
{
	uint8_t count;
	CRITICAL_SECTION_START;
	count = b->cnt;
	CRITICAL_SECTION_END;
//	return b->cnt;
	return count; // ### Pierre 090919
} 

 
IP Logged
 
Reply #1 - 09/21/09 at 06:34:47

barragan   Offline
YaBB Administrator

Posts: 939
*****
 
Dear Pierre, thank you very much for submitting the patch, I´ll check it and incorporate it into svn for next release. Please let me know if you are interested in help us in other matters Smiley
Thanks again,
Hernando.
 
IP Logged
 
Reply #2 - 09/22/09 at 03:48:59

Pierre   Offline
YaBB Newbies
Phoenix, AZ, USA

Posts: 2
*
 
You are very welcome, Hernando.
Actually, I'd be glad to contribute some of the software I developed for my current Wiring-based project. Specifically, I have:
- A class for reading commands from a PlayStation2 controller (I am using a Logitech wireless model)
- A class for displaying on a single or a pair of 7-segments LED display units (e.g. NTE3079)
- The previously mentioned PreciseServo class, which can control up to 6 servos per PWM pin at 1/10 degree granularity, at the cost of one other generic pin per servo and a bit of external hardware (for each servo: one AND gate and resistor)

I'll polish them for release once I have finished the current project version...
 
IP Logged
 
Reply #3 - 09/30/09 at 07:07:26

barragan   Offline
YaBB Administrator

Posts: 939
*****
 
Hi Pierre, I have updated SVN with the changes, are you building the software from the source? If not, I can upload a release candidate for you to test, let me know which OS are you using. I have updated the avr toolchain, and fix the OSX snow leopard issues. Let me know.
Please also send me your contact info to add you as contributor.
 
IP Logged
 
Page Index Toggle Pages: 1