Electronic – USART ISR keeps repeating 4 times

avravr-gccinterruptsuart

​Hi,

​I'm having a problem with the USART_RX_vect interrupt of the ATMega 328p. The receiving works fine and I'm able to evaluate the sent bits (so it's not due to the baudrate) but everytime I send a bit, the ISR gets executed 4 times.​

So a ISR like this:

ISR(USART_RX_vect) {
    uart_transmit('t');
    uart_flush();
}

Will transmit {'t', 't', 't', 't'}​

I have no clue why this is happening, and I hope you can help me.


My main is a empty loop (of course I execute uart_setup). My ISR is like above. I'm using avr-gcc on linux.

This is my UART Setup:

void uart_setup() {                                                                                                                                                                                                 
      UBRR0H = (BAUDRATE >> 8);                                                                                                                                                                                       
      UBRR0L = BAUDRATE;                                                                                                                                                                                              

      // Set 8-Bit mode                                                                                                                                                                                               
      setBit(UCSR0C, UCSZ01);                                                                                                                                                                                         
       setBit(UCSR0C, UCSZ00);                                                                                                                                                                                         

      setBit(UCSR0B, RXEN0); // Receiver enable                                                                                                                                                                       
      setBit(UCSR0B, TXEN0); // Transmitter enable                                                                                                                                                                    

      setBit(UCSR0B, RXCIE0); // Enable RX Interrupt                                                                                                                                                                  
      sei(); // Enable global interrupts                                                                                                                                                                              
  }       

void uart_transmit(unsigned char data)                                                                 
{                                                                                                      
    while (!( UCSR0A & (1<<UDRE0))); // wait while register is free                     
    UDR0 = data; // load data in the register                        
}         

void uart_flush( void ) {                                                                              
          unsigned char dummy;                                                                           
          while ( UCSR0A & (1<<RXC0) ) dummy = UDR0;                                                     
  }                                                                                                                                                                                                                                                                                                                                                   

Best Answer

It appears that the design of your code is such that when a byte is received in the UART, you wish to dispose of that byte and respond by transmitting a "t".

The UART shares a common receive/transmit buffer register. Since your UART receive routine is interrupt driven, you should immediately read the received character before putting the response in the buffer otherwise you will retrigger the receive interrupt when you exit.

There is also no reason to have the 'while' code in the uart_flush routine since you know there is a character in the buffer based on the fact that an interrupt brought you to this point. Just read the buffer register.

Here is a simplified version of a corrected ISR without the functions:

ISR(USART_RX_vect) {
     unsigned char dummy;
     dummy = UDR0;                     // dump the rcvr buffer
     while (!( UCSR0A & (1<<UDRE0)));  // wait until the register is free                     
     UDR0 = data;                      // load xmt data in the register
}
Related Topic