Note
This article is a part of Arduino / ATmega328p Embedded C Firmware Programming Tutorial. Consider exploring the course home page for articles on similar topics.
Also visit the Release Page for Register Level Embedded C Hardware Abstraction Library and Code for AVR.
Introduction
The Universal Synchronous and Asynchronous serial Receiver and Transmitter (USART) is a highly flexible serial communication device. The USART hardware in ATmega48A/PA/88A/PA/168A/PA/328/P is represented as USART0.
The major hardware components in USART are Clock Generator, Transmitter, and Receiver.
The Clock Generation logic consists of synchronization logic for external clock input used by synchronous slave operation, and the baud rate generator.
The Transmitter consists of a single write buffer, a serial Shift Register, Parity Generator, and Control logic for handling different serial frame formats. The write buffer allows a continuous transfer of data without any delay between frames.
The Receiver is the most complex part of the USART module due to its clock and data recovery units. The recovery units are used for asynchronous data reception. In addition to the recovery units, the Receiver includes a Parity Checker, Control logic, a Shift Register, and a two-level receive buffer (UDRn). The Receiver supports the same frame formats as the Transmitter and can detect Frame Error, Data OverRun, and Parity Errors.
Each of the hardware units needs to be configured by writing bits in their respective control registers. The USART supports four modes of operation: Normal asynchronous, Double Speed asynchronous, Master synchronous, and Slave synchronous mode.
What You Will Learn
- How to Program the UART in Arduino?
- How to do UART Programming in AVR ATmega328p?
- How to Transmit and Receive Data using UART communication in Arduino/ATmega328p?
- How to Program UART for Polling and Interrupt based communication?
- How to Transmit and Receive data to and fro Computer and Arduino/ATmega328p?
Prerequisite
- Knowledge of C/C++ programming
Hardware Bill of Materials
- Arduino UNO
- USBasp (Optional, when Arduino’s serial programming is not used)
Software Bill of Materials
- Atmel Studio 7
- Arduino IDE
- Arduino Drivers
- USBasp Drivers (Needed when USBasp is used)
Schematic Connection
If you are using Arduino UNO and Atmel Studio 7 / Arduino IDE. You just have to connect the Arduino board with your computer via USB.
In case you are not using the Arduino’s serial programming to flash the microcontroller, you will also need an additional USBasp to connect your computer and Arduino board. In this condition, you need two USB connections to your Arduino. One via USBasp to flash and another one for serial communication.
USART Programming
The three major hardware components that need to be initialized before any communication are Clock Generator, Transmitter, and Receiver. The initialization process normally consists of setting the baud rate, setting frame format, and enabling the Transmitter or the Receiver.
The baud rate is generated from the system clock with the help of Prescaler and clock circuit. The USART Baud Rate Register (UBRR0) controls the programmable down counter / Prescaler to generate a particular clock signal. The down-counter, running at system clock (fosc), is loaded with the UBRR0 value each time the counter has counted down to zero and generates a clock pulse.
The above formula is used to calculate the right value of UBBR0. For Arduino UNO the system clock is running at 16Mhz. If we intend to communicate at a speed of 9600bps. The value of UBBR0 should be UBBR0 = ((16,000,000 / 16*9600) -1) = 103 ( Rounded )
Baud Rate (bps) | UBRR0 | Error % |
---|---|---|
2400 | 416 | -0.1 |
4800 | 207 | 0.2 |
9600 | 103 | 0.2 |
14.4k | 68 | 0.6 |
19.2k | 51 | 0.2 |
28.8k | 34 | -0.8 |
38.4k | 25 | 0.2 |
57.6k | 16 | 2.1 |
76.8k | 12 | 0.2 |
115.2k | 8 | -3.5 |
230.4k | 3 | 8.5 |
250k | 3 | 0.0 |
0.5M | 1 | 0.0 |
1M | 0 | 0.0 |
So from the above table, it is easy to choose the available baud rates with their respective UBBR0. At 16Mhz the highest communication speed we can reach is 1Mbps.
The next step is to set the Frame Format using UCSR0C register. The USART accepts all 30 combinations of the following as valid frame formats:
• 1 start bit
• 5, 6, 7, 8, or 9 data bits
• no, even or odd parity bit
• 1 or 2 stop bits
The next step is to enable the Transmitter and Receiver to use UCSR0B register and load the UBR0 register with the data to transmit. In the case of reception, the UBR0 is read by the application.
NOTE: All the code below can be compiled and flashed both from Atmel Studio and Arduino IDE. Use any Serial Monitor at 9600bps, 1 Stop Bit, No Parity. I recommend using the Data Visualizer in Atmel Studio for Serial Port Terminal in case you are programming and flashing from Atmel Studio.
Polling Transmission
Polling transmission is the simplest method of transmission where the application software keeps monitoring the status of the USART transmitter hardware and loads a byte of data into the USART buffer UDR0 only when the hardware is ready for transmission. This wastes CPU time in constantly monitoring the status of UDRE0 bit of UCSR0A register.
/*
* usart.c
*
* Created : 15-08-2020 07:24:45 PM
* Author : Arnab Kumar Das
* Website : www.ArnabKumarDas.com
*/
#define F_CPU 16000000UL // Defining the CPU Frequency
#include <avr/io.h> // Contains all the I/O Register Macros
#include <util/delay.h> // Generates a Blocking Delay
#define USART_BAUDRATE 9600 // Desired Baud Rate
#define BAUD_PRESCALER (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#define ASYNCHRONOUS (0<<UMSEL00) // USART Mode Selection
#define DISABLED (0<<UPM00)
#define EVEN_PARITY (2<<UPM00)
#define ODD_PARITY (3<<UPM00)
#define PARITY_MODE DISABLED // USART Parity Bit Selection
#define ONE_BIT (0<<USBS0)
#define TWO_BIT (1<<USBS0)
#define STOP_BIT ONE_BIT // USART Stop Bit Selection
#define FIVE_BIT (0<<UCSZ00)
#define SIX_BIT (1<<UCSZ00)
#define SEVEN_BIT (2<<UCSZ00)
#define EIGHT_BIT (3<<UCSZ00)
#define DATA_BIT EIGHT_BIT // USART Data Bit Selection
void USART_Init()
{
// Set Baud Rate
UBRR0H = BAUD_PRESCALER >> 8;
UBRR0L = BAUD_PRESCALER;
// Set Frame Format
UCSR0C = ASYNCHRONOUS | PARITY_MODE | STOP_BIT | DATA_BIT;
// Enable Receiver and Transmitter
UCSR0B = (1<<RXEN0) | (1<<TXEN0);
}
void USART_TransmitPolling(uint8_t DataByte)
{
while (( UCSR0A & (1<<UDRE0)) == 0) {}; // Do nothing until UDR is ready
UDR0 = DataByte;
}
int main()
{
USART_Init();
while (1)
{
USART_TransmitPolling('A');
USART_TransmitPolling('R');
USART_TransmitPolling('N');
USART_TransmitPolling('A');
USART_TransmitPolling('B');
USART_TransmitPolling('\n');
_delay_ms(1000);
}
return 0;
}
The output of the above code is “ARNAB” every 1 Second in the Serial Monitor.
ARNAB
ARNAB
ARNAB
Polling Reception
Polling reception is the simplest method of reception where the application software keeps monitoring the status of the USART receiver hardware and reads the data from the USART buffer UDR0 only when the hardware has received a byte of data. This wastes CPU time in constantly monitoring the status of RXC0 bit of UCSR0A register.
The below code waits for the user input. If the serial input is ‘a’ it glows the D13 LED on the Arduino UNO board. To turn the LED off any other character can be written using the Serial Monitor.
/*
* usart.c
*
* Created : 15-08-2020 07:44:46 PM
* Author : Arnab Kumar Das
* Website : www.ArnabKumarDas.com
*/
#define F_CPU 16000000UL // Defining the CPU Frequency
#include <avr/io.h> // Contains all the I/O Register Macros
#include <util/delay.h> // Generates a Blocking Delay
#define USART_BAUDRATE 9600 // Desired Baud Rate
#define BAUD_PRESCALER (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#define ASYNCHRONOUS (0<<UMSEL00) // USART Mode Selection
#define DISABLED (0<<UPM00)
#define EVEN_PARITY (2<<UPM00)
#define ODD_PARITY (3<<UPM00)
#define PARITY_MODE DISABLED // USART Parity Bit Selection
#define ONE_BIT (0<<USBS0)
#define TWO_BIT (1<<USBS0)
#define STOP_BIT ONE_BIT // USART Stop Bit Selection
#define FIVE_BIT (0<<UCSZ00)
#define SIX_BIT (1<<UCSZ00)
#define SEVEN_BIT (2<<UCSZ00)
#define EIGHT_BIT (3<<UCSZ00)
#define DATA_BIT EIGHT_BIT // USART Data Bit Selection
void USART_Init()
{
// Set Baud Rate
UBRR0H = BAUD_PRESCALER >> 8;
UBRR0L = BAUD_PRESCALER;
// Set Frame Format
UCSR0C = ASYNCHRONOUS | PARITY_MODE | STOP_BIT | DATA_BIT;
// Enable Receiver and Transmitter
UCSR0B = (1<<RXEN0) | (1<<TXEN0);
}
uint8_t USART_ReceivePolling()
{
uint8_t DataByte;
while (( UCSR0A & (1<<RXC0)) == 0) {}; // Do nothing until data have been received
DataByte = UDR0 ;
return DataByte;
}
int main()
{
DDRB |= 1 << 5; // Configuring PB5 / D13 as Output
USART_Init();
char LocalData;
while (1)
{
LocalData = USART_ReceivePolling();
if (LocalData == 'a')
{
PORTB |= 1<<5; // Writing HIGH to glow LED
}
else
{
PORTB &= ~(1<<5); // Writing LOW
}
_delay_ms(1000);
}
return 0;
}
Polling Loopback
The loopback test is a great way to verify any communication channel. A loopback test of USART will verify both the reception and transmission side of the code. A loopback test sends back the same data that is received. The below code will echo back the same character that is sent from the serial terminal.
/*
* usart.c
*
* Created : 15-08-2020 08:34:15 PM
* Author : Arnab Kumar Das
* Website : www.ArnabKumarDas.com
*/
#define F_CPU 16000000UL // Defining the CPU Frequency
#include <avr/io.h> // Contains all the I/O Register Macros
#include <util/delay.h> // Generates a Blocking Delay
#define USART_BAUDRATE 9600 // Desired Baud Rate
#define BAUD_PRESCALER (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#define ASYNCHRONOUS (0<<UMSEL00) // USART Mode Selection
#define DISABLED (0<<UPM00)
#define EVEN_PARITY (2<<UPM00)
#define ODD_PARITY (3<<UPM00)
#define PARITY_MODE DISABLED // USART Parity Bit Selection
#define ONE_BIT (0<<USBS0)
#define TWO_BIT (1<<USBS0)
#define STOP_BIT ONE_BIT // USART Stop Bit Selection
#define FIVE_BIT (0<<UCSZ00)
#define SIX_BIT (1<<UCSZ00)
#define SEVEN_BIT (2<<UCSZ00)
#define EIGHT_BIT (3<<UCSZ00)
#define DATA_BIT EIGHT_BIT // USART Data Bit Selection
void USART_Init()
{
// Set Baud Rate
UBRR0H = BAUD_PRESCALER >> 8;
UBRR0L = BAUD_PRESCALER;
// Set Frame Format
UCSR0C = ASYNCHRONOUS | PARITY_MODE | STOP_BIT | DATA_BIT;
// Enable Receiver and Transmitter
UCSR0B = (1<<RXEN0) | (1<<TXEN0);
}
uint8_t USART_ReceivePolling()
{
uint8_t DataByte;
while (( UCSR0A & (1<<RXC0)) == 0) {}; // Do nothing until data have been received
DataByte = UDR0 ;
return DataByte;
}
void USART_TransmitPolling(uint8_t DataByte)
{
while (( UCSR0A & (1<<UDRE0)) == 0) {}; // Do nothing until UDR is ready
UDR0 = DataByte;
}
int main()
{
USART_Init();
char LocalData;
while (1)
{
LocalData = USART_ReceivePolling();
USART_TransmitPolling(LocalData);
}
return 0;
}
Interrupt Transmission
In polling the CPU waste valuable time monitoring the USART registers. This valuable time could be used in the execution of other instructions. This problem is solved by interrupt based transmission. Below code transmits using interrupts. The code transmits the character ‘a’ endlessly while the D13 LED on Arduino UNO keeps blinking. The CPU keeps performing the LED blinking in an infinite loop and every time the transmission finishes an interrupt is generated to state that the UDR0 buffer is ready to receive new data. The CPU pauses the LED blinking and serves the ISR.
/*
* usart.c
*
* Created : 15-08-2020 09:34:44 PM
* Author : Arnab Kumar Das
* Website : www.ArnabKumarDas.com
*/
#define F_CPU 16000000UL // Defining the CPU Frequency
#include <avr/io.h> // Contains all the I/O Register Macros
#include <util/delay.h> // Generates a Blocking Delay
#include <avr/interrupt.h> // Contains all interrupt vectors
#define USART_BAUDRATE 9600 // Desired Baud Rate
#define BAUD_PRESCALER (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#define ASYNCHRONOUS (0<<UMSEL00) // USART Mode Selection
#define DISABLED (0<<UPM00)
#define EVEN_PARITY (2<<UPM00)
#define ODD_PARITY (3<<UPM00)
#define PARITY_MODE DISABLED // USART Parity Bit Selection
#define ONE_BIT (0<<USBS0)
#define TWO_BIT (1<<USBS0)
#define STOP_BIT ONE_BIT // USART Stop Bit Selection
#define FIVE_BIT (0<<UCSZ00)
#define SIX_BIT (1<<UCSZ00)
#define SEVEN_BIT (2<<UCSZ00)
#define EIGHT_BIT (3<<UCSZ00)
#define DATA_BIT EIGHT_BIT // USART Data Bit Selection
#define RX_COMPLETE_INTERRUPT (1<<RXCIE0)
#define DATA_REGISTER_EMPTY_INTERRUPT (1<<UDRIE0)
volatile uint8_t USART_TransmitBuffer; // Global Buffer
void USART_Init()
{
// Set Baud Rate
UBRR0H = BAUD_PRESCALER >> 8;
UBRR0L = BAUD_PRESCALER;
// Set Frame Format
UCSR0C = ASYNCHRONOUS | PARITY_MODE | STOP_BIT | DATA_BIT;
// Enable Receiver and Transmitter
UCSR0B = (1<<RXEN0) | (1<<TXEN0);
//Enable Global Interrupts
sei();
}
void USART_TransmitInterrupt(uint8_t Buffer)
{
USART_TransmitBuffer = Buffer;
UCSR0B |= DATA_REGISTER_EMPTY_INTERRUPT; // Enables the Interrupt
}
int main()
{
DDRB |= 1 << 5; // Configuring PB5 / D13 as Output
uint8_t LocalData = 'a';
USART_Init();
USART_TransmitInterrupt(LocalData);
while (1)
{
PORTB |= 1<<5; // Writing HIGH to glow LED
_delay_ms(500);
PORTB &= ~(1<<5); // Writing LOW
_delay_ms(500);
}
return 0;
}
ISR(USART_UDRE_vect)
{
UDR0 = USART_TransmitBuffer;
//UCSR0B &= ~DATA_REGISTER_EMPTY_INTERRUPT; // Disables the Interrupt, uncomment for one time transmission of data
}
Interrupt Reception
Interrupt reception behaves exactly the same as polling reception but in the case of interrupt reception. The CPU is busy looping an infinite loop and whenever data is received in USART Buffer an interrupt is thrown and the CPU serves it and toggles the LED accordingly. The CPU doesn’t have to monitor the USART register bits to check the status of the reception.
/*
* usart.c
*
* Created : 15-08-2020 09:34:44 PM
* Author : Arnab Kumar Das
* Website : www.ArnabKumarDas.com
*/
#define F_CPU 16000000UL // Defining the CPU Frequency
#include <avr/io.h> // Contains all the I/O Register Macros
#include <util/delay.h> // Generates a Blocking Delay
#include <avr/interrupt.h> // Contains all interrupt vectors
#define USART_BAUDRATE 9600 // Desired Baud Rate
#define BAUD_PRESCALER (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#define ASYNCHRONOUS (0<<UMSEL00) // USART Mode Selection
#define DISABLED (0<<UPM00)
#define EVEN_PARITY (2<<UPM00)
#define ODD_PARITY (3<<UPM00)
#define PARITY_MODE DISABLED // USART Parity Bit Selection
#define ONE_BIT (0<<USBS0)
#define TWO_BIT (1<<USBS0)
#define STOP_BIT ONE_BIT // USART Stop Bit Selection
#define FIVE_BIT (0<<UCSZ00)
#define SIX_BIT (1<<UCSZ00)
#define SEVEN_BIT (2<<UCSZ00)
#define EIGHT_BIT (3<<UCSZ00)
#define DATA_BIT EIGHT_BIT // USART Data Bit Selection
#define RX_COMPLETE_INTERRUPT (1<<RXCIE0)
#define DATA_REGISTER_EMPTY_INTERRUPT (1<<UDRIE0)
volatile uint8_t USART_ReceiveBuffer; // Global Buffer
void USART_Init()
{
// Set Baud Rate
UBRR0H = BAUD_PRESCALER >> 8;
UBRR0L = BAUD_PRESCALER;
// Set Frame Format
UCSR0C = ASYNCHRONOUS | PARITY_MODE | STOP_BIT | DATA_BIT;
// Enable Receiver and Transmitter
UCSR0B = (1<<RXEN0) | (1<<TXEN0);
//Enable Global Interrupts
sei();
}
int main()
{
DDRB |= 1 << 5; // Configuring PB5 / D13 as Output
USART_Init();
UCSR0B |= RX_COMPLETE_INTERRUPT;
while (1)
{
}
return 0;
}
ISR(USART_RX_vect)
{
USART_ReceiveBuffer = UDR0;
if (USART_ReceiveBuffer == 'a')
{
PORTB |= 1<<5; // Writing HIGH to glow LED
}
else
{
PORTB &= ~(1<<5); // Writing LOW
}
}
Interrupt Loopback
The below example works exactly like polling loopback but here the CPU doesn’t waste time in checking the status of the USART registers.
/*
* usart.c
*
* Created : 15-08-2020 09:34:44 PM
* Author : Arnab Kumar Das
* Website : www.ArnabKumarDas.com
*/
#define F_CPU 16000000UL // Defining the CPU Frequency
#include <avr/io.h> // Contains all the I/O Register Macros
#include <util/delay.h> // Generates a Blocking Delay
#include <avr/interrupt.h> // Contains all interrupt vectors
#define USART_BAUDRATE 9600 // Desired Baud Rate
#define BAUD_PRESCALER (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#define ASYNCHRONOUS (0<<UMSEL00) // USART Mode Selection
#define DISABLED (0<<UPM00)
#define EVEN_PARITY (2<<UPM00)
#define ODD_PARITY (3<<UPM00)
#define PARITY_MODE DISABLED // USART Parity Bit Selection
#define ONE_BIT (0<<USBS0)
#define TWO_BIT (1<<USBS0)
#define STOP_BIT ONE_BIT // USART Stop Bit Selection
#define FIVE_BIT (0<<UCSZ00)
#define SIX_BIT (1<<UCSZ00)
#define SEVEN_BIT (2<<UCSZ00)
#define EIGHT_BIT (3<<UCSZ00)
#define DATA_BIT EIGHT_BIT // USART Data Bit Selection
#define RX_COMPLETE_INTERRUPT (1<<RXCIE0)
#define DATA_REGISTER_EMPTY_INTERRUPT (1<<UDRIE0)
volatile uint8_t USART_ReceiveBuffer; // Global Buffer
void USART_Init()
{
// Set Baud Rate
UBRR0H = BAUD_PRESCALER >> 8;
UBRR0L = BAUD_PRESCALER;
// Set Frame Format
UCSR0C = ASYNCHRONOUS | PARITY_MODE | STOP_BIT | DATA_BIT;
// Enable Receiver and Transmitter
UCSR0B = (1<<RXEN0) | (1<<TXEN0);
//Enable Global Interrupts
sei();
}
int main()
{
USART_Init();
UCSR0B |= RX_COMPLETE_INTERRUPT;
while (1)
{
}
return 0;
}
ISR(USART_RX_vect)
{
USART_ReceiveBuffer = UDR0;
UDR0 = USART_ReceiveBuffer;
}
10 Comments
Kevin Walton · April 2, 2021 at 3:22 pm
Great work, will give this a go, thank you. A version for bit banging Serial laid out so simply and easy to understand would be a great addition?
Crazy Engineer · April 3, 2021 at 1:14 am
Thank You, for spending time on the website. Thanks for the Idea. I will implement it soon.
BALAJI · September 20, 2021 at 12:32 am
Thank you . You have written it in the best way possible.
Rudraksh Arora · December 2, 2021 at 2:07 am
Excellent Work! This is one of the rare blogs written with so much clarity. Thanks for sharing your knowledge…
Filip · January 8, 2022 at 2:47 am
Thanks a lot for what all the bits of each register do and possible combinations
Takunda Nyamashuka · September 5, 2022 at 9:27 pm
Thanks. Quite informative. Hope you make more programing tutorials for AVR processors, particularly atmega328
MQ · December 21, 2022 at 7:21 am
I tested the polling loopback, there was nothing in Arduino IDE, Putty and Data Visualizer.
MQ · December 21, 2022 at 7:25 am
TX polling and TX interrupt were OK.
MQ · December 21, 2022 at 7:30 am
interrupt loopback was the same with polling loopback, there was nothing shown.
MQ · December 26, 2022 at 8:49 am
I tested all the 6 programs, they all works.