Serial Communications

The AVR ATmega provides the following serial peripheral interfaces that allow you communicate with external devices.

  • SPI
  • I2C
  • Universal Synchronous Asynchronous Receiver Transmitter (USART)

The SPI and I2C interfaces are synchronous interfaces that require transmission of both the clock and data. These are typically used for short distance communication between the microcontroller and external devices such as accelerometers.

The USART interface is often used to provide a full duplex communications channel between different microcontrollers and from the microcontroller to a personal computer.

The USART is able to operate at a range of baud rates. Typical baud rates are: 2400, 4800, 9600 (this is the most typical rate used in older devices),14400, 19200, 28800, 38400, 57600, 76800, 115200.

Initialisation

To use the USART it is necessary to initialise the baud rate as follows. Here the baud rate is intialised to 9600

#define F_CPU 16000000UL
#define BAUDRATE 9600
#define BAUD_PRESCALER (((F_CPU / (BAUDRATE * 16UL))) - 1)
UBRR0 = BAUD_PRESCALER;// Set the baud rate prescale rate register

You also need to set the data frame format. this includes the number of data bits, stop bits and parity as shown below

USART data format register settings

Fig. 1 USART data format register settings

UCSR0C = ((0<<USBS0)|(1 << UCSZ01)|(1<<UCSZ00));// Set frame format: 8data, 1 stop bit.

It is also necessary to enable the USART transmitter and receiver. The USART initialisation can be put together as a single intialisation function

void USART_init(void)
{
	// Macro to determine the baud prescale rate see table 22.1 in the Mega datasheet
	#define BAUD_PRESCALER (((F_CPU / (BAUDRATE * 16UL))) - 1)

	UBRR0 = BAUD_PRESCALER;			// Set the baud rate prescale rate register

UCSR0C = ((0<<USBS0)|(1 << UCSZ01)|(1<<UCSZ00));   // Set frame format: 8data, 1 stop bit. See Table 22-7 for details
UCSR0B = ((1<<RXEN0)|(1<<TXEN0));		// Enable receiver and transmitter
}

Transmission

Fig. 2 USART Tx process

Fig. 2 USART Tx process

To transmit data you simply write the bytes to be transmitted to the UDR register. However it is necessary to make sure that this register is empty before doing this to avoid overwriting data that has not yet been transmitted. This is done by checking the UDRE bit in the UCSRnA register before writing as shown in the UML diagram in figure 2. The transmit code is written as a function called USART_send

void USART_send( unsigned char data)
{
	//while the transmit buffer is not empty loop
	while(!(UCSR0A & (1<<UDRE0)));

	//when the buffer is empty write data to the transmitted
	UDR0 = data;
}

USART_send can be used as follows

USART_send('a');	// Send a
USART_send(0x61);	// Send a
USART_send('\r');	// Send carriage return
USART_send('\n');	// Send linefeed

Note that single characters are surrounded by single quotes and special characters such as carriage return are preceded by \

Often you would like to transmit multiple characters so it is also useful to write a function that accepts a char array that contains the string that you wish to transmit and transmits the entire string. the function USART_putstring, below, does this. Note that the input to this function is actually just the pointer to the memory location where the char array that you wish to send is stored.

void USART_putstring(char* StringPtr)
// sends the characters from the string one at a time to the USART
{
	while(*StringPtr != 0x00)
	{
		USART_send(*StringPtr);
		StringPtr++;
	}
}

USART_putstring can be called either by directly specifying the string in the function call e.g.

USART_putstring("Hello There.");

Note that strings are surrounded by double quotes. Or by passing the variable where the string is stored e.g.

volatile uint8_t buffer[20];
USART_putstring(buffer);

Reception

Fig. 3 USART receive process

Fig. 3 USART receive process

The process from receiving data is similar to transmission. In that we simply need to check the RXC bit in the UCSRnA register to see if any data has been received and then read in the data from the UDR register. the UML diagram that illustrates this process is shown in figure 3. This can be accomplished with the following function

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

Simply calling this function will cause your program to wait until a byte is received by the USART. This might be the desired operation, however, if your application is also meant to deal with other tasks this will cause your application to hang until a byte is received. A better approach to handle USART reception is to make use of the USART interrupt. When this interrupt is enabled an interrupt will be asserted whenever a byte is received and your program can then read in the data in the interrupt service routine. To use this mode of operation it is necessary to first enable the USART to use interrupts using the following initialization code

void USART_interrupt_init(void)
{
	cli();
	// Macro to determine the baud prescale rate see table 22.1 in the Mega datasheet
	#define BAUD_PRESCALER (((F_CPU / (BAUDRATE * 16UL))) - 1)

	UBRR0 = BAUD_PRESCALER;					// Set the baud rate prescale rate register
	UCSR0B = ((1<<RXEN0)|(1<<TXEN0)|(1 << RXCIE0));		// Enable receiver and transmitter and Rx interrupt
	UCSR0C = ((0<<USBS0)|(1 << UCSZ01)|(1<<UCSZ00));	// Set frame format: 8data, 1 stop bit. See Table 22-7 for details
	sei();
}

In your main source code file it is necessary to include the interrupt header file and to define the interrupt service routine.

#include <avr/interrupt.h>

volatile int i=0;
volatile uint8_t buffer[20];
volatile uint8_t StrRxFlag=0;

ISR(USART0_RX_vect)
{
	buffer[i]=UDR0;			//Read USART data register
	if(buffer[i++]=='\r')	//check for carriage return terminator and increment buffer index
	{
		// if terminator detected
		StrRxFlag=1;		//Set String received flag
		buffer[i-1]=0x00;	//Set string terminator to 0x00
		i=0;				//Reset buffer index
	}
}

Here USART0_RX_vect is the interrupt vector name for the USART 0 receiver interrupt vector. A full list of interrupt vector names can be found here. The interrupt service routine (ISR) above simply puts the received byte into the char array variable called buffer and increments the buffer index by one si that it is ready to receive the next byte. The ISR also checks for the termination character, in this case carriage return, that the sender is going to use to signal the end of transmission. When this is detected the ISR sets the StrRxFlag to indicate to the main program that the received string is now ready. Note the use of the volatile keyword when defining the variables that are used inside the ISR to ensure that the compiler knows that these variables can be changed at any time by the ISR.

The main code then periodically checks the StrRxFlag, and when this is set it uses the data in the buffer, resetting the StrRxFlag to indicate that it has read the data. In this example the main program simply send the received string back to the sender.

int main(void)
{
	USART_interrupt_init();   // Initailise USART 0 to use the receive interrupt

	while(1)
	{
		if (StrRxFlag)
		{
			USART_putstring(buffer);
			USART_send('\r');			// Send carriage return
			USART_send('\n');			// Send linefeed
			StrRxFlag=0;				// Reset String received flag
		}
	}
}

To test your code you will need a Serial terminal program that allows you to send and receive bytes over the serial port. If you are using AVR Studio 6 then you can download a terminal extension that will run right inside AVR studio 6. See how to do this here. You can also use a simple terminal program such as HyperTerminal, PuTTy or my preferred option Br@y Terminal.

A complete code example using interrupt driven serial communication is available to download here.

When using the AVR terminal program you need to toggle the CR switch to ensure that the CR terminator is appended to your command.

Advertisements

2 thoughts on “Serial Communications

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s