Sunday, August 7, 2022

UART library for AVR microcontrollers

A highly customizable UART library that can also be called USART since it has the option for synchronous transfer mode. It can work with transmit and receive buffers down to 1 byte in size, making it suitable for devices with small memory. It was mainly made for ATmega328PB.

AVR microcontrollers have support for both UART and USART modes. USART stands for Universal Synchronous Asynchronous Receiver Transmitter. Notice that UART lacks the S that stands for Synchronous.

UART is a type of serial interface, as opposed to a parallel interface. A parallel interface can work at higher speeds but the disadvantage is that it needs multiple input/output lines. Other examples of serial interfaces are SPI and I2C.

UART library for AVR microcontrollers using interrupts


  • Custom Baud rate
  • Asynchronous or synchronous modes
  • Supports serial frames with 5, 6, 7, 8, or 9 data bits
  • Odd, even or no parity
  • Error detection
  • Multi-processor communication mode used to address multiple devices on the same serial bus
  • Double speed asynchronous communication mode



USART Characteristics

USART can be set to work in the following modes: Normal asynchronous, Double Speed asynchronous, Master synchronous, Slave synchronous mode and Multi-Processor Communication Mode.

Asynchronous and synchronous serial

Asynchronous means that data is transferred without support from an external clock signal since the clock is generated internally and is synchronized to the incoming serial frames.

Synchronous mode requires an extra clock line but has the advantage that it can work at higher speed rates that is asynchronous mode.

In Master synchronous the the main device generates and outputs the clock on XCKn pin while in Slave synchronous mode the device uses the clock generated by the Master.

Multi-Processor Communication Mode

The Multi-Processor Communication mode enables several slave MCUs to receive data from a master MCU. This is done by first decoding an address frame to find out which MCU has been addressed. If a particular slave MCU has been addressed, it will receive the following data frames as normal, while the other slave MCUs will ignore the received frames until another address frame is received.

Frame formats

A serial frame is composed of a character of data bits with synchronization bits (start and stop bits), and optionally a parity bit for error checking.

A frame starts with the start bit, followed by the data bits (from 5 up to 9 data bits in total): first the least significant data bit, then the next data bits ending with the most significant bit. If enabled, the parity bit is inserted after the data bits, before the one or two stop bits. When a complete frame is transmitted, it can be directly followed by a new frame, or the communication line can be set to an idle (high) state. the figure below illustrates the possible combinations of the frame formats. Bits inside brackets are optional.

AVR UART frame formats

IDLE: No transfers on the communication line (Rx or Tx). An IDLE line must be high.

St: Start bit, always low.

(n): Data bits (0 to 8).

P: Parity bit. Can be odd or even.

Sp: Stop bit, always high

The Stop bit can be followed by a Start bit (low) for another frame, or Idle (high). All devices connected to the UART bus must use the same frame format and baud rate.

Baud Rate

The baud rate specifies how fast data is sent over a serial line. It's usually expressed in units of bits-per-second (bps). Most commonly used baud rates are 9600 and 115200 bps.


Some AVRs have two UARTs with pins named RXDn (Receiver) and TXDn (Transmitter). n represent the UART number - 0 or 1.

Lets say you want to interface UART 0 with a device that has this pins: RX and TX. Then you connect RXD0 to TX and TXD0 to RX. Also they must share a common ground.

In Synchronous mode you also need the XCKn pin for clock.

For ATmega328PB the TX/RX pins are as follows:

TXD0 - PD1, RXD0 - PD0, XCK0 - PD4

TXD1 - PB3, RXD1 - PB4, XCK1 - PB5

For other types of microcontroller check the Pin Configurations chapter of the datasheet.

Using UART library

Selecting UART number

By default, the library is set to use UART0. To use UART1 set UART_NUMBER to 1.

#define UART_NUMBER	0

TX and RX buffers

Transmitted and Received data are buffered in two circular arrays that by default have a size of 64 bytes each. The size can be from 1 to 255 bytes. Having a larger array can be a bit faster. When using higher baud rates it is recommended a bigger buffer to avoid loosing incoming data and to give more time to the microcontroller to process that data.


Initialization function

Sets the baud rate, frame format, enables UART interrupts and global interrupts. The function can be used at any point to change baud rate or frame parameters.

void UART_begin()


uint32_t baudrate

Bits per second

uint8_t mode

UART mode: Asynchronous Normal mode, Asynchronous Double Speed, Synchronous Master/Slave mode. When using synchronous mode, the Data Direction Register for the XCKn pin controls whether the clock source is internal (Master mode) or external (Slave mode). The XCKn pin is only active when using synchronous mode.


uint8_t parity


uint8_t bits

Number of bits in the frame, from 5 to 8. Most common format is having 8 bits but if you have small numbers and speed is important, using 5, 6 or 7 bits can provide a bit more speed. The maximum number that can be transmitted is 2^n where n is the number of bits. 9 bits is usually only used in Multi-processor Communication mode where the 9'th bit indicate an address or data frame.



Send a byte

Puts a byte of data in the transmit buffer and enables the Data Register Empty Interrupt.

void UART_send()


uint8_t data

The data byte

Send a string

Send a null terminated string.

void UART_sendString()


char* s

A string

Send bytes

Send a series of bytes in a buffer.

void UART_sendBytes()


uint8_t* buff

An array of bytes.

uint8_t length

Length of the array.

Send an integer number

Convert an integer number into a string array and send it over UART.

void UART_sendInt()


INT_SIZE number

INT_SIZE is defined in the "utils.h" and can be int32_t or int64_t

Send a float number

Convert a float number into a string array and send it over UART.

void UART_sendFloat()


float number

A float number.

uint8_t decimals

Number of digits after the dot.

Send hex numbers

Convert a 1- 2- or 4-byte integer number into hexadecimal value and send it over UART.

void UART_sendHex8(uint8_t value)
void UART_sendHex16(uint16_t value)
void UART_sendHex32(uint32_t value)

Check for new data

Returns true if new data is available.

bool UART_available(void)

Read a byte

Returns the next received byte or 0 if no new data is available. Should be used only if  UART_available() returns true.

uint8_t UART_read(void)

Read bytes

Read received bytes into the provided buffer. The function terminates if the specified length has been read, or it times out (around 0.5s).

uint8_t UART_readBytes()


uint8_t* buff

An array buffer where to put incomming data.

uint8_t length

Length of the array.


Returns the number of characters read.

Read bytes until

Read received bytes into the provided buffer. The function terminates if the specified length has been read, the termination character has been found or it times out (around 0.5s). The termination character is not included in the buffer.

uint8_t UART_readBytesUntil()


char character

The termination character. When this character is encountered, the function terminates.

uint8_t* buff

An array buffer where to put incomming data.

uint8_t length

Length of the array.


Returns the number of characters read.

Disable UART

Disable UART transmitter, receiver and interrupts.

void UART_end(void)


If the UART buffer has to be flushed during normal operation, due to for instance an error condition, read the UDRn I/O location until the RXCn Flag is cleared.

void USART_flush(void)

Receiver error flags

After each byte is received, the error bits in the UART register are saved in a global variable. The following macros can be used to check if an error occurred and the type of error.At the end, the error flag must be cleared.

        UART_sendString("Frame Error\r");
	UART_sendString("Parity Error\r");
	UART_sendString("Data OverRun\r");

Frame Error

The Frame Error (FE) Flag indicates the state of the first stop bit of the next readable frame stored in the receive buffer. The FE Flag is zero when the stop bit was correctly read as '1', and the FE Flag will be one when the stop bit was incorrect (zero). This flag can be used for detecting out-of-sync conditions, detecting break conditions and protocol handling.

Parity Error

The Parity Error (UPE) Flag indicates that the next frame in the receive buffer had a Parity Error when received. If Parity Check is not enabled the UPE bit will always read '0'.

Data OverRun

The Data OverRun (DOR) Flag indicates data loss due to a receiver buffer full condition. A Data OverRun occurs when the receive buffer is full (two characters), a new character is waiting in the Receive Shift Register, and a new start bit is detected. If the DOR Flag is set, one or more serial frames were lost between the last frame read from UDR, and the next frame read from UDR.

Enable MPCM

Enable the Multi-processor Communication mode (MPCM). After this function sets the MPCM bit, frames that do not contain an address will be ignored by the UART. Frames containing an address have the 9'th bit set to 1.

void UART_mpcmEnable(void)

Disable MPCM

Disables the Multi-processor Communication mode (MPCM). After this function sets the MPCM bit to 0, data frames can be received. This should be used after a valid address has been received after using  UART_mpcmEnable().

void UART_mpcmDisable(void)

Send UART address

Select a device by sending an address frame over UART.

void UART_mpcmSelectDevice()


uint8_t address

Address of the device to select.

Code example

#define F_CPU	16000000

#include <avr/io.h>
#include <util/delay.h>

#include "uart.h"

int main(void){
    const uint8_t bufferSize = 20;
    uint8_t buff[bufferSize];
    uint8_t bytes_read = 0;
    UART_sendString("Hi there!\r");
    UART_sendString("Is this thing working?\r");

		// Read serial data
		bytes_read = UART_readBytes(buff, bufferSize);
			// Print received data to a serial terminal
			UART_sendBytes(buff, bytes_read);
		// Check for transmission errors
				UART_sendString("Frame Error\r");
				UART_sendString("Parity Error\r");
				UART_sendString("Data OverRun\r");



Version 1.0


utils.h - used to convert numbers into strings.

Share it if you like it!

No comments:

Post a Comment