all
Chapter 9 of 20

USART in Tiva C — Tiva C

Eslam El Hefny Apr 9, 2025 6 min read
45% done

USART in Tiva C — Tiva C

Overview

The TM4C123GH6PM integrates eight UART modules (UART0–UART7), each capable of standard asynchronous serial communication with programmable baud rates up to 5 Mbps. UART0 is particularly important as it is connected to the on-board ICDI debug chip, enabling easy terminal communication without external hardware.


Beginner Level — What & Why

What is UART?

UART (Universal Asynchronous Receiver-Transmitter) is one of the simplest serial communication protocols. It sends data as a stream of bits at an agreed-upon speed (baud rate), framed with start and stop bits. No shared clock signal is needed between devices — only two wires: TX (transmit) and RX (receive).

Real-World Analogy

UART is like two people exchanging morse code over walkie-talkies. Both agree on the speed beforehand (baud rate). One talks, the other listens. Start and stop beeps frame each letter (character frame). No wristwatch synchronisation is needed — only matching speeds.

What Problem Does It Solve?

  • Debug output from microcontroller to PC terminal (the “printf of embedded systems”)
  • Communication with GPS modules, Bluetooth adapters, sensors with UART interfaces
  • PC-to-MCU command interface

Key Terms

Term Meaning
Baud rate Bits per second (typically 9600, 115200)
FIFO 16-byte hardware buffer that holds received/transmitted data
8N1 8 data bits, No parity, 1 stop bit — the most common frame format
BRD Baud Rate Divisor — integer + fractional parts
UARTFR Flag register: TX/RX FIFO status

Intermediate Level — How It Works

Baud Rate Calculation

The UART clock on TM4C123 is the system clock (80 MHz by default).

BRD = f_clk / (16 × baud_rate)

For 115200 baud at 80 MHz:
BRD = 80,000,000 / (16 × 115200) = 43.4028

Integer part  (UARTIBRD) = 43
Fractional part (UARTFBRD) = round(0.4028 × 64) = round(25.78) = 26

Key Registers

Register Offset Description
UARTDR 0x000 Data Register (TX write / RX read)
UARTRSR 0x004 Receive Status / Error Clear
UARTFR 0x018 Flag: TXFF, RXFE, BUSY, TXFE, RXFF
UARTIBRD 0x024 Integer Baud Rate Divisor
UARTFBRD 0x028 Fractional Baud Rate Divisor
UARTLCRH 0x02C Line Control (word length, FIFO, parity, stop)
UARTCTL 0x030 Control (UARTEN, TXE, RXE, LBE)
UARTIFLS 0x034 FIFO Level Select (interrupt thresholds)
UARTIM 0x038 Interrupt Mask
UARTRIS 0x03C Raw Interrupt Status
UARTMIS 0x040 Masked Interrupt Status
UARTICR 0x044 Interrupt Clear

UARTFR flag bits:

Bit Name Meaning
3 BUSY UART is transmitting
4 RXFE RX FIFO empty (no data to read)
5 TXFF TX FIFO full (cannot write)
6 RXFF RX FIFO full
7 TXFE TX FIFO empty

TivaWare DriverLib API

#include "driverlib/uart.h"
#include "driverlib/pin_map.h"

/* Enable UART0 and Port A */
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_UART0));
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOA));

/* Assign PA0=RX, PA1=TX alternate functions */
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

/* Configure 115200, 8N1 */
UARTConfigSetExpClk(UART0_BASE,
                    SysCtlClockGet(),  // 80 MHz
                    115200,
                    UART_CONFIG_WLEN_8 |
                    UART_CONFIG_STOP_ONE |
                    UART_CONFIG_PAR_NONE);

/* Send a character */
UARTCharPut(UART0_BASE, 'A');

/* Receive a character (blocking) */
int32_t c = UARTCharGet(UART0_BASE);

/* Non-blocking receive */
if (UARTCharsAvail(UART0_BASE))
    int32_t c = UARTCharGetNonBlocking(UART0_BASE);

UARTprintf via uartstdio

#include "utils/uartstdio.h"

/* Initialize UARTStdio on UART0 at 115200 baud */
UARTStdioConfig(0, 115200, SysCtlClockGet());

/* printf-style output over UART */
UARTprintf("Hello from TM4C123! Tick = %d\r\n", tick);

Advanced Level — Deep Dive

Bare-Metal UART Configuration

#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_uart.h"
#include "inc/hw_gpio.h"

void UART0_Init(uint32_t ui32SysClock, uint32_t ui32Baud)
{
    /* Enable UART0 and GPIO Port A clocks */
    HWREG(SYSCTL_RCGCUART)  |= (1U << 0);  // UART0
    HWREG(SYSCTL_RCGCGPIO)  |= (1U << 0);  // Port A
    while (!(HWREG(SYSCTL_PRUART)  & (1U << 0)));
    while (!(HWREG(SYSCTL_PRGPIO)  & (1U << 0)));

    /* Disable UART while configuring */
    HWREG(UART0_BASE + UART_O_CTL) &= ~UART_CTL_UARTEN;

    /* Calculate baud rate divisor
     * BRD_integer  = SysClock / (16 * baud)
     * BRD_fraction = round((BRD - int) * 64) */
    uint32_t ui32IBrd = ui32SysClock / (16 * ui32Baud);
    uint32_t ui32FBrd = (((ui32SysClock * 8) / ui32Baud) + 1) / 2 & 0x3F;

    HWREG(UART0_BASE + UART_O_IBRD) = ui32IBrd;
    HWREG(UART0_BASE + UART_O_FBRD) = ui32FBrd;

    /* 8-bit, 1 stop bit, no parity, FIFO enabled */
    HWREG(UART0_BASE + UART_O_LCRH) = UART_LCRH_WLEN_8 | UART_LCRH_FEN;

    /* Configure PA0/PA1 for UART alternate function */
    HWREG(GPIO_PORTA_BASE + GPIO_O_AFSEL) |= 0x03;   // PA0, PA1
    HWREG(GPIO_PORTA_BASE + GPIO_O_PCTL)  =
        (HWREG(GPIO_PORTA_BASE + GPIO_O_PCTL) & ~0xFF) | 0x11;  // PMC=1 (UART)
    HWREG(GPIO_PORTA_BASE + GPIO_O_DEN)   |= 0x03;

    /* Enable UART: TX and RX enabled */
    HWREG(UART0_BASE + UART_O_CTL) =
        UART_CTL_UARTEN | UART_CTL_TXE | UART_CTL_RXE;
}

void UART0_SendChar(char c)
{
    /* Wait while TX FIFO is full */
    while (HWREG(UART0_BASE + UART_O_FR) & UART_FR_TXFF);
    HWREG(UART0_BASE + UART_O_DR) = (uint32_t)c;
}

char UART0_RecvChar(void)
{
    /* Wait while RX FIFO is empty */
    while (HWREG(UART0_BASE + UART_O_FR) & UART_FR_RXFE);
    return (char)(HWREG(UART0_BASE + UART_O_DR) & 0xFF);
}

Interrupt-Driven UART

/* Enable RX interrupt when FIFO reaches 1/2 full */
UARTFIFOLevelSet(UART0_BASE, UART_FIFO_TX4_8, UART_FIFO_RX4_8);
UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_RT);
IntEnable(INT_UART0);
IntMasterEnable();

void UART0_Handler(void)
{
    uint32_t ui32Status = UARTIntStatus(UART0_BASE, true);
    UARTIntClear(UART0_BASE, ui32Status);

    while (UARTCharsAvail(UART0_BASE))
    {
        char c = (char)UARTCharGetNonBlocking(UART0_BASE);
        /* Echo back */
        UARTCharPut(UART0_BASE, c);
    }
}

FIFO and Flow Control

The UART has a 16-byte deep TX FIFO and a 16-byte deep RX FIFO. FIFO interrupt thresholds (UARTIFLS) can be set to 1/8, 1/4, 1/2, 3/4, or 7/8 full. Hardware flow control (RTS/CTS) is available on UART1.

Gotchas

  • UARTLCRH must be written after UARTIBRD/UARTFBRD — writing LCRH latches the baud rate registers.
  • FIFO enabled by default (FEN bit) — if you disable it, each write/read goes directly to a single buffer register.
  • UART0 is the debug UART on LaunchPad — it routes through the ICDI chip to the PC’s USB virtual COM port.
  • Blocking UARTCharGet hangs the CPU if no data arrives — always use timeout or interrupt-based reception in production code.

Step-by-Step Example

/*
 * uart_echo.c
 * UART0 echo: receives characters from PC terminal, sends them back
 * Board  : TM4C123GXL EK LaunchPad (UART0 via USB Virtual COM Port)
 * SDK    : TivaWare_C_Series-2.2.x
 * Terminal: 115200 baud, 8N1
 */

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/uart.h"
#include "driverlib/pin_map.h"
#include "utils/uartstdio.h"

int main(void)
{
    /* Step 1: 80 MHz clock */
    SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL |
                   SYSCTL_OSC_MAIN   | SYSCTL_XTAL_16MHZ);

    /* Step 2: Enable peripherals */
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_UART0));
    while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOA));

    /* Step 3: Configure PA0 (RX) and PA1 (TX) */
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

    /* Step 4: Configure UARTStdio (wraps UART0 with printf) */
    UARTStdioConfig(0, 115200, SysCtlClockGet());

    /* Step 5: Print banner */
    UARTprintf("\r\n=== TM4C123 UART Echo Demo ===\r\n");
    UARTprintf("Type characters and press Enter:\r\n");

    /* Step 6: Echo loop */
    while (1)
    {
        /* UARTCharGet blocks until a character arrives */
        int32_t c = UARTCharGet(UART0_BASE);

        /* Echo the character back */
        UARTCharPut(UART0_BASE, (uint8_t)c);

        /* Add newline after carriage return for readability */
        if (c == '\r')
            UARTCharPut(UART0_BASE, '\n');
    }
}

Summary

Key Point Details
UART modules UART0–UART7
UART0 location PA0 (RX), PA1 (TX)
Debug UART UART0 via USB ICDI on LaunchPad
Max baud rate 5 Mbps
Baud register UARTIBRD (integer) + UARTFBRD (fractional)
Frame format Configurable via UARTLCRH
FIFO depth 16 bytes TX, 16 bytes RX
printf UARTStdioConfig + UARTprintf from utils/uartstdio.h
RX interrupt UART_INT_RX, UART_INT_RT (receive timeout)

Next Chapter

I2C Interface

Share: