Back

UART

Loading views...

UART Protocol

Overview

UART (Universal Asynchronous Receiver/Transmitter) is a hardware communication protocol that uses asynchronous serial communication with configurable speed. It is one of the oldest and most widely used serial protocols in embedded systems, used to connect microcontrollers to sensors, GPS modules, GSM modems, and debug consoles.


1. Theory & Fundamentals

  • Problem solved: Simple point-to-point serial communication without a shared clock
  • Physical layer: Single-ended, TTL voltage levels (0V = logic 0, 3.3V or 5V = logic 1)
  • Wires: TX (transmit), RX (receive), optional RTS/CTS for flow control
  • Asynchronous: No shared clock; sender and receiver must agree on baud rate
  • Idle state: Line held HIGH
  • Common baud rates: 9600, 115200, 230400, 460800, 921600 bps

2. Frame / Packet Structure

IDLE  START  D0  D1  D2  D3  D4  D5  D6  D7  PARITY  STOP
HIGH   LOW   ←────────── Data bits ──────────→  (opt)  HIGH
  • Start bit: Always 0 (LOW), signals start of frame
  • Data bits: 5–9 bits, LSB first (typically 8 bits)
  • Parity bit: Optional — Even, Odd, or None
  • Stop bit(s): 1 or 2 bits, always HIGH

Example frame (0x41 = 'A', 8N1):

IDLE  S  1  0  0  0  0  0  1  0  STOP
 1    0  1  0  0  0  0  0  1  0   1

3. Protocol Mechanics

  • Synchronization: Receiver samples at middle of each bit period using internal clock
  • Baud rate tolerance: Typically ±2–3% acceptable mismatch
  • Hardware flow control: RTS (Request To Send) / CTS (Clear To Send)
  • Software flow control: XON (0x11) / XOFF (0x13) characters
  • Error detection: Parity bit, framing error (missing stop bit), overrun error
  • Break signal: TX held LOW for longer than one frame duration

4. Hardware Implementation

  • MCU pins: TX and RX (dedicated USART/UART peripheral pins)
  • Level shifter: Needed when mixing 3.3V and 5V devices (e.g., TXS0102)
  • RS-232 transceiver: MAX232 to convert TTL to ±12V RS-232 levels
  • RS-485 transceiver: MAX485 for differential multi-drop
  • Pull-up resistors: 10kΩ on TX/RX lines for noise immunity
  • PCB rules: Keep TX/RX traces short, avoid routing near switching circuits

5. Register-Level Programming (STM32 example)

// Enable USART2 clock
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;

// Configure GPIO PA2 (TX), PA3 (RX) as alternate function
GPIOA->MODER |= (2 << 4) | (2 << 6);  // Alternate function mode
GPIOA->AFR[0] |= (7 << 8) | (7 << 12); // AF7 = USART2

// Set baud rate: BRR = fCLK / baud
// For 115200 baud, 16MHz clock: BRR = 16000000/115200 = 139
USART2->BRR = 139;

// Enable TX, RX, USART
USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;

6. Driver Development

#define UART_BUF_SIZE 256
static uint8_t rx_buf[UART_BUF_SIZE];
static volatile uint16_t rx_head = 0, rx_tail = 0;

void UART_SendByte(uint8_t data) {
    while (!(USART2->SR & USART_SR_TXE)); // Wait TX empty
    USART2->DR = data;
}

void UART_SendString(const char *str) {
    while (*str) UART_SendByte(*str++);
}

// ISR for RX
void USART2_IRQHandler(void) {
    if (USART2->SR & USART_SR_RXNE) {
        rx_buf[rx_head] = USART2->DR;
        rx_head = (rx_head + 1) % UART_BUF_SIZE;
    }
}

uint8_t UART_ReadByte(uint8_t *data) {
    if (rx_head == rx_tail) return 0; // empty
    *data = rx_buf[rx_tail];
    rx_tail = (rx_tail + 1) % UART_BUF_SIZE;
    return 1;
}

7. Debugging & Testing

  • Logic analyzer: Decode UART frames at correct baud rate
  • Oscilloscope: Measure bit timing, check voltage levels
  • Loopback test: Connect TX to RX, verify echo
  • Common bugs:

- Baud rate mismatch → garbled data

- Floating RX pin → random characters

- Missing stop bit → framing error

- Buffer overflow → data loss

  • Signal integrity: Check for ringing, use 33Ω series resistor if needed

8. Real-World Applications

  1. GPS module (NEO-6M): NMEA sentences at 9600 baud
  2. GSM modem (SIM800L): AT commands at 115200 baud
  3. Debug console: printf over UART to PC terminal
  4. Bootloader: STM32 system bootloader uses UART for firmware update
  5. IMU (MPU-9250): Some variants use UART for data streaming

9. Advanced Topics & Edge Cases

  • Auto-baud detection: USART measures start bit to determine baud rate
  • LIN mode: UART variant with break field for automotive use
  • RS-485 half-duplex: Direction control via GPIO before TX
  • DMA transfers: Zero CPU overhead for large data blocks
  • Multi-processor mode: Address byte detection for multi-device bus
  • Wakeup from sleep: USART can wake MCU from Stop mode

10. Standards & Variants

Variant Voltage Distance Devices
TTL UART 0–5V or 0–3.3V <1m 1:1 only
RS-232 ±3–15V 15m 1:1
RS-485 ±1.5–6V diff 1200m Up to 32
RS-422 Differential 1200m 1:10

💡 Practical Examples

Example 1: Basic UART echo

while (1) {
    if (UART_ReadByte(&byte))
        UART_SendByte(byte); // Echo back
}

Example 2: GPS NMEA parsing

// Read until newline, then parse $GPGGA sentence
void ParseNMEA(char *sentence) {
    if (strncmp(sentence, "$GPGGA", 6) == 0) {
        // parse latitude, longitude, etc.
    }
}

Example 3: DMA-based bulk transfer (STM32)

// Configure DMA1 Channel 7 for USART2 TX
DMA1_Channel7->CPAR = (uint32_t)&USART2->DR;
DMA1_Channel7->CMAR = (uint32_t)tx_buffer;
DMA1_Channel7->CNDTR = length;
DMA1_Channel7->CCR = DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_EN;
USART2->CR3 |= USART_CR3_DMAT;

🧪 Practice Questions

Beginner

  1. What is the purpose of the start bit in UART?
  2. What is the default idle state of a UART TX line?
  3. If baud rate is 9600, how long is one bit in microseconds?
  4. What is 8N1 configuration?
  5. What happens if TX and RX baud rates don't match?

Intermediate

  1. Calculate the BRR register value for 115200 baud with a 72 MHz system clock.
  2. Explain the difference between hardware and software flow control.
  3. How would you implement a non-blocking UART receive using interrupts and a circular buffer?
  4. What is a framing error and what causes it?
  5. How does DMA improve UART performance compared to interrupt-driven transfer?

Advanced

  1. Design a UART bootloader that can receive Intel HEX files and flash them to internal memory.
  2. How would you implement reliable UART over a noisy RS-485 bus with 20 nodes?
  3. Explain how auto-baud rate detection works at the register level.
  4. What are the trade-offs between polling, interrupt, and DMA modes for UART?
  5. How would you debug intermittent data corruption in a 921600 baud UART link?

Hands-on Projects

  1. GPS Logger: Read NMEA from GPS module, parse coordinates, log to SD card over SPI.
  2. RS-485 Modbus RTU: Implement a Modbus RTU slave on STM32 using UART + RS-485 transceiver.
  3. UART Bootloader: Write a bootloader that receives a binary over UART and writes it to flash.

Checklist

  • [ ] Explain UART frame structure from memory
  • [ ] Calculate baud rate register values for any clock frequency
  • [ ] Write a polling-based TX/RX driver from scratch
  • [ ] Write an interrupt-driven RX driver with circular buffer
  • [ ] Implement DMA-based TX
  • [ ] Connect and test with a logic analyzer
  • [ ] Debug baud rate mismatch issues
  • [ ] Interface with a real GPS or GSM module
  • [ ] Implement hardware flow control (RTS/CTS)
  • [ ] Build an RS-485 half-duplex driver with direction control
  • [ ] Write a UART bootloader
  • [ ] Handle all UART error conditions (framing, overrun, parity)