AVR USART Serial


In this guide we create a few serial functions that are very helpful in dealing with the USART on an AVR microprocessor.

If you’ve been following along in these guides, then your STK500 should be setup like this.

We need to add a couple of additional things if we are going to do serial port communications.

  1. The cables from PORTA to LEDS and PORTB to SWITCHES should still be connected. We will use them in the example program listing at the end of this guide.
  2. You must connect a second serial cable to the serial port labeled RS232 SPARE, and connect the other end of that cable to an unused serial port on your computer. We use Trendnet TU-S9 USB to Serial cables for this with no problems.
  3. Last, you need to connect the ATmega32’s TX and RX pins to the spare serial port on your STK500. Connect a two wire jumper cable from PD0 (that’s PORTD, Bit 0) to RXDand PD1 (that’s PORTD, Bit 1) to TXD, as is done in the picture below with the blue and white cable.

    The ATmega32 uses PD0 and PD1 for serial communications, and the TXD/RXD pins on the STK500 are tied to some logic (a MAX202 to be exact) that connects to RS232SPARE.

Here is how your serial ports should be set up.

We had to pull the blue plastic cover back a little on the USB to Serial adapters to get them to both fit on the DB9 connectors.

When wired this way, any bytes that you send out the USART from inside your AVR will show up at the TX pins of the RS232 SPARE connector. Likewise, any bits that show up on the RX pins of the RS232 SPARE connector will end up in your USART buffer for your program to use.

Terminal Program

A terminal program does nothing more than display on screen the bytes that are received from the serial port, and send to the serial port the characters that are typed on the keyboard.

When working with a microcontroller, a terminal program is an excellent way to see what is going on as your program runs, and maybe even send some commands to a program running on your microcontroller.

You need to have a terminal program running on the computer that RS2332 SPARE connects to. There are a couple of free choices out there.

  1. HyperTerminal comes with most versions of Windows and seems to get the job done. You can find it in your Start Menu under Accessories->Communications.
  2. Brays Terminal is one of the most popular, free terminal programs out there.

Go ahead and get your terminal program set up now. For this example we are going to use the following settings:

  • 19200 baud
  • 8 data bits
  • 1 stop bit
  • no parity
  • no handshaking

One Computer, 2 Serial Ports

It is very common to have both the RS232 CONTROL port and the RS232 SPARE port plugged in to the same computer. All you have to do is setup AVRStudio to point at the serial port (also called a comm port) that the RS232 CONTROL line is plugged in to. Then you configure your terminal program to connect to the serial port that RS232 SPAREconnects to.

As you develop your code, you can task switch between AVRStudio and the terminal program, and both will remain connected to your STK500.

Now that we have the hardware part taken care of, lets move on to the source code that you will use to talk to your terminal program.

USART Source Code

There are 3 things that we need to take care of in order to have basic serial communications.

  1. Initialize the USART for serial I/O at the correct baud rate
  2. Write a function that places a character in the output buffer of the USART
  3. Write a function that takes a character from the input buffer of the USART

There are 2 additional things that we should do to have complete serial communications.

  1. Write a function that sends a complete string of characters out the serial port.
  2. Write a function that checks if a character is waiting at the serial port input buffer.

Name them All usart_functionname()

We like the names all of our USART related functions to begin with usart_. This is to differentiate them from other functions that will be very similar, yet act on a different I/O device. For instance, later on we are going to have a bunch of lcd_ names such as lcd_putchar() and lcd_getchar() that would otherwise collide with our usart_ names, such asusart_putchar and usart_getchar.

1. Initialize USART For Serial I/O

The following code was written with the help of the ATmega32 users guide published by Atmel located here.

You must decide what speed you are going to initialize the USART at. Once you have decided that, you can write a #define statement that will make it easier to change your code later. We are going to go with 19,200 BAUD. Our define looks like this:

#define BAUD 19200
#define MYUBRR F_CPU/16/BAUD-1

The precompiler will do the math for you, none of this code actually runs in the microcontroller. The value in MYUBBR is going to be used in the USART initialization function.

F_CPU is handed to the precompiler by AVRStudio if you followed the instructions in our WinAVR guide. If you get the error error: ‘F_CPU’ undeclared (first use in this function) when you compile, then you need to go back and follow that guide again.

Here is the actual USART initialization function:

void usart_init( uint16_t ubrr) {
    // Set baud rate
    UBRRH = (uint8_t)(ubrr>>8);
    UBRRL = (uint8_t)ubrr;
    // Enable receiver and transmitter
    UCSRB = (1<<RXEN)|(1<<TXEN);
    // Set frame format: 8data, 1stop bit
    UCSRC = (1<<URSEL)|(3<<UCSZ0);
}

Again, this code is based on the ATMega32 user guide located here.

Call this function by passing in the MYUBBR value that we created with #define statements above, like this:

usart_init(MYUBBR);

This code first splits the 16 bit ubrr value into a lower half and an upper half, and then stores them in UBRRH and UBRRL appropriately.

Then it sets up UCSRB to enable both the transmitter and the receiver.

Finally it sets up the frame format in UCSRC as 8 data bits, 1 stop bit, no parity.

putchar() and getchar()

We need a function to get characters from the USART and one to put characters to the USART. In C, these functions are traditionally called getchar() and putchar() respectively. Here is what they look like.

void usart_putchar(char data) {
    // Wait for empty transmit buffer
    while ( !(UCSRA & (_BV(UDRE))) );
    // Start transmission
    UDR = data;
}
char usart_getchar(void) {
    // Wait for incoming data
    while ( !(UCSRA & (_BV(RXC))) );
    // Return the data
    return UDR;
}

The putchar() function simply waits for the transmit buffer to be empty by checking the UDRE Flag, Data Register Empty, before
loading it with new data to be transmitted. When UDRE becomes a 1, the while loop ends and the value passed in through data is put in the UDR register and transmitted.

The getchar() function waits for the RXC flag (Receive Complete) to go to 1, and then it returns the value in UDR, which is the received character.

Notice that putchar() requires a single byte of type char and has a void return type, while getchar() expects no parameter and has a single byte of type char return.

Notice how getchar() waits until a character is received before continuing on? This means that getchar() will not return control to the calling function until it receives a character. This behavior is called blocking, in that putchar() blocks any thing else from being able to run until it is done. We will get over this limitation next with the kbhit() function.

Check for Keyboard Hit

If you want to know if a key has been pressed at the keyboard, then you can ask the kbhit() function every once in a while. kbhit() will return zero if there are no characters waiting in the receive buffer, and it will return 1 if there are any characters waiting.

A common way to write embedded programs is to have the embedded program check for status changes at various intervals. This is called polling. A program can poll the kbhit()function every once in a while and find out if it should call the getchar() routine. If kbhit() returns 1, then getchar() is going to get a char and will not block. Here is whatkbhit() looks like:

unsigned char kbhit(void) {
    //return nonzero if char waiting polled version
    unsigned char b;
    b=0;
    if(UCSRA & (1<<RXC)) b=1;
    return b;
}

All that kbhit() has to do is check the RXC (Receive Complete) bit, and return 1 if it is set. There are ways to optimize this function a little bit such as getting rid of the variable b, but it can take away from the readability of it, so we like to leave it like this.

Printing a String with pstr()

Sometimes it is handy to print an entire string out to the terminal program. That is exactly what pstr (short for printstring) does.

void usart_pstr(char *s) {
    // loop through entire string
    while (*s) {
        usart_putchar(*s);
        s++; 
    }
}

This little function accepts a string (a char pointer) as an argument, and loops over that string one character at a time dumping each character to our putchar() function, until it encounters a zero. This effectively prints a NULL terminated string to the console. NULL terminated strings is the C standard way of representing a string. This simply means that the last character in a string is the byte value of 0. Do not confuse this with the ASCII value of 0, which is 48 decimal, or 0x30 hex.

Putting it All Together

Lets write a program using all of these USART functions. The program will do the following:

  • Print a welcome string on screen
  • Continually watch for a character to arrive at the serial port. When one arrives, print it back out incremented by 1. This will translate an ‘a’ to a ‘b’, and a ‘1’ to a ‘2’, and so on.
  • Map the PINB input switches to the PORTA output LEDs.
  • If PB0 is pressed, say something to the user, like “Stop poking me!”

This might sound like a lot, but once you have these concepts down you are ready to tackle just about anything. Here is the program listing.

// ********************************************************************************
// Includes
// ********************************************************************************
#include <avr/io.h>
#include <stdio.h>
#include <stdbool.h>
// ********************************************************************************
// Macros and Defines
// ********************************************************************************
#define BAUD 19200
#define MYUBRR F_CPU/16/BAUD-1
// ********************************************************************************
// Function Prototypes
// ********************************************************************************
void usart_init(uint16_t ubrr);
char usart_getchar( void );
void usart_putchar( char data );
void usart_pstr (char *s);
unsigned char usart_kbhit(void);
 
// ********************************************************************************
// Main
// ********************************************************************************
int main( void ) {
    // configure PORTA as output
    DDRA = 0xFF;
    // setup PORTB data direction as an input
    DDRB = 0;
    // make sure it is high impedance and will not source
    PORTB = 0;
    // fire up the usart
    usart_init ( MYUBRR );
    // dump some strings to the screen at power on
    usart_pstr("Ready to rock and roll!\n\r");
    usart_pstr("Type in a character, and I will transpose it up by 1:\n\r");
    // main loop
    while(true) {
        // if a key has been pressed, then process it
        if(usart_kbhit()) {
        usart_putchar(usart_getchar() + 1);
        }
        // map the PINB inputs to the PORTA outputs
        PORTA = PINB;
 
        // a little humor is always good, PB0 gets the user yelled at
        if(bit_is_clear(PINB,PB0)) {
            usart_pstr("OUCH! Stop poking me!\n\r");
        }
    }
}
 
// ********************************************************************************
// usart Related
// ********************************************************************************
void usart_init( uint16_t ubrr) {
    // Set baud rate
    UBRRH = (uint8_t)(ubrr>>8);
    UBRRL = (uint8_t)ubrr;
    // Enable receiver and transmitter
    UCSRB = (1<<RXEN)|(1<<TXEN);
    // Set frame format: 8data, 1stop bit
    UCSRC = (1<<URSEL)|(3<<UCSZ0);
}
void usart_putchar(char data) {
    // Wait for empty transmit buffer
    while ( !(UCSRA & (_BV(UDRE))) );
    // Start transmission
    UDR = data;
}
char usart_getchar(void) {
    // Wait for incoming data
    while ( !(UCSRA & (_BV(RXC))) );
    // Return the data
    return UDR;
}
unsigned char usart_kbhit(void) {
    //return nonzero if char waiting polled version
    unsigned char b;
    b=0;
    if(UCSRA & (1<<RXC)) b=1;
    return b;
}
void usart_pstr(char *s) {
    // loop through entire string
    while (*s) {
    usart_putchar(*s);
    s++;
    }
}

You can download the complete source code here.

Go ahead and copy and paste this code in to AVRStudio and hit compile. It should compile down to 452 bytes and be ready to download in to your chip.

Once it is downloaded, every time you reset your AVR you should see Ready to rock and roll! printed on screen. If you do not see that, then something is not quite right.

Things to Check if you Do Not Get Output On Screen

  • Make sure that you have your terminal program connected to the correct comm port.
  • Make sure that your terminal program is setup for 19200 baud, 8 data bits, 1 stop bit, no parity and no handshaking
  • Make sure that you have told AVRStudio the correct frequent of your AVR.
  • Check your fuses to make sure that you are using the correct oscillator.

Up Next – A Menu

in our next guide we will show you how to make a menu that responds to which key you press, and still does system tasks in the background. Your AVR will appear to multitask. In fact, this is the beginning of a Real Time Operating System.

Continue on to our next guide to see how to make an embedded menu in your AVR.

Or head back to our index of AVR Guides here.


Make electronics fun.

One comment

  • Hello dear jason very good article,i have one question because i am tired trying to receive entire string.three days working on it but no result,so if you will have some time please help.

Leave a Reply

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