3 Send ‘Hello World’

This use case demonstrates how to send a string to the PC and visualize it in the terminal. The USART will be configured for Asynchronous mode, and only the TX pin will be used.

The USART1 instance default pins, PC0 (TX) and PC1 (RX), are connected directly to the debugger interface. They can be used to send data bytes to a host PC by using the Virtual COM Port (CDC) interface of the Embedded Debugger (EDBG), bypassing the need for an additional UART to USB converter.

This use case follows the steps:

  • Set the baud rate
  • Enable the Transmitter (TX)
  • Configure the pins

How to Configure the Baud Rate

The baud rate shows how many bits are sent per second. The higher the baud rate, the faster the communication. Common baud rates are 1200, 2400, 4800, 9600, 19200, 38400, 57600 and 115200, with 9600 being the most commonly used one.

On the megaAVR 0-series, the maximum baud rate is limited to 1/8 * (maximum USART clock) in Asynchronous mode and 1/2 * (maximum USART clock) in Synchronous mode. To set the baud rate, write to the USARTn.BAUD register:

USART1.BAUD = (uint16_t)USART1_BAUD_RATE(9600);

Notice the use of the USART1_BAUD_RATE macro to compute the register’s value from the baud value. This macro must be defined based on the formula in the image below. This formula depends on the USART configurations, so it might not be the same in other modes.

Figure 3-1. Equations for Calculating Baud Rate Register Setting

S is the number of samples per bit. In Asynchronous operating mode, it is 16 (NORMAL mode) or 8 (CLK2X mode). For Synchronous operating mode, S equals 2.

This is how the USART1_BAUD_RATE macro is defined. It uses F_CPU because the USART clock matches the CPU clock.

#define F_CPU 3333333
#define USART1_BAUD_RATE(BAUD_RATE) ((float)(F_CPU * 64 / (16 * (float)BAUD_RATE)) + 0.5)

How to Enable the Transmitter and Send Data

Depending on the application needs, the user may choose to enable only the receiver or the USART module transmitter. Since in this use case only the microcontroller sends messages, only the transmitter needs to be enabled.

USART1.CTRLB |= USART_TXEN_bm;

Before sending data, the user needs to check if the previous transmission is completed by checking the USARTn.STATUS register. The following code example waits until the transmit DATA register is empty and then writes a character to the USARTn.TXDATA register:

void USART1_sendChar(char c) 
{
    while (!(USART1.STATUS & USART_DREIF_bm))
    {
        ;
    }        
    USART1.TXDATAL = c;
}

The Send register is nine bits long. Therefore, it was split into two parts: The lower part that holds the first eight bits, called TXDATAL, and the higher part that holds the remaining one bit, called TXDATAH. TXDATAH is used only when the USART is configured to use nine data bits. When used, this ninth bit must be written before writing to USARTn.TXDATAL, except if CHSIZE in USARTn.CTRLC is set to ‘9-bit - Low byte first’, where USARTn.TXDATAL should be written first.

How to Configure Pins

The TX pin must be configured as an output. By default, each peripheral has some associated pin positions. The pins are described in the Multiplexed Signals section in the device-specific data sheet. Each USART has two sets of pin positions. The default and alternate pin positions for USART1 are shown below.

Table 3-1. Multiplexed Signals
Pin name(1,2)USARTn
PC01,TxD
PC11,RxD
PC21,XCK
PC31,XDIR
VDD
GND
PC41,TxD(3)
PC51,RxD(3)
PC61,XCK(3)
PC71,XDIR(3)
Note:
  1. Pin names are of type Pxn, with x being the PORT instance (A,B,C, ...) and n the pin number. Notation for signals is PORTx_PINn. All pins can be used as event input.
  2. All pins can be used for external interrupt, where pins Px2 and Px6 of each port have full asynchronous detection.
  3. Alternate pin positions. For selecting the alternate positions, refer to the PORTMUX documentation.

For this use case, the default USART1 pin position is used; this is PC0 to PC1. The following code sets the TX pin direction to output.

PORTC.DIR |= PIN0_bm;

To use the alternate pin positions, write to the PORTMUX.USARTROUTEA register.

PORTMUX.USARTROUTEA |= PORTMUX_USART10_bm;
Note: In this example, the default pin position is used, not the alternate one.

Demo Code

This code example is used to continuously send the 'Hello World!' string through USART. A string is sent character by character. The ‘USART1_sendString’ function calls the ‘USART1_sendChar’ function for each character in the ‘Hello Word!’ string. Before sending each character, the ‘USART1_sendChar’ function waits for the previous character transmission to be completed. This is done by polling the Data Register Empty Interrupt Flag, DREIF, from the STATUS register until it is set.

#define F_CPU 3333333
#define USART1_BAUD_RATE(BAUD_RATE) ((float)(F_CPU * 64 / (16 * (float)BAUD_RATE)) + 0.5)

#include <avr/io.h>
#include <util/delay.h>
#include <string.h>

void USART1_init(void);
void USART1_sendChar(char c);
void USART1_sendString(char *str);

void USART1_init(void)
{
    PORTC.DIR &= ~PIN1_bm;
    PORTC.DIR |= PIN0_bm;
    
    USART1.BAUD = (uint16_t)USART1_BAUD_RATE(9600);

    USART1.CTRLB |= USART_TXEN_bm; 
}

void USART1_sendChar(char c)
{
    while (!(USART1.STATUS & USART_DREIF_bm))
    {
        ;
    }        
    USART1.TXDATAL = c;
}

void USART1_sendString(char *str)
{
    for(size_t i = 0; i < strlen(str); i++)
    {
        USART1_sendChar(str[i]);
    }
}

int main(void)
{
    USART1_init();
    
    while (1) 
    {
        USART1_sendString("Hello World!\r\n");
        _delay_ms(500);
    }
}
Note: For the delay function to work properly, define the CPU frequency before including the <avr/delay.h> header.
Note: The default configurations are used for the CPU clock frequency and the USART frame structure. The default CPU and peripheral clock frequency is 3.33 MHz. The USART default frame structure is comprised of eight data bits, no parity bit, and one stop bit (8N1).

An MPLAB® Code Configurator (MCC) generated code example for AVR128DA48 with the same functionality as the one described in this section can be found here: