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.

Arduino Tutorial Embedded C Register Level Arduino Master Class

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.

PCBway Banner
AVR USART Register Configuration
AVR USART Register Configuration

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 unit need 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.

In this tutorial we will be discussing the programming for Normal Asynchronous Mode of USART that is also called UART.

Prerequisite

  • Knowledge of C/C++ programming

Hardware bill of materials

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 connection to your Arduino.One via USBasp to flash and other 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.

Arduino UNO / Atmega328p USART Clock Circuit
Arduino UNO / Atmega328p USART Clock Circuit

The baud rate is generated form 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.

Arduino UNO / Atmega328p USART Baud Rate Calculation
Arduino UNO / Atmega328p USART Baud Rate Calculation

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)UBRR0Error %
2400416-0.1
48002070.2
96001030.2
14.4k680.6
19.2k510.2
28.8k34-0.8
38.4k250.2
57.6k162.1
76.8k120.2
115.2k8-3.5
230.4k38.5
250k30.0
0.5M10.0
1M00.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 using UCSR0B register and load the UBR0 register with the data to transmit. In 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 waste 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
Arduino UNO / Atmega328p USART Atmel Studio Output
Arduino UNO / Atmega328p USART Atmel Studio Output
Arduino UNO / Atmega328p USART Arduino IDE Output
Arduino UNO / Atmega328p USART Arduino IDE Output

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 waste 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

Loopback test are 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 execution of other instructions. This problem is solved by interrupt based transmission. Below code transmits using interrupts. The code transmits character ‘a’ endlessly while the D13 LED on Arduino UNO keeps blinking. The CPU keeps performing the LED blinking in an infinite loop and everytime the transmission finishes an interrupt is generated to state that the UDR0 buffer is ready to receive a 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 same like polling reception but in the case of interrupt reception. The CPU is busy looping an infinite loop and whenever an data is received in USART Buffer an interrupt is thrown and the CPU serves it and toggles the LED accordingly. The CPU don’t have to monitot 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

Below example works exactly like polling loopback but here the CPU dosen’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;
}

Manufacturing

Do you want to get your PCB manufactured? Check out PCBWay They have their own factory at Shenzhen, China and provides affordable PCB Manufacturing and Assembly Services. They can manufactur FR-4 , Aluminum, Rogers, HDI, Flexible and Rigid-Flex boards, with very reasonable price. Before you order you can check for quotation Online instant Quote .

If you want to know how to place an order on PCBWay Website and get your PCB Board manufactured in professional quality check out my step by step guide on PCBWay : How to Order Article.PCBWay.com Manufacturer Review - Electronics-LabTo check if the gerbers generated by your CAM software is correct you can view them on the Online Gerber Viewer. If you want to get any project assembled and mass manufactured then they also have a SMT & THT assembly service, which starts from only $30 with free stencil and free worldwide shipping. Learn More about their Assembly Service.

They have other services like Layout Design and also supports students with PCB and Project Sponsorship. It is a higly recommended company to work with. Learn more about PCBWay.


Crazy Engineer

MAKER - ENGINEER - YOUTUBER

0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.