Using the USART to Loopback From a Serial Terminal

Last modified by Microchip on 2023/11/09 09:02

In this video:

  • Block diagram overview of the AVR® USART (Universal Synchronous and Asynchronous serial Receiver and Transmitter).
  • USART initialization: eight data bits, one stop bit, 9600 baud
  • USART receive function: Wait for data to be received, then store it in the data register.
  • USART transmit function: Wait for empty transmit buffer, then put data in the buffer.
  • Implement a simple loopback and test in Atmel® Studio's terminal window.

Procedure

Start a New Project

We will be using the Xplained mini containing the ATmega328PB.  Create a new project and add a new AVR main.c file.  Your new project should be similar to the accompanying image. 

New Main

 


Review the USART Block Diagram

The accompanying block diagram shows the registers we need to work with in order to get our USART loopback working.

  • UBRRn - USART Baud Rate Register
  • UDRn - USART Data Register
  • UCSRn - USART Control and Status Registers

USART Block Diagram


Review the Registers in the Block Diagram

The USART Baud Rate Register (UBRRn) shown in the accompanying image consists of an upper and lower byte (16-bit) and contains the value for the baud rate we will be using.

USART UBRR

The USART Data Register is shared between the Transmit and Receive and has the same I/O address (UDRn).  You can see this register is shown in both sections of the block diagram.  This register is the location from which we will both read and write data that is transmitted.

USART UDR Register

The USART Control and Status Registers (UCSRn) allows us to enable the Receiver and Transmitter, set the Frame Format, and let us know when our data has been sent or received

USART UCSR Register

USART UCSRB Register

USART UCSRC Register

 


Set up the Baud Rate

The datasheet provides a macro that will help us with the baud rate value that will be stored in the UBRRn register.  We will be using UBBR0H and UBBR0L (recall that this is a 16-bity register).  Also, recall from previous lessons that we are running our device at 16 MHz. Our baud rate will be 9600.

Place this code just below the #include <avr/io.h> line.

#define F_CPU 16000000UL // Clock Speed
#define BAUD 9600
#define UBRR_VALUE ((F_CPU / (BAUD * 16UL)) - 1)

Initialize the USART

We will add a function that initializes the USART and then we will call it from main.  This function writes the calculated value for UBRRn from Step 4 and writes it to UBBR0H and UBBR0L.  We will also enable the receiver and transmitter but write to the UCSR0B register bits.  Copy the following code and place it below the macro from Step 4.

void USART_Init()
{
/*Set baud rate */
 UBRR0H = (unsigned char)(UBRR_VALUE >> 8);
 UBRR0L = (unsigned char) UBRR_VALUE;

/* Enable receiver and transmitter */
 UCSR0B = (1<<RXEN0)|(1<<TXEN0);

/* Set frame format: 8data, 2stop bit */
 //UCSR0C = (3 << UCSZ00);
 UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}

Use the following line to call if from main.c above the while loop because we only need to initialize it once in this application.

USART_Init();

Wait for Empty Transmitter

The following function tests to see if the transmitter buffer is empty.  When it determines that it is empty, it writes data to the buffer.  Place this function below the  USART initialization code.

void USART_Transmit( unsigned char data )
{
/* Wait for empty transmit buffer */
while ( !( UCSR0A & (1 << UDRE0)) );
/* Put data into buffer, sends the data */
 UDR0 = data;
}

Wait for Data to be Received

This function tests to see if data has been received. Once data is received, it is retrieved from the USART Data Register (UDR0) and returned as the function output. This ensures that the program does not proceed until data has been successfully received.  Place the following code below the Wait for Empty Transmitter code from Step 6.

unsigned char USART_Receive( void )
{
/* Wait for data to be received */
while ( !(UCSR0A & (1<<RXC0)) )
 ;
/* Get and return received data from buffer */
return UDR0;
}

Putting it all Together

Now we'll add the contents of the while loop. 

USART_Transmit(USART_Receive());

Our application will loop back because we will be transmitting the received data which will result in an echo to our Terminal which is demonstrated in the video

Here is the complete code for the USART Loopback application.

#include <avr/io.h>

#define F_CPU 16000000UL // Clock Speed
#define BAUD 9600
#define UBRR_VALUE ((F_CPU / (BAUD * 16UL)) - 1)


void USART_Init()
{
/*Set baud rate */
 UBRR0H = (unsigned char)(UBRR_VALUE >> 8);
 UBRR0L = (unsigned char) UBRR_VALUE;

/* Enable receiver and transmitter */
 UCSR0B = (1<<RXEN0)|(1<<TXEN0);

/* Set frame format: 8data, 2stop bit */
 //UCSR0C = (3 << UCSZ00);
 UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}

void USART_Transmit( unsigned char data )
{
/* Wait for empty transmit buffer */
while ( !( UCSR0A & (1 << UDRE0)) );
/* Put data into buffer, sends the data */
 UDR0 = data;
}

unsigned char USART_Receive( void )
{
/* Wait for data to be received */
while ( !(UCSR0A & (1<<RXC0)) )
 ;
/* Get and return received data from buffer */
return UDR0;
}



int main(void) {
   
    USART_Init();
   
   while (1)
    {
       
        USART_Transmit(USART_Receive());
       
       
    }
}

Previous Lesson:

Setting up the Data Visualizer

Next Lesson:

Understanding the USART Baud Rate Macro