UART: Universal Asynchronous Receiver-Transmitter
UART is a simple asynchronous serial link used for debug consoles, GPS receivers, modems, Bluetooth modules, bootloaders, and factory test ports. It sends one bit stream per direction with no separate clock wire. Both ends agree on baud rate and frame format before communication starts.
UART is easy to bring up, but it is also easy to miswire. Most failures come from the lowest layer: crossed pins, missing ground, wrong voltage level, wrong baud rate, or inverted RS-232 signaling.
Learning Objectives
By the end of this lesson, you should be able to:
- explain why UART is asynchronous and how the receiver finds each byte;
- decode an 8N1 UART frame;
- calculate bit time, frame time, and approximate throughput;
- wire TX, RX, and GND correctly between two logic-level devices;
- choose level shifting when voltage levels are incompatible;
- debug baud, framing, parity, overrun, and wiring faults.
Core Idea: Shared Speed, No Clock Wire
Synchronous buses such as SPI send a clock. UART does not. The transmitter and receiver are configured to the same baud rate, and each side uses its own local clock.
The idle line is logic HIGH for normal TTL or CMOS UART. A frame begins when the transmitter pulls TX LOW for a start bit. The receiver uses that falling edge to align its sampling points near the middle of each bit.
UART Frame Format
The most common format is 8N1: eight data bits, no parity bit, and one stop bit.
| Field | Logic level or content | Purpose |
|---|---|---|
| Idle | HIGH | no byte being transmitted |
| Start | LOW | marks the beginning of a frame |
| Data | 5 to 9 bits, commonly 8 | byte payload, least significant bit first |
| Parity | optional | simple odd/even error check |
| Stop | HIGH for 1, 1.5, or 2 bit times | gives the receiver a clean frame end |
For the ASCII character A, the byte is 0x41, binary 0100 0001. UART sends it least significant bit first: 1, 0, 0, 0, 0, 0, 1, 0.
title "UART 8N1 example, byte 0x41"
time start=0 end=10 unit=bit divisions=10
TX: square label="TX line, illustrative bits" low=0 high=1 duty=50 cycles=5 unit=logic color=#2563eb
marker START at=1 label="start low" color=#dc2626
marker STOP at=9 label="stop high" color=#16a34a
The waveform is illustrative. A real oscilloscope trace would show each bit value held for one bit time, with rise/fall time and noise from the actual circuit.
Baud Rate, Bit Time, And Throughput
For ordinary UART, one symbol is one bit, so baud rate is approximately bits per second.
$$
t_{bit} = \frac{1}{baud}
$$
For an 8N1 frame:
$$
bits_{frame} = 1 + 8 + 1 = 10
$$
$$
bytes/s \approx \frac{baud}{10}
$$
| Baud rate | Bit time | 8N1 frame time | Approximate payload |
|---|---|---|---|
| 9,600 | 104.17 us | 1.04 ms | 960 bytes/s |
| 57,600 | 17.36 us | 173.6 us | 5,760 bytes/s |
| 115,200 | 8.68 us | 86.8 us | 11,520 bytes/s |
| 1,000,000 | 1.00 us | 10.0 us | 100,000 bytes/s |
UART tolerates only limited clock error because the receiver samples a whole frame based on the start edge. A common design target is to keep total transmitter plus receiver baud error within a few percent, then verify on real hardware.
Wiring Rules
UART TX and RX are crossed:
- device A TX connects to device B RX;
- device B TX connects to device A RX;
- grounds are connected, unless the link uses isolation;
- never connect two push-pull TX outputs together.
For short board-level links, a direct connection is often enough when voltage levels match. For cables, noisy machines, long distance, or multi-drop networks, use a physical layer designed for that environment, such as RS-232, RS-485, CAN, USB, or Ethernet.
Voltage Levels And RS-232
UART describes framing. It does not guarantee that two connectors use compatible voltages.
| Interface style | Typical logic levels | Notes |
|---|---|---|
| 3.3 V CMOS UART | LOW near 0 V, HIGH near 3.3 V | common on modern MCUs |
| 5 V TTL UART | LOW near 0 V, HIGH near 5 V | common on older boards |
| RS-232 | negative voltage for logic 1, positive voltage for logic 0 | inverted and higher voltage |
Never drive a non-5-V-tolerant 3.3 V input from a 5 V TX pin. Use a level shifter or at least a checked resistor divider for the receive direction. RS-232 needs a transceiver such as MAX3232; it is not directly compatible with MCU pins.
Firmware Flow
UART hardware shifts bits automatically. Firmware usually interacts with data and status registers, FIFOs, interrupts, or DMA.
Minimal blocking pseudocode:
void uart_putc(uint8_t b) {
while (!uart_tx_ready()) {
;
}
UART_TX_REG = b;
}
bool uart_getc(uint8_t *b) {
if (!uart_rx_ready()) {
return false;
}
*b = UART_RX_REG;
return true;
}
Production firmware usually uses interrupts or DMA so slow serial I/O does not block time-critical control loops.
Error Flags
Common UART hardware status flags:
| Flag | Meaning | Typical cause |
|---|---|---|
| Framing error | stop bit was not sampled HIGH | wrong baud, wrong polarity, noise, line break |
| Parity error | parity bit does not match | wrong parity setting or corrupted bit |
| Overrun | new byte arrived before old byte was read | firmware too slow or interrupts disabled |
| Break detect | line stayed LOW longer than a frame | intentional break, short to ground, stuck transmitter |
Always read the microcontroller reference manual because flag-clearing sequences differ. Some MCUs clear errors only after reading status and then data in a required order.
Worked Example
A GPS module sends NMEA sentences at 9,600 baud, 8N1. One frame is 10 bits.
$$
t_{frame} = \frac{10}{9600} = 1.0417 ms
$$
If a sentence is 80 characters, transmit time is approximately:
$$
t_{sentence} = 80 \times 1.0417 ms = 83.3 ms
$$
At one sentence per second this is easy. At many sentences per second, or if firmware blocks interrupts for long periods, the receive buffer can overflow.
Practical Debug Checklist
- Confirm both devices share a voltage reference or use isolation.
- Confirm voltage level compatibility with a meter or oscilloscope.
- Cross TX to RX, not TX to TX.
- Match baud rate, data bits, parity, stop bits, and polarity.
- Verify idle is HIGH for logic-level UART.
- Send a known byte such as
0x55, which produces alternating bits. - Check framing and overrun flags before blaming the parser.
- Reduce baud rate to see whether the problem is timing or signal integrity.
Common Mistakes
- Using an RS-232 adapter directly on MCU UART pins.
- Forgetting GND on a two-board link.
- Assuming a 5 V TX pin is safe for a 3.3 V RX pin.
- Matching baud rate but not parity or stop bits.
- Reading received data too slowly and losing bytes to overrun.
- Printing from inside a high-rate UART interrupt until the system stalls.
- Connecting two transmitters to the same line without a proper bus transceiver.
Summary
UART sends framed bytes over separate TX and RX lines without a shared clock. The receiver locks onto each start bit, samples data bits at the configured baud rate, and checks the stop bit. Reliable UART links require correct wiring, compatible voltage levels, matching frame settings, and firmware that services receive data before buffers overflow.
Further Reading
- Microchip USART peripheral chapters in AVR and PIC data sheets.
- STMicroelectronics STM32 USART reference manual chapters.
- Texas Instruments application notes on logic-level translation.
- Analog Devices and Maxim Integrated RS-232 transceiver data sheets.