Search code examples
serial-portmicrocontrolleravrusartatmega16

Can UDR be written inside USART_RXC interrupt handler without checking the UDRE flag in AVR ATMega32?


I have been trying to understand this code which is supposed to echo each byte received on the AVR's USART interface using interrupt.

#include 
#include 

#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

int main (void)
{
   UCSRB = (1 << RXEN) | (1 << TXEN);   // Turn on the transmission and reception circuitry
   UCSRC = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); // Use 8-bit character sizes

   UBRRH = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
   UBRRL = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register

   UCSRB |= (1 << RCXIE); // Enable the USART Recieve Complete interrupt (USART_RXC)
   sei(); // Enable the Global Interrupt Enable flag so that interrupts can be processed

   for (;;) // Loop forever
   {
         // Do nothing - echoing is handled by the ISR instead of in the main loop
   }   
}

ISR(USART_RXC_vect)
{
   char ReceivedByte;
   ReceivedByte = UDR; // Fetch the received byte value into the variable "ByteReceived"
   UDR = ReceivedByte; // Echo back the received byte back to the computer
}

I'm having troubles understanding this part

ISR(USART_RXC_vect)
    {
       char ReceivedByte;
       ReceivedByte = UDR; // Fetch the received byte value into the variable "ByteReceived"
       UDR = ReceivedByte; // Echo back the received byte back to the computer
    }

Why isnt the UDRE flag checked here to see if indeed new data can be written, without overwriting previous data? Since we do the same thing in the corresponding polling approach:

while ((UCSRA & (1 << RXC)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
ReceivedByte = UDR; // Fetch the received byte value into the variable "ByteReceived"

while ((UCSRA & (1 << UDRE)) == 0) {}; // Do nothing until UDR is ready for more data to be written to it
UDR = ReceivedByte; // Echo back the received byte back to the computer

Shouldn't there be a checking for the UDRE flag before the UDR = ReceivedByte; line inside the ISR too? Any insights would be much appreciated.


Solution

  • Short answer is - yes: you can write UDR in any time you want, without any checks ahead.

    But, if output buffer is full (the UDRE flag in UCSRA is not set) then written data will be ignored by the transmitter, or, in other words, it will be lost.

    The USART module have a double output buffer. That means it is possible to write there two bytes ahead: one is being transmitted right now and one in the buffer will be transmitted later. UDRE flags show when the buffer byte is empty, while TXC flag shows when transmitting byte is pulled out.

    So, if you have a way to be sure the transmitter buffer will not be overrun, then it is possible not to check that flag at all. Since receiving of the byte requires exactly the same time as transmitting, you can be sure that RXC interrupts will not be happened more often than bytes will be transmitted, so, it if UDR is not written elsewhere, it is possible to assume that output buffer can always accept at least one-byte, when RXC interrupt happens.

    Still, if UDR is written somewhere else, it is possible that output buffer will not be empty, when RXC interrupt is happened, so transmitted echo byte will be lost.

    At the other hand, the good programming practice is to leave interrupt handlers as soon as possible. Putting wait loops in the interrupt handler routine is a bad idea. In that case, if you can not be sure the output buffer will be empty on the RXC event, better to either have some kind of output buffer in RAM, which will be processed in UDRE interrupt, or to perform echo outside the RXC interrupt.