Interrupt driven AVR USART communication

Simple USART routines arenot always practical because waiting transmitting buffer to be ready in a simple loop occupies processor. Especially if we don't know when data will be received. Another issue when multiple data bytes has to be sent/received. As microcontroller speciality is interrupts so it is better to use them and this way improve overall performance and energy saving.

In previous article we discussed simple USART implementation. Where microcontroller constantly has to check if there is data received in UDR register. Also for sending it has to check if send buffer is free. Such coding is ineffective as MCU have always to run at 100% performance checking the buffer. In such mode batteries are going down really fast. Why not to set up guardian which would wake the MCU if it have received a byte via USART. In other hand Interrupt mode allows to perform other tasks at full capacity while waiting for USART interrupt.

Lets modify our program to Interrupt driven USART mode

For this wee will need to include another one library:

#include <avr/interrupt.h>

Then wee will have to enable USART Receive Complete Interrupt (USART_RXC_vect) and enable global interrupts. And at last ti write interrupt service routine ISR(USART_RXC_vect). Lets write simple bounce program – USART sends back incremented by one what he received:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>  
#ifndef F_CPU
//define cpu clock speed if not defined
#define F_CPU 3686400
#endif
//set desired baud rate
#define BAUDRATE 19200
//calculate UBRR value
#define UBRRVAL ((F_CPU/(BAUDRATE*16UL))-1)
void USART_Init()
{
	//Set baud rate
	UBRRL=UBRRVAL;		//low byte
	UBRRH=(UBRRVAL>>8);	//high byte
	//Set data frame format: asynchronous mode,no parity, 1 stop bit, 8 bit size
	UCSRC=(1<<URSEL)|(0<<UMSEL)|(0<<UPM1)|(0<<UPM0)|
		(0<<USBS)|(0<<UCSZ2)|(1<<UCSZ1)|(1<<UCSZ0);	
	//Enable Transmitter and Receiver and Interrupt on receive complete
	UCSRB=(1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
	//enable global interrupts
	set_sleep_mode(SLEEP_MODE_IDLE);
	sei();
}
ISR(USART_RXC_vect)
{
	//defien temp value for storing received byte
	uint8_t Temp;
	//Store data to temp
	Temp=UDR;
	Temp++;//increment
	//send received data back
	// no need to wait for empty send buffer
	UDR=Temp;
}
int main(void)
{
	USART_Init();
	while(1)
	sleep_mode();
	//nothing here interrupts are working
	return 0;
}

Transmitter interrupt mode works same way. There are two flags indicating and generating interrupts: USART Data Register Empty(UDRE), which generates interrupt when UDR register is empty and ready to receive new data; and Transmit Complete (TXC) – generates interrupt when transmit is complete and no new data is in UDR. These interrupts are useful in half duplex mode, where transmitting operation mus enter receive mode. Also receive and transmit interrupt modes are often used in buffered mode when for example after all multi-byte data buffer is sent interrupt may request to load new data or set USART for receive mode.

But as I mentioned Interrupt mode frees MCU resources and allows to run other background tasks like sending information to LCD, Reading button states, flashing LED and so on. In other hand Interrupts has to be short enough so they wouldn't block other interrupts. More interrupts – more care should be taken while setting them.

Comments

for ATMEGA128 ISR (USART1_RXC_vect) doesn't work! We should write ISR(USART1_RX_vect)

This example is old and writthen for older winavr. ISR vector names were unified after then to match datasheets. if you don't wanna change whole code, simply do:

#define USART1_RXC_vect USART1_RX_vect

EVEN NUMBERS ARE NOT INCREMENTING IN MY ATMEGA32 FOR ABOVE PROGRAM.BUT ODD NUMBERS ARE INCREMENTING.I ALSO TRIED DIFFERENT CLOCK FREQUENCY .MY OUTPUT GOT WORSE IN REALTERM SERIAL CAPTURE SOFTWARE