In this tutorial we are going to get serial communication up and running. Having serial communication helps in that we can send human readable messages to a computer instead of decoding a blinking led, unless that’s your cup of tea go ahead and skip this tutorial 🙂
Getting started.
We are going to learn about header files as well here as I like to split my code up into files that have meaning, such as uart.h and uart.c having your functions etc in these files makes it easy for you to remember along with good function names where your function is. Incase the need arises to edit or change something in the function.
Adding files to our project.
First we need to add a few files to our project, board.h, uart.h, uart.c. board.h is where we will redefine all of our important pins, our clock, desired serial baud rate etc.
Uart.h and uart.c are going to house all of our important and reusable serial functions. One approach to creating new functions is to use main.c as a prototype area, define your function in main.c and test it out. If all goes well it can go into the library it was meant for.
So right-click on your project name go to add and add new. This will bring up a new window that allows you to pick and name a new file.

We will be adding two .h (header files) and one .c file to our project. Go ahead and create the required three files. Your file tree should look like this now.

In brief what is a header file? A header file contains the definitions for your desired library, this file is also included in your main program with the
#include
directive. This tells the Atmel studio you want this library and it’s functions compiled into your program. If you try to use a function from a library without including it the compiler will throw all kinds of errors some cryptic and won’t compile your project.
Board.h file contents.
So first we are going to redefine some pins define our clock and desired baud rate. I will comment the code so it’s well explained. I will go into further detail on registers when we do uart.c.
/*
board.h
Author OCybress
Summery used to define easy to. remember names
and other board related defines.
*/
#ifndef BOARD_H_
#define BOARD_H_
//this will be used in calculating baud.
//Its also defined in your compiler settings
//so this may throw a warning F_CPU redefined.
#define F_CPU 16000000UL
//move led pin definition here
#define LED_PIN PB5
#endif /* BAORDS_H_ */
uart.h header file
the uart header file contains some uart specific re defines and function prototypes, uart_init, uart_write_byte, and uart_read_byte
#ifndef UART_H_
#define UART_H_
//uart related defines
#define UART_DATA_REG UDR0
//port D bit 0, TX (register UDRE0)
#define TX UDRE0
//port D bit 1, RX (register RXC0)
#define RX RXC0
//baudrate can be set to 115200 as well since we are
//calculating the baud rate based off F_CPU
#define BAUD_RATE 9600
//Calculated baud rate basses on F_CPU
#define BAUD_PRESCALLER ((F_CPU/(BAUD_RATE * 16UL))-1)
//usart baud rate register high byte
#define USART_BAUD_REG_HB UBRROH
//usart baud rate register low byte
#define USART_BAUD_REG_LB UBRR0L
//tx/rx en bits
#define TX_ENABLE TXEN0
#define RX_ENABLE RXEN0
//Uart Control and Status Registers
#define UART_CTRL_STS_REG_A UCSR0A
#define UART_CTRL_STS_REG_B UCSR0B
#define UART_CTRL_STS_REG_C UCSR0C
//critical uart functions
void usart_init (void);
/*********************************/
/*Useful uart function prototypes*/
/*********************************/
uint8_t uart_read_byte();
void uart_write_byte(uint8_t data);
#endif /* UART_H_ */
uart.c
If you wish to use the bare registers instead of the redefined names feel free, it will work the same. Note they are already defined so you do not have to define the register names. For example if you wanted to use UDR0 instead of UART_DATA_REG that will not effect the code in any way. In fact I urge you to use the registers instead of redefining them for the first few times you write your uart functions. Once you are comfortable with the register names then you can rename as you see fit.
#include "board.h"
#include "uart.h"
/*************************/
/*Critical uart functions*/
/*************************/
void usart_init ()
{
USART_BAUD_REG_HB = (uint8_t(BAUD_PRESCALLER>>8);
USART_BAUD_REG_LB = (uint8_t)(BAUD_PRESCALLER);
UART_CTRL_STS_REG_B = (1<<RX_ENABLE )|(1<<TX_ENABLE);
//Data flow settings.
//data bits = 8, parity = none, stop bit = 1
UART_CTRL_STS_REG_C=(3<<ucsz00);
}
/***********************/
/*useful uart functions*/
/***********************/
/*Read one byte to the UDR0 register*/
uint8_t uart_read_byte()
{
//wait until the port is ready to read.
while((UART_CTRL_STS_REG_A &(1<<RX))==0){}
//return received data
return UART_DATA_REG;
}
/*Write one byte to the UDR0 register*/
void uart_write_byte(uint8_t data)
{
// wait until the port is ready to write.
while((UART_CTRL_STS_REG_A & (1<<TX)) == 0){}
//write data to port
UART_DATA_REG = data;
}
Putting it all together.
Now that we have everything setup, and our uart library is ready to go. Lets get main.c ready to spit some text to our terminal.
#include <avr/io.h>
//include our library files.
#include "boart.h"
#include "uart.h"
//Test message to send over uart.
const char aTestMsg[] = {"hello world"}
//example of a prototype function in main.c to later
//move into uart.c
void uart_write_string(char *s)
{
unsigned int i=0;
while(s[i] != 0x00)
{
uart_write_byte(s[i++]);
}
}
void main(void)
{
/*setup uart for 9600baud*/
usart_init();
//send byte with uart example Hello World
uart_write_byte("H");
uart_write_byte("e");
uart_write_byte("l");
uart_write_byte("l");
uart_write_byte("o");
uart_write_byte(" ");
uart_write_byte("W");
uart_write_byte("o");
uart_write_byte("r");
uart_write_byte("l");
uart_write_byte("d");
uart_write_byte("\n"); //new line
//send a string
uart_write_string(aTestMsg);
uint8_t u8_data;
//example of sending a string without the array.
uart_send_string("Send an option, h = hello, x = exit\r\n");
//uart_read_byte example
while(u8_data != 'x')
{
u8_data = uart_read_byte();
if(u8_data == 'h')
{
uart_write_string("hello\r\n");
}
if(u8_data == 'x')
{
uart_write_string("Exiting menu.\r\n");
u8_data = 'x';
}
}/* end while */
uart_write_string("End Example..\r\n");
}/* end main */
Once everything is in order. Compile the project and upload it to the avr (atmega328 series), load up a terminal such as cool term and you should be ready to go. Be sure to go to options and set your connection settings to 9600 baud, the rest should be the default settings.
I’ll post a terminal picture soon.
Going forward.
Now that we know how to toggle some bits, and use the uart to extract useful information from our microcontroller. We can do a lot more experimentation and still know whats going on inside the micro if anything goes wrong.
Next time or later on we may look at implementing Uart functions in ASM just for the hell of it 😉
Whats next?
I think the ADC is a good place to start next, as this allows us to input real world data into the micro , such as a 4×3 keyboard, analog sensors etc. Or we may do something with SPI flash… If you have any comments, questions, or corrections feel free to drop a comment or email me.
Like this:
Like Loading...
Recent Comments