Using an App Note to Implement IRQ Based USART Communications
In This Video
- Find AVR® MCU application notes and source code.
- Provide an overview of "AVR306: Using the AVR USART in C".
- Copy relevant code into the project.
- Add the Libc.h header file for interrupts.
- Modify the USART initialization function to auto-calculate the baud rate register and use one stop bit.
- Adjust interrupt vectors written for IAR to AVR Libc.
- Debug to understand what happens when we hit the data register empty interrupt.
Procedure
Find the AVR® MCU Application Notes and Source Code
Follow the link to the AVR App Note page. Enter the terms "USART in C" in the search bar and press the Magnifying Glass to see the results. You should see the following...
Create a New Project
The search result AN1451-1-AVR306 has example C files at the top link and useful reference material in the bottom link. Select the top link and download the example files. Open the usart_interrupt.c file.
Modify the Example uart_interrupt.c File
Some changes need to be made to this file to get it to work with our device. These changes have been made in the code below and include changes like...
- Included the avr/interrupt.h file
- Renamed registers and bits to use USART0
- Used our calculated UBRR value instead of hard coding it
- Used correct ISR vectors for our device
Here is the modified code...
#define BAUD 9600
#define MYUBRR ((F_CPU / (BAUD * 16UL)) - 1)
#include <avr/io.h>
#include <avr/interrupt.h> //ADDED
/* USART Buffer Defines */
#define USART_RX_BUFFER_SIZE 128 /* 2,4,8,16,32,64,128 or 256 bytes */
#define USART_TX_BUFFER_SIZE 128 /* 2,4,8,16,32,64,128 or 256 bytes */
#define USART_RX_BUFFER_MASK (USART_RX_BUFFER_SIZE - 1)
#define USART_TX_BUFFER_MASK (USART_TX_BUFFER_SIZE - 1)
#if (USART_RX_BUFFER_SIZE & USART_RX_BUFFER_MASK)
#error RX buffer size is not a power of 2
#endif
#if (USART_TX_BUFFER_SIZE & USART_TX_BUFFER_MASK)
#error TX buffer size is not a power of 2
#endif
/* Static Variables */
static unsigned char USART_RxBuf[USART_RX_BUFFER_SIZE];
static volatile unsigned char USART_RxHead;
static volatile unsigned char USART_RxTail;
static unsigned char USART_TxBuf[USART_TX_BUFFER_SIZE];
static volatile unsigned char USART_TxHead;
static volatile unsigned char USART_TxTail;
/* Prototypes */
void USART0_Init(unsigned int baudrate);
unsigned char USART0_Receive(void);
void USART0_Transmit(unsigned char data);
int main(void)
{
/* Set the baudrate to 9600 bps using 8MHz internal RC oscillator */
USART0_Init(MYUBRR); //CHANGED to PASS MYUBRR from calculation
sei();
for( ; ; ) {
/* Echo the received character */
USART0_Transmit(USART0_Receive());
}
}
/* Initialize USART */
void USART0_Init(unsigned int baudrate)
{
unsigned char x;
/* Set the baud rate */
UBRR0H = (unsigned char) (baudrate>>8);
UBRR0L = (unsigned char) baudrate;
/* Enable USART receiver and transmitter */
UCSR0B = ((1 << RXCIE0) | (1 << RXEN0) | (1 << TXEN0));
/* For devices in which UBRRH/UCSRC shares the same location
* eg; ATmega16, URSEL should be written to 1 when writing UCSRC
*
*/
/* Set frame format: 8 data 2stop */
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); //CHANGED ADDRESS and 1 stop bit
/* Flush receive buffer */
x = 0;
USART_RxTail = x;
USART_RxHead = x;
USART_TxTail = x;
USART_TxHead = x;
}
ISR(USART0_RX_vect) //CHANGE ISR
{
unsigned char data;
unsigned char tmphead;
/* Read the received data */
data = UDR0;
/* Calculate buffer index */
tmphead = (USART_RxHead + 1) & USART_RX_BUFFER_MASK;
/* Store new index */
USART_RxHead = tmphead;
if (tmphead == USART_RxTail) {
/* ERROR! Receive buffer overflow */
}
/* Store received data in buffer */
USART_RxBuf[tmphead] = data;
}
ISR(USART0_UDRE_vect)
{
unsigned char tmptail;
/* Check if all data is transmitted */
if (USART_TxHead != USART_TxTail) {
/* Calculate buffer index */
tmptail = (USART_TxTail + 1) & USART_TX_BUFFER_MASK;
/* Store new index */
USART_TxTail = tmptail;
/* Start transmission */
UDR0 = USART_TxBuf[tmptail];
} else {
/* Disable UDRE interrupt */
UCSR0B &= ~(1<<UDRIE0);
}
}
unsigned char USART0_Receive(void)
{
unsigned char tmptail;
/* Wait for incoming data */
while (USART_RxHead == USART_RxTail);
/* Calculate buffer index */
tmptail = (USART_RxTail + 1) & USART_RX_BUFFER_MASK;
/* Store new index */
USART_RxTail = tmptail;
/* Return data */
return USART_RxBuf[tmptail];
}
void USART0_Transmit(unsigned char data)
{
unsigned char tmphead;
/* Calculate buffer index */
tmphead = (USART_TxHead + 1) & USART_TX_BUFFER_MASK;
/* Wait for free space in buffer */
while (tmphead == USART_TxTail);
/* Store data in buffer */
USART_TxBuf[tmphead] = data;
/* Store new index */
USART_TxHead = tmphead;
/* Enable UDRE interrupt */
UCSR0B |= (1<<UDRIE0);
}
Summary
In our previous USART lesson, our code would spend most of its available clock cycle time waiting for the RX and TX to happen with hardly any time to do anything else. With the changes, our code is free to do other things until an interrupt occurs. It will then save the state of the MCU, handle the interrupt logic, and then continue to do other things.