USART in Tiva C — Tiva C
- Eslam El Hefny
- Tutorials, Tiva c
- April 9, 2025
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 (
FENbit) — 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
UARTCharGethangs 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) |