Programming AVR USART with AVR-GCC. Part 1

AVR USART tutorial is gonna be multi-part tutorial as this peripheral is quite complex device and need some attention.

USART Overview

USART is an acronym of Universal Synchronous and Asynchronous serial Receiver and Transmitter. Instead of using this long expression lets stick to USART. So, at least one USART is found in most of AVR microcontrollers (except few most of Tiny ones). Atmega328 microcontroller has one USART module that is highly configurable and flexible.

Datasheet provides a list of supported features including Full Duplex, Asynchronous and Synchronous operation, Master or Slave operation mode, variable frame size, even or odd parity bits, one or two stop bits, several interrupt sources and even more. We won’t be able to cover all of them in tutorial – we will take common cases and probably something that might look interesting.

Setting USART hardware

USART is usually referred as RS232 interface what is wrong. USART stands for communication protocol while RS232 stands for signal logic levels and control signals. RS232 now is a thing of the past, but there are still lots of boards that support RS232. RS232 communication standard needs different signal levels than AVR microcontroller can provide. AVR usually gives 5V (or 3V) for logical “1” and 0V for logical “0”. RS232 standard uses +3V to 25V for logical “0” and -3V to -25V for logical “1”. For this there is a special TTL to RS232 converter chip used like MAX232. But if you look for development boards you will see than majority boards now uses USB communication standard and instead of using MAX232 there are an USB to TTL converters like FT232.

After installing proper drivers on PC there is a virtual COM port created that acts like regular serial interface. What ever converter chip we are gonna use microcontroller communication is same – USART and we are going to stick to it.

Importance of system clock frequency

Once we are using USART communications in our project there comes another dilemma – choosing a frequency of system clock. The problem is that USART module uses system clock to generate BAUD rates and sample incoming RX and outgoing Tx lines at some specific frequencies. Simply speaking to make perfect USART communication your AVR has to be clocked at frequency that can be divided by 1.8432MHz. This is why you can frequently find crystals like 7.3728MHz. Datasheet says that frequency can be different but baudrate error cannot exceed 0.5% in order to maintain reliable communication. In datasheet you will find a formula and pre-calculated tables of recommended baud rate settings for commonly used oscillator frequencies. In our tutorial we are using 16MHz crystal, so according to datasheet table (19-6) most of baud rates don’t exceed 0.5% mark. Of course you can use baudrates with higher errors but this may lead to sampling errors on receiver side.

Understanding USART data frames

USART communication uses special protocol to transmit data reliable. It consists of several parts:

  • 1 start bit;

  • 5,6,7,8 or 9 data bits;

  • no, even, or odd parity bit;

  • 1 or 2 stop bits.

No matter what format is chosen, transmitter and receiver sides must be configured same. Usually for terminal programs we use 1 start bit, 8 data bits, no parity and 1 stop bit.

Initializing USART communication

OK, leave theory behind and do some real examples. To do this and following experiments you can use Arduino Duemilanove board as general development board. USART is transmitted through TTL-USB converter, so once drivers are installed, on PC you should see a virtual RS232 port like COM3 or similar. Download any terminal program for receiving and sending serial data. Bray’s terminal works perfectly for this.

Starting USART communication isn’t that hard as it may seem. Firs of all we need to decide our baud rate. Our development board is clocked with 16MHz crystal and lets say we want our baud rate to be 9600. Datasheet says that such combination will give an error value 0.2% which is less than 0.5% and is safe to use. So in our program we define baudrate value:

#define USART_BAUDRATE 9600

Then next step is to calculate UBRR value which actually is a baud rate prescaller calculated out of system clock and desired baud rate. Usually it is calculated by following formula that is also defined as follows:

#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

UBRR0 register is a 16 bit register so it should be treated as any other 16-bit register in AVR – first write 8 bits to UBRR0H and then to UBRR0L.

// Set baud rate

UBRR0H = (uint8_t)(UBBR_VALUE>>8);

UBRR0L = (uint8_t)UBBR_VALUE;

Luckily in AVR GCC we can simply assign UBRR_VALUE value to UBRR0 and leave to compiler to take care of writing sequence.

UBRR0= UBRR_VALUE;

Next thing we have to take care is a data frame format. Lets use common format:

// Set frame format to 8 data bits, no parity, 1 stop bit
UCSR0C |= (1<<UCSZ1)|(1<<UCSZ0);

Now we can enable transmission by turning reception and transmission bits in UCSR0B register:

Sending and receiving USART data

In previous chapter we learned how to configure and enable USART0 transmission, now we are ready to perform simple data transmission. Lets write couple functions – one for transmitting data and another for receiving. There is a dedicated register UDR0 in USART0 module which is used to send and receive data. Once you write data to this register, data transfer is started automatically. In order to send next data byte program has to wait for transmission completion. It is done by checking transmission buffer empty flag UDRE0 in UCSR0A register. So always we have to make sure if data is already sent before writing another data byte. This may look as follows:

//wait while previous byte is completed

while(!(UCSR0A&(1<<UDRE0))){};

// Transmit data

UDR0 = u8Data;

Same situation is when receiving data. We need to check RXC0 flag to be raised when receive is complete and there is data present in receive buffer.

// Wait for byte to be received

while(!(UCSR0A&(1<<RXC0)) ){};

// Return received data

return UDR0;

Working USART0 example

Once we have our bits of program we can put everything in to one working program. Lets do simple test routine where we will send a byte number from terminal program, our program receives it, increments by one and send it back to terminal.

#include <avr/io.h>
#define USART_BAUDRATE 9600
#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
void USART0Init(void)
{
// Set baud rate
UBRR0H = (uint8_t)(UBRR_VALUE>>8);
UBRR0L = (uint8_t)UBRR_VALUE;
// Set frame format to 8 data bits, no parity, 1 stop bit
UCSR0C |= (1<<UCSZ01)|(1<<UCSZ00);
//enable transmission and reception
UCSR0B |= (1<<RXEN0)|(1<<TXEN0);
}
void USART0SendByte(uint8_t u8Data)
{
//wait while previous byte is completed
while(!(UCSR0A&(1<<UDRE0))){};
// Transmit data
UDR0 = u8Data;
}
uint8_t USART0ReceiveByte()
{
// Wait for byte to be received
while(!(UCSR0A&(1<<RXC0))){};
// Return received data
return UDR0;
}
int main (void)
{
uint8_t u8TempData;
//Initialize USART0
USART0Init();
    while(1)
    {
        // Receive data
		u8TempData = USART0ReceiveByte();
		// Increment received data
		u8TempData++;
		//Send back to terminal
        USART0SendByte(u8TempData);
    }
}

and here are results in terminal screen

As you can see transmitted data bytes are incremented by one and sent back to terminal program. Simple cryptography, isn’t it?

In this tutorial part we actually learned to do simple USART communications. Don’t get relaxed too much, because this method is least efficient because we need to pool down the flags in order to perform transmission and receive. These loops occupy microcontrollers resources and wastes energy. If you are looking for more robust and efficient USART transmission then we need to use interrupt driven USART communications. That is a topic for next tutorial part. As always your feedback is very welcome.

Download example program here.

Bookmark the permalink.

3 Comments

  1. Pingback: Electronics-Lab.com Blog » Blog Archive » Programming AVR USART with AVR-GCC. Part 1

  2. Pingback: Electronics-Lab.com Blog » Blog Archive » Programming AVR USART with AVR-GCC. Part 2

  3. Hi, dear Admin.
    I’m newbie in avrs so I ask:
    1) How to switch to external oscillator? Is there any action need to be done for it?
    2) I could not find where is F_CPU defined. I suspect it should be #define F_CPU 16000000L //16 MHz. Is it correct?

    Thanks, Vladimir.

Leave a Reply

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