UART Explained: Frame Format, Baud Rate, Parity and Timing Errors

UART, the universal asynchronous receiver-transmitter, is the simplest serial link you will use: one wire to send (TX), one to receive (RX), and a shared ground. It connects microcontrollers to GPS modules, GSM modems, Bluetooth modules like the HC-05, and to your PC through a USB-to-serial converter. There is no clock wire, so both ends must agree on the speed and frame format in advance.

This article takes apart a UART frame bit by bit, shows how to compute bit time and real throughput, explains parity, and calculates the baud-rate error that causes garbled characters.

The UART Frame

When idle, the TX line sits at logic 1 (high). Each character is sent as a frame:

  1. Start bit: one bit of logic 0. The falling edge from idle tells the receiver a frame has begun.
  2. Data bits: usually 8, but 5 to 9 are possible, sent least significant bit first.
  3. Parity bit (optional): even or odd parity for simple error detection.
  4. Stop bit(s): one or two bits of logic 1, returning the line to idle and giving the receiver time before the next start bit.

The format is written as data bits, parity letter and stop bits. 8N1 means 8 data bits, no parity, 1 stop bit, and is by far the most common. 8E1 means 8 data bits, even parity, 1 stop bit.

Example: Sending 'A' (0x41) in 8N1

0x41 = 0100 0001 (binary, MSB first)
Sent LSB first: 1 0 0 0 0 0 1 0

Idle  Start  D0 D1 D2 D3 D4 D5 D6 D7  Stop  Idle
 1      0     1  0  0  0  0  0  1  0    1     1
----+     +--+                 +--+     +--------
    |     |  |                 |  |     |
    +-----+  +-----------------+  +-----+

Total: 10 bit periods per character

Baud Rate, Bit Time and Throughput

Bit time  Tb = 1 / baud
Frame bits = 1 start + data + parity (0 or 1) + stop
Characters per second = baud / frame bits

9600 baud:    Tb = 104.17 us
115200 baud:  Tb = 8.68 us

8N1 at 115200: 115200 / 10 = 11,520 bytes/s
8E1 at 9600:   9600 / 11  = 872.7 bytes/s

With 8N1 only 80 percent of the line time carries data. This overhead is the price of not having a clock wire: every frame resynchronises the receiver on its own start bit.

Baud rateBit time8N1 bytes/sTypical use
9600104.2 us960GPS modules (many default), HC-05 data mode
3840026.0 us3840HC-05 AT command mode
1152008.68 us11520Debug console, ESP8266 / ESP32 boot logs
10000001.0 us100000Fast MCU-to-MCU links over short distances

Parity

With even parity, the parity bit is chosen so the total number of 1s in data plus parity is even. With odd parity, the total is odd.

'A' = 0x41 = 0100 0001  -> two 1s
Even parity bit = 0   (total stays 2, even)
Odd parity bit  = 1   (total becomes 3, odd)

'C' = 0x43 = 0100 0011  -> three 1s
Even parity bit = 1
Odd parity bit  = 0

Parity detects any single-bit error, and any odd number of bit errors, but misses two flipped bits. When the receiver sees a parity mismatch it sets a parity error flag; it is up to your software to discard the byte or request a resend. Modbus RTU, for instance, specifies even parity by default.

Baud-Rate Error: Why Characters Get Garbled

The receiver samples each bit near its centre, timed from the start-bit edge. If the two ends run at slightly different speeds, the sampling point drifts through the frame. By the stop bit, after about 9.5 bit times, the accumulated drift must stay well under half a bit. In practice designers aim for a combined error of no more than about 2 percent, though the exact tolerance depends on the receiver's sampling method.

Microcontrollers derive the baud rate by dividing their clock. On an ATmega328P (Arduino Uno) in normal speed mode:

UBRR = f_osc / (16 x baud) - 1     (rounded to an integer)
Actual baud = f_osc / (16 x (UBRR + 1))
Error = (actual / desired - 1) x 100 %

f_osc = 16 MHz, baud = 9600:
UBRR = 16e6 / 153600 - 1 = 103.17 -> 103
Actual = 16e6 / (16 x 104) = 9615   -> error +0.2 %  (fine)

f_osc = 16 MHz, baud = 115200:
UBRR = 16e6 / 1843200 - 1 = 7.68 -> 8
Actual = 16e6 / (16 x 9) = 111111   -> error -3.5 %  (risky)

With double-speed mode (U2X = 1, divide by 8):
UBRR = 16e6 / (8 x 115200) - 1 = 16.36 -> 16
Actual = 16e6 / (8 x 17) = 117647   -> error +2.1 %

The Arduino core enables double-speed mode automatically for 115200 on a 16 MHz board, which is why it mostly works. Crystals such as 11.0592 MHz or 14.7456 MHz exist precisely because they divide evenly into standard baud rates, giving 0 percent error.

Voltage Levels and Wiring

StandardLogic 1Logic 0Notes
TTL / CMOS UART (5 V MCU)Near 5 VNear 0 VArduino Uno pins 0 and 1
3.3 V UARTNear 3.3 VNear 0 VESP32, STM32, Raspberry Pi; Pi GPIO is not 5 V tolerant
RS-232-3 V to -15 V+3 V to +15 VInverted logic; needs a MAX232-type level shifter

Always cross the lines: TX of one device to RX of the other, and connect grounds. A short sketch to try 8E1 on an Arduino:

Arduino sketch sending a counter with 8 data bits, even parity, 1 stop bit
// Open the Serial Monitor at 9600 baud. Most USB-serial adapters
// and terminal programs must also be set to 8E1 to read it cleanly.
unsigned int counter = 0;

void setup() {
  Serial.begin(9600, SERIAL_8E1);  // 8 data bits, even parity, 1 stop bit
}

void loop() {
  Serial.print("Count: ");
  Serial.println(counter++);
  delay(500);
}

Troubleshooting

  • Garbage characters: baud mismatch or wrong frame format. Check both ends, and check clock accuracy if using an internal RC oscillator.
  • Nothing received: TX and RX not crossed, missing common ground, or the module is still in a different mode.
  • Works at 9600, fails at 115200: baud error from clock division; use U2X or a better crystal.
  • Damaged pin: RS-232 levels connected directly to a microcontroller pin.
  • Occasional wrong bytes over long wires: noise and ground shifts; consider RS-485 for distances beyond a few metres.

Summary

A UART frame is a low start bit, 5 to 9 data bits sent LSB first, an optional parity bit and one or two high stop bits. At 8N1, ten bit times carry one byte, so 115200 baud gives 11,520 bytes per second. Parity catches single-bit errors. Keep the combined baud error to about 2 percent, check the divider maths for your clock, and use the right voltage levels for the devices you connect.