Programing AVR USART module

USART Intro

AVR USART module is is one of more complex modules in AVR microcontroller, but it gives ability to interface microcontroller to other hardware like PC or other devices with many flexible features. USART differs from other AVR communication modules by its communication protocol and because it doesn't use separate pin for synchronizing communication. Synchronisation is made within protocol itself. It has several benefits as there are less wires used but there can be communication errors due to incorrect clock settings or due to clock non-stability. In some AVRs there is ability to synchronize communication externally with separate clock pin and run USAR in synchronous mode.

USART interface specification

Most people knows USART as RS232 interface – which is basically in computers. We cannot AVR name USART module to RS232 as there are not RS232 conditions met. One of them are voltage levels. AVR microcontroller operates lets say at 5V level, while RS232 logic levels are different: “0” - from +3V to 25V while “1” - from -3V to -25V. To make USART interface as RS232 there is an adapter needed. It is not hard to build one. You can connect two AVR or other microcontrollers with same voltages without any adapter via USART, but if you are going to communicate to PC, then you must connect through TTL – RS232 converter.

USART clock settings

As we mentioned USART does not use external clock. There is different synchronisation model. There is Baud Rate term used. Baud rate is understood as clock generator which runs internally. Baud rate generator is tied to system clock source. This is why choosing MCU clock is very important when devise has to communicate via USART.

Simply speaking Baud rate generator samples incoming data via Rx channel and sets Tx data to be sent precisely. Special care has to be taken when setting Baud rate generator. Probably all know that computer serial port can be configured for some specific baud rate (2400, 4800, 9600, 19200, 38400, 57600, 115200,...). This means that usually one of these standard baud rates is usually used. According to datasheet baud value needed to written to UBRR register can be calculated by formula:

UBRR=((f_clk/(BaudRate*16))-1)

where f_clk – microcontroller clock speed(Hz); BaudRate – communication baud rate.

Now lets take couple examples and calculate baud rate.

Example 1

If f_clk=4MHz and BaudRate=19200, then UBRR=(4000000/(19200·16)-1)=12.02083(3). We know that baud value is whole number number. So rounded number UBRR=12. But this gives us a little different baud rate BaudRate=4000000/(16·(UBRR+1))=19230.769~19231. So we wanted 19200 but got 19231. So we have communication error which is equal to Err=(19200/19231-1)·100%=-0.16%.

Example 2

If f_clk=3686400Hz and BaudRate=19200, then UBRR=(4433600/(19200·16)-1)=11. So we get whole number without fractional part. And baud rate error =0%.

Normally 0.5% error is acceptable and will work without problems. But if higher errors are accepted, then receiver will be less resistant to noise – especially in large serial frames. So it is better to select proper crystal at the beginning design phase.

Lets start USART

So when we cleared out one important part - baud rates it is time to start USART module. First of all USART has to be initialized before sending or receiving data. Setting procedure includes:

  • Setting Baud rate;

  • Setting frame format;

  • Enabling Transmitter and/or Receiver;

Lets do it.

#include <avr/io.h>

#ifndef F_CPU

//define cpu clock speed if not defined

#define F_CPU 3686400

#endif

//set desired baud rate

#define BAUDRATE 19200

//calculate UBRR value

#define UBRRVAL ((F_CPU/(BAUDRATE*16UL))-1)

void USART_Init()

{

//Set baud rate

UBRRL=UBRRVAL; //low byte

UBRRH=(UBRRVAL>>8); //high byte

//Set data frame format: asynchronous mode,no parity, 1 stop bit, 8 bit size

UCSRC=(1<<URSEL)|(0<<UMSEL)|(0<<UPM1)|(0<<UPM0)|

(0<<USBS)|(0<<UCSZ2)|(1<<UCSZ1)|(1<<UCSZ0);

//Enable Transmitter and Receiver

UCSRB=(1<<RXEN)|(1<<TXEN);

}

void USART_bounce()

{

//defien temp value for storing received byte

uint8_t Temp;

//wait until data is received

while(!(UCSRA&(1<<RXC))){};

//store received data to temp

Temp=UDR;

//wait for empty transmit buffer

while (!(UCSRA&(1<<UDRE))){};

//send received data back

UDR=Temp;

}

int main(void)

{

USART_Init();

while(1)

USART_bounce();

return 0;

}

This is very simple communication setting. In reality you may use more advanced techniques like interrupt receive and transmit mode. Using receive and transmit buffers, Full duplex mode and so on. These we will discuss in other articles.

Final word

Dont be confused if you try to simulate this code on AVRStudio. As UBRRH and UCSRC registers share same memory address there is special logic on updating UCSRC register – writing 1 to URSEL bit. Otherwise it will update UBRRH register. In AVRStudio there is a bug which don't care about (1<<URSEL) and updates both registers at once. Hopefully some day this will be fixed.

It is always better to put module specific functions in to separate c file so it could be easily reused in other designs. It is not good practice to mess main.c file and make it les readable. Example above works OK and can also be used if needed.

And last and probably most important notice is about clocks. Dont rely on AVR built in oscillator as they are less precise and frequency may fluctuate especially during temperature changes. Choosing wrong clock source may lead to significant errors or even communication failure. If USART communication is going to be used, better stick with crystal or ceramic resonators.