Peripherals
intermediate
Weight: 4/10

UART

Master UART (Universal Asynchronous Receiver-Transmitter) communication including baud rate configuration, framing, DMA, flow control, and common pitfalls in embedded systems.

peripherals
uart
serial
communication
dma
flow-control

Quick Cap

UART (Universal Asynchronous Receiver-Transmitter) is an asynchronous serial communication peripheral that provides two-wire (TX/RX) communication between devices using configurable baud rates, frame formats, and flow control. It is the most ubiquitous serial interface in embedded systems, used for debug consoles, GPS modules, Bluetooth radios, sensor bridges, and inter-processor communication.

Interviewers probe UART to test whether you understand asynchronous timing, frame structure, error detection mechanisms, and the difference between the UART peripheral and the electrical standards (RS-232, RS-485) that carry its signals.

Key Facts:

  • Asynchronous communication: No shared clock signal — uses start/stop bits for synchronization
  • Configurable framing: Start bit, data bits (5-8), optional parity bit, stop bits (1-2)
  • Baud rate selection: Data transmission rate from 300 bps to several Mbps
  • Flow control: Hardware (RTS/CTS) or software (XON/XOFF) flow control mechanisms
  • DMA integration: Hardware-assisted data transfer for improved performance
  • Error detection: Parity, framing, overrun, and timeout error detection

Deep Dive

At a Glance

FeatureDetail
Wires2 minimum (TX, RX); 4 with flow control (+ RTS, CTS)
ClockAsynchronous — no shared clock; both sides agree on baud rate
DuplexFull-duplex (TX and RX are independent lines)
TopologyPoint-to-point (one transmitter, one receiver per line)
Typical Speeds9600 bps - 921600 bps (up to several Mbps on some MCUs)
Data FormatFramed: start bit + data + optional parity + stop bit(s)
Error DetectionParity bit, framing error, overrun detection (hardware flags)
AddressingNone — point-to-point only (multi-drop requires RS-485 or a protocol layer)

How UART Works

UART communication is asynchronous — there is no shared clock wire between transmitter and receiver. Instead, both sides must independently agree on the same baud rate (bit timing) before communication begins. This is fundamentally different from synchronous protocols like SPI and I2C, where a master generates a clock that the slave follows. In UART, both sides run their own clocks and trust that they are close enough in frequency to stay synchronized for the duration of one frame.

Each byte is wrapped in a frame that provides synchronization:

  1. The line sits idle at logic high (called the mark state)
  2. The transmitter pulls the line low for one bit period — this is the start bit
  3. Data bits are clocked out LSB-first (most common) at the agreed baud rate
  4. An optional parity bit follows the data for basic error detection
  5. The line returns to high for 1 or 2 bit periods — the stop bit(s)
  6. The receiver uses the falling edge of the start bit to synchronize its internal sampling clock, then samples each subsequent bit at the center of the bit period

The receiver typically oversamples the line at 16x the baud rate. When it detects the falling edge of the start bit, it counts 8 oversampling ticks to reach the center of the start bit, verifies it is still low (to reject noise glitches), then samples each subsequent bit at 16-tick intervals. This oversampling provides noise immunity and allows the receiver to tolerate small differences in baud rate between the two sides.

Because TX and RX are separate wires, data can flow in both directions simultaneously (full-duplex). Unlike SPI, there is no master/slave relationship — both sides can transmit independently at any time.

Frame Structure

A UART frame contains the following fields, transmitted in order from left to right:

FieldBitsValuePurpose
Idle-Logic 1 (high)Line state between frames; can be any duration
Start bit1Always 0 (low)Signals frame start; receiver synchronizes on this falling edge
Data bits5-8 (usually 8)PayloadThe actual data, transmitted LSB first
Parity bit0 or 1ComputedEven or odd parity for single-bit error detection
Stop bit(s)1 or 2Always 1 (high)Guarantees line returns to idle; provides timing margin

The most common configuration is 8N1 — 8 data bits, no parity, 1 stop bit — which gives 10 total bits per byte on the wire (1 start + 8 data + 1 stop). This means the effective data rate is 80% of the raw bit rate. At 115200 baud, the throughput is approximately 11,520 bytes per second.

Other common configurations:

NotationData BitsParityStop BitsTotal Bits/ByteTypical Use
8N18None110Most common default
8E18Even111Modbus RTU, industrial protocols
8O18Odd111Some legacy systems
8N28None212Extra timing margin for slow receivers
7E17Even110ASCII-only protocols, legacy terminals

Parity explained: Even parity sets the parity bit so the total number of 1s (data + parity) is even. Odd parity makes it odd. Parity catches single-bit errors but cannot correct them and misses even numbers of bit errors (e.g., two bits flipped). For stronger error detection, use a CRC or checksum at the application protocol layer. Many modern protocols (including the common 8N1 default) skip parity entirely and rely on higher-layer checksums instead.

UART Frame Diagram

UART Frame — 8N1 (most common)
IdleHIGH
Start1 bit
D0LSB
D1
D2
D3
D4
D5
D6
D7MSB
Stop1 bit
IdleHIGH
UART Frame — 8E1 (with even parity)
IdleHIGH
Start1 bit
D0-D78 bits
Parity1 bit
Stop1 bit
IdleHIGH
  • Idle (gray) — Line held HIGH (mark state) between frames.
  • Start (red) — Always LOW. Receiver synchronizes on this falling edge.
  • Data (blue) — 8 bits, transmitted LSB first. No clock — timed by baud rate.
  • Parity (orange) — Optional even/odd parity for single-bit error detection.
  • Stop (green) — Always HIGH. Returns line to idle, provides timing margin.

UART vs RS-232 vs RS-485

This is a frequent interview question. UART is a digital peripheral inside the MCU. RS-232 and RS-485 are electrical signaling standards that define voltage levels and wiring for carrying UART data over cables. They operate at different layers:

FeatureUART (TTL)RS-232RS-485
What it isMCU peripheral (logic-level signals)Voltage standard for serial linksDifferential voltage standard
Logic levels0V / 3.3V or 5V+3 to +15V (space/0) / -3 to -15V (mark/1)Differential pair, +/- 1.5V to 6V
Max distancePCB-level (inches to a few feet)~15 m (50 ft)~1200 m (4000 ft)
TopologyPoint-to-pointPoint-to-pointMulti-drop bus (up to 32+ nodes)
Common connectorHeader pins, no standardDB-9 / DB-25Screw terminals (A/B differential pair)
Noise immunityLow (single-ended, short distance)Moderate (wider voltage swing)High (differential signaling rejects common-mode noise)
Half/Full duplexFull-duplex (separate TX/RX)Full-duplex (separate TX/RX wires)Half-duplex (shared differential pair) or full-duplex (2 pairs)
Typical useMCU-to-MCU on same PCB, debug headerPC serial ports, legacy lab equipmentIndustrial networks, Modbus RTU, building automation

Key points for interviews:

  • To connect a 3.3V UART MCU to an RS-232 device, you need a level shifter IC (e.g., MAX232, MAX3232). RS-232 uses inverted logic and +/- 12V swings that will damage an unprotected MCU pin.
  • To connect to an RS-485 bus, you need a differential transceiver IC (e.g., MAX485, SN75176). The transceiver converts between single-ended UART and the differential A/B pair. On a half-duplex RS-485 bus, firmware must control the transceiver's direction enable (DE) pin — switching from receive to transmit mode before sending data and back to receive mode afterward.
  • The UART peripheral configuration (baud rate, framing) is the same regardless of which electrical standard carries the signal. Only the external interface circuitry changes.
⚠️Common Trap: TTL-to-RS-232 Voltage Mismatch

Connecting MCU UART pins (3.3V) directly to RS-232 signals (+/- 12V) will damage the MCU. Always use a level-shifting IC. This is a common bench mistake that interviewers specifically ask about.

Baud Rate

The baud rate is the number of bits transmitted per second. Both sides must use the same baud rate — there is no clock wire to enforce synchronization, so a mismatch produces garbage data. This is the single most common cause of "I see random characters on my terminal" problems.

Common baud rates:

Baud RateBit PeriodByte Time (8N1)ThroughputTypical Use
9600104.2 us1.04 ms~960 B/sGPS modules, low-speed sensors, Modbus
1920052.1 us521 us~1920 B/sMIDI (31250 is also common)
3840026.0 us260 us~3840 B/sMedium-speed peripherals
1152008.68 us86.8 us~11.5 KB/sDebug consoles, general purpose
4608002.17 us21.7 us~46 KB/sHigh-speed data logging
9216001.09 us10.9 us~92 KB/sFast firmware downloads

How baud rate is generated: The MCU divides its peripheral clock by a prescaler to produce the bit-rate clock. The baud rate register value is typically calculated as:

BRR = F_CLK / (OVERSAMPLING * BAUD_RATE)

Because this division is integer (or fixed-point with limited fractional bits), the actual baud rate may not exactly match the target. For example, with a 72 MHz clock and 16x oversampling, requesting 115200 baud gives 72000000 / (16 * 115200) = 39.0625. Rounding to 39 gives an actual baud rate of 115384 bps — an error of 0.16%, which is well within tolerance.

The 3% tolerance rule: The accumulated timing error between transmitter and receiver must stay within approximately 3-5% of a bit period over the length of one frame (10-12 bits). Beyond this threshold, the receiver's sampling point drifts into the adjacent bit, causing framing errors. Practical implications:

  • Each side should individually be within ~1.5% of the target baud rate
  • Cheap internal RC oscillators (5-10% tolerance) often cannot sustain reliable UART, especially at high baud rates — use a crystal or ceramic resonator
  • Higher baud rates are more sensitive to clock error because the absolute timing margin (in microseconds) shrinks proportionally
  • Temperature drift matters: an RC oscillator that works at 25C on the bench may drift out of tolerance at 85C in the field
  • Some MCUs support 8x oversampling (instead of 16x) to achieve higher baud rates from a given clock, but this halves the noise immunity margin
⚠️Common Trap: Baud Rate Mismatch

If you see seemingly random garbage on a UART terminal, the most common cause is a baud rate mismatch. Verify both sides are configured identically and that the MCU's clock source is accurate enough for the chosen baud rate.

Common Errors

UART hardware flags these errors automatically via status register bits. Understanding them is critical for debugging and is a frequent interview topic:

ErrorWhat HappenedCommon CauseHow to Handle
Framing ErrorStop bit was not logic-high when expectedBaud rate mismatch, electrical noise, wrong data format (e.g., 7-bit vs 8-bit), or TX/RX lines swappedVerify baud rate and frame settings on both sides; check wiring
Parity ErrorComputed parity does not match received parity bitBit corruption from electrical noise or EMIFlag and discard the byte; request retransmission at the protocol layer
Overrun ErrorNew byte arrived before the previous byte was read from the data registerCPU too slow to service the UART, or no interrupt/DMA configuredUse interrupt-driven or DMA reception with a ring buffer
Break ConditionRX line held low for longer than one complete frame periodDeliberate break signal from the remote side, or cable disconnected/cutDetect and handle as a special event (e.g., reset protocol state machine)
Noise ErrorOversampling detected inconsistent bit values during a single bit periodElectrical noise, poor signal integrity, or marginal baud rate matchCheck wiring, grounding, and signal quality; reduce baud rate if needed

Overrun is the most commonly encountered error in practice and the most dangerous because it is silent — you lose data without any obvious symptom unless you explicitly check the overrun error flag. Every byte that arrives while the previous byte sits unread in the data register is lost. This is why interrupt-driven reception with a ring buffer is the standard approach for any non-trivial UART usage.

Framing errors are the second most common and are almost always caused by a baud rate mismatch or by the receiver starting mid-frame (e.g., after a reset while the other side is transmitting). If you see persistent framing errors, the first thing to check is that baud rate, data bits, parity, and stop bits match on both sides.

Flow Control

Flow control prevents a fast transmitter from overwhelming a slow receiver. Without it, if the receiver cannot process incoming bytes fast enough, data is lost to overrun errors. There are two approaches:

FeatureHardware (RTS/CTS)Software (XON/XOFF)
SignalsRTS (output from receiver) and CTS (input to transmitter) — 2 extra GPIO linesXON (0x11) and XOFF (0x13) characters sent in-band on the existing TX/RX lines
Response timeNear-instantaneous (hardware-level, ~1 bit period)Delayed by software processing time (potentially multiple byte times)
Extra wiring2 additional wiresNone
Bandwidth costNone (out-of-band signaling)Slight — XON/XOFF bytes consume bandwidth
Binary data safe?Yes — does not interfere with data contentNo — 0x11 and 0x13 cannot appear in the payload without escaping
Typical useHigh-speed links, Bluetooth/Wi-Fi modules, any binary protocolLegacy terminals, text-based protocols, simple ASCII streams
How it worksReceiver deasserts RTS when its buffer is nearly full; transmitter pauses when it sees CTS deassertedReceiver sends XOFF character to pause remote transmitter; sends XON character to resume

Hardware flow control (RTS/CTS) is preferred for any high-speed or binary-data link. Many Bluetooth modules (e.g., HC-05, RN42) and Wi-Fi modules (e.g., ESP32) require or strongly recommend hardware flow control. In MCUs, RTS and CTS are often handled automatically by the UART peripheral hardware — once enabled, the peripheral will pause transmission when CTS is deasserted without any software intervention.

Software flow control (XON/XOFF) is simpler to wire (no extra pins) but has significant limitations. The receiver must parse every incoming byte to detect flow control characters, and there is inherent latency — by the time the transmitter receives and processes an XOFF, it may have already sent several more bytes. Software flow control is only safe for ASCII/text streams where the XON and XOFF byte values (0x11, 0x13) do not appear in the payload.

Data Transfer Methods

MethodCPU OverheadThroughputLatencyBest For
PollingHigh (CPU busy-waits on status flags)LowLowest (immediate check)Simple single-byte debug prints, bare-metal prototyping
InterruptMedium (ISR fires per byte, ~1-5 us each)MediumLow (ISR responds within interrupt latency)General-purpose RX/TX with ring buffer — the standard approach
DMAVery low (hardware moves data to/from memory)HighHigher (processes data in chunks)Bulk transfers, continuous data logging, firmware downloads

Polling is the simplest approach: the CPU reads the status register in a loop, waiting for the transmit-empty or receive-not-empty flag. This works for quick debug prints but wastes CPU cycles and is unsuitable for receiving data (the CPU must poll faster than bytes arrive, or data is lost).

Interrupt-driven with a ring buffer is the standard production approach. The UART receive interrupt fires each time a byte arrives. The ISR reads the byte from the hardware data register and pushes it into a circular (ring) buffer. The main loop or an RTOS task reads from the buffer at its own pace. This decouples reception timing from processing timing and prevents overrun errors as long as the buffer is large enough to absorb bursts.

c
/* Conceptual ring buffer — shows the pattern, not a complete driver.
* In production, BUF_SIZE should be a power of 2 so the modulo
* operation compiles to a bitwise AND.
*/
#define BUF_SIZE 256
typedef struct {
uint8_t data[BUF_SIZE];
volatile uint16_t head; /* ISR writes here */
volatile uint16_t tail; /* Main loop reads here */
} ring_buf_t;
/* Called from UART RX interrupt handler */
void ring_buf_push(ring_buf_t *rb, uint8_t byte) {
uint16_t next = (rb->head + 1) % BUF_SIZE;
if (next != rb->tail) { /* buffer not full */
rb->data[rb->head] = byte;
rb->head = next;
}
/* If full, the byte is silently dropped.
* In a real system, assert flow control (RTS) or increment
* an overrun counter here.
*/
}
/* Called from main loop or RTOS task */
bool ring_buf_pop(ring_buf_t *rb, uint8_t *byte) {
if (rb->head == rb->tail) {
return false; /* buffer empty */
}
*byte = rb->data[rb->tail];
rb->tail = (rb->tail + 1) % BUF_SIZE;
return true;
}

Buffer sizing: The ring buffer must be large enough to hold all bytes that can arrive during the longest period the main loop (or task) might be busy before it processes the buffer. For example, at 115200 baud (8N1), bytes arrive every 86.8 us. If the main loop has a worst-case busy period of 10 ms, you need at least 10ms / 86.8us = 116 bytes of buffer. Round up to the next power of 2 (128 or 256) for efficient modulo arithmetic. In an RTOS, the task's worst-case blocking time determines the minimum buffer size.

DMA is the right choice for high-throughput streams (GPS NMEA sentences, sensor data logging, firmware download over UART). Configure DMA to write incoming bytes directly into a memory buffer without CPU involvement. Use the half-transfer and transfer-complete DMA interrupts — or the UART peripheral's idle-line detection interrupt — to process data in chunks rather than byte-by-byte. Idle-line detection is particularly useful for variable-length messages: the UART peripheral detects when the RX line has been idle for one frame period and triggers an interrupt, telling you that a burst of data has finished.

For DMA transmit, load the source buffer and transfer length into the DMA channel, then start the transfer. The DMA engine feeds bytes to the UART transmit register automatically. A transfer-complete interrupt signals when all bytes have been sent, allowing you to load the next buffer or free the memory.

Low-Power Considerations

UART has unique power implications because the peripheral must remain active to detect incoming data:

  • Idle current: Even when no data is being transmitted, the UART receiver consumes power to monitor the RX line for incoming start bits. In battery-powered systems, this always-on receiver is a significant power drain.
  • Wake-on-UART: Many MCUs support waking from low-power sleep modes when activity is detected on the UART RX pin. The UART peripheral (or a GPIO edge interrupt on the RX pin) detects the falling edge of a start bit and triggers a wake-up. However, the first byte is often lost because the MCU takes time to wake and configure clocks — the transmitter should send a preamble or wake-up byte before the actual data.
  • Baud rate and power: Lower baud rates keep the transmitter active longer per byte, consuming more energy per byte transferred. For battery-powered devices, it is often more power-efficient to use a higher baud rate (transmit quickly, then sleep) than a lower one.
  • Peripheral clock gating: Disable the UART peripheral clock when not in use to save power. Re-enable it before transmission and ensure the baud rate generator is stable before sending data.

Debugging Story: The Automotive Data Corruption

A team was debugging an automotive sensor node that received corrupted UART data intermittently. The system worked perfectly on the bench at room temperature but produced garbled readings in the vehicle — roughly 1 in 20 messages arrived with bad data. The SPI and I2C buses on the same board were unaffected, which ruled out general EMI problems.

After connecting an oscilloscope to the UART lines, they measured the actual bit timing and compared it to the expected 115200 baud period (8.68 us). The transmitter was accurate (crystal-based), but the receiver's bit period was 9.03 us — a 4% error. The receiver MCU was clocked from its internal RC oscillator. At 25C on the bench, the RC oscillator was within 1% of nominal. But at 85C inside the engine compartment, thermal drift pushed it to 4%, exceeding the 3% tolerance window.

The fix was straightforward: switch the receiver MCU's clock source from the internal RC oscillator to an external 8 MHz crystal. Total cost: one crystal and two capacitors. The real lesson was deeper.

Lesson: For any UART link that must work across temperature ranges, use a crystal or ceramic resonator as the clock source — not the internal RC oscillator. The baud rate tolerance margin is tighter than most engineers assume, and temperature-induced clock drift is the most common cause of intermittent UART failures in deployed products. Always calculate and verify the worst-case baud rate error across your full operating temperature range during design review, not after field failures.

Wiring and Cross-Connection

UART wiring is simple but has one rule that trips up beginners and occasionally experienced engineers:

TX connects to RX, and RX connects to TX. This is a cross-connection — the transmitter of one device connects to the receiver of the other, and vice versa. Additionally, both devices must share a common ground (GND).

ConnectionDevice ADevice B
Transmit dataTX (pin out)RX (pin in)
Receive dataRX (pin in)TX (pin out)
GroundGNDGND
Flow control (optional)RTS (out)CTS (in)
Flow control (optional)CTS (in)RTS (out)

Common wiring mistakes (and interview discussion points):

  • TX-to-TX connection: Both sides drive the line simultaneously — causes bus contention, possible damage, definitely no communication. This is the most common beginner mistake.
  • Missing ground: Without a shared ground reference, voltage levels are meaningless. The UART may appear to work intermittently or produce noise-corrupted data.
  • Voltage mismatch: Connecting a 5V UART TX directly to a 3.3V UART RX can damage the 3.3V device. Use a level shifter or voltage divider.
  • Swapped RTS/CTS: If hardware flow control is enabled but RTS and CTS are swapped, the link will hang immediately — the transmitter sees CTS deasserted and refuses to send.

Loopback testing: To verify that the UART peripheral and driver software are working, connect TX to RX on the same device (with flow control disabled). Any byte transmitted should be immediately received. This isolates the UART from the external wiring and remote device, making it the first step in debugging a non-working link.

Common UART-Based Protocols

UART is a transport layer — it moves bytes. Many higher-level protocols are built on top of UART and are commonly encountered in embedded systems:

ProtocolBaud RateFrame FormatDescription
Modbus RTU9600-1152008E1 (8 data, even parity, 1 stop)Industrial automation standard; uses device addresses and CRC-16 for multi-drop RS-485 communication
NMEA 01834800 or 9600 (default)8N1GPS sentence format; ASCII text lines starting with $ and ending with * + checksum
AT commands9600-1152008N1Text-based command/response protocol for modems, Bluetooth (HC-05), Wi-Fi (ESP8266), cellular modules
CLI / Debug console115200 (common)8N1Human-readable command line interface for debugging and configuration
SBUS1000008E2 (inverted)RC receiver protocol; 25-byte frames at a non-standard baud rate with inverted signal polarity
DMX5122500008N2Stage lighting control; uses RS-485 physical layer with break-based framing

Understanding these protocols helps in interviews because it shows you can connect UART knowledge to real applications. For example, knowing that Modbus RTU uses 8E1 framing (not the default 8N1) and that SBUS uses an inverted, non-standard baud rate demonstrates practical experience.

Debugging UART Issues — A Systematic Approach

UART debugging follows a logical elimination process. When communication fails, work through these steps in order:

Step 1: Verify physical connectivity

  • Are TX and RX cross-connected (TX-to-RX, not TX-to-TX)?
  • Is GND connected between both devices?
  • Are voltage levels compatible (3.3V vs 5V)?
  • Is a level shifter or transceiver present if needed (RS-232, RS-485)?

Step 2: Verify configuration match

  • Same baud rate on both sides?
  • Same data bits, parity, and stop bits (e.g., both 8N1)?
  • Is flow control configured identically (both enabled or both disabled)?

Step 3: Use loopback to isolate

  • Connect TX to RX on the suspect device (disconnect the other side)
  • Transmit known data and verify it is received correctly
  • If loopback fails, the problem is in the local UART configuration or driver
  • If loopback works, the problem is in the wiring or the remote device

Step 4: Use an oscilloscope or logic analyzer

  • Measure the actual bit period and compare to the expected value — this catches baud rate errors caused by clock inaccuracy
  • Verify the start bit, data bits, and stop bit are present and correctly timed
  • Check voltage levels (especially if crossing voltage domains)
  • Look for noise, ringing, or incomplete transitions

Step 5: Check error flags

  • Read the UART status register after each failed reception
  • Framing errors point to baud rate or format mismatch
  • Overrun errors point to software not reading data fast enough
  • Parity errors point to electrical noise
⚠️Common Trap: UART Buffer Overflow

If you receive the first few bytes correctly but then data becomes corrupted or missing, the most likely cause is an overrun error — the software is not reading received bytes fast enough. Switch from polling to interrupt-driven reception with a ring buffer.

UART in Embedded Linux

In embedded Linux, UART ports appear as tty device files (e.g., /dev/ttyS0, /dev/ttyAMA0, /dev/ttyUSB0). The kernel's serial subsystem handles the low-level UART peripheral configuration, and user-space applications interact with the port through the standard POSIX termios API.

Key concepts for interviews:

ConceptDetail
Device node/dev/ttySn for built-in UARTs, /dev/ttyUSBn for USB-to-serial adapters, /dev/ttyAMAn for ARM AMBA UARTs
termios APItcgetattr() / tcsetattr() to configure baud rate, framing, flow control, and input/output processing
Canonical vs raw modeCanonical mode buffers input until a newline (for interactive terminals); raw mode delivers bytes as they arrive (for binary protocols)
Console vs application portThe kernel may use one UART as the boot console (console=ttyS0,115200 in kernel command line); application UARTs need separate configuration
DTS (Device Tree)UART pin assignments, clock sources, and default configuration are defined in the device tree; the driver reads these at probe time

Common interview question: "How do you configure a UART port for a binary protocol in Linux?" The key answer points are: open the device file, use tcgetattr() to read current settings, set raw mode with cfmakeraw(), set baud rate with cfsetspeed(), configure 8N1, disable flow control, set VMIN/VTIME for read behavior, then apply with tcsetattr(). This is the standard pattern for GPS parsers, Modbus masters, and sensor bridges running on Linux SBCs.

Comparison: UART vs SPI vs I2C

CriteriaUARTSPII2C
ClockAsynchronous (no clock wire)Synchronous (master generates clock)Synchronous (master generates clock)
Wires2 (TX/RX)4+ (SCK/MOSI/MISO/CS)2 (SDA/SCL)
SpeedUp to ~1 Mbps typical1 - 50+ MHz100 kHz - 3.4 MHz
DuplexFull-duplexFull-duplexHalf-duplex
TopologyPoint-to-pointSingle master, multiple slaves (via CS)Multi-master, multi-slave (via addresses)
AddressingNone (point-to-point only)CS lines select the slave7-bit or 10-bit device addresses
Error detectionParity bit (hardware)None built-inACK/NACK per byte
Flow controlRTS/CTS or XON/XOFFNone (master controls clock)Clock stretching (slave holds SCL low)
Typical useDebug console, GPS, Bluetooth, sensor bridgesFlash, ADC/DAC, displays, RF transceiversSensors, EEPROM, RTC, GPIO expanders

When to choose UART: You need a simple two-wire link between two devices that can transmit independently (no master/slave), or the peripheral only offers a UART interface (GPS, Bluetooth, many wireless modules). UART is also the standard choice for human-readable debug consoles. UART excels where the link is point-to-point, moderate speed, and does not need to be shared — exactly the scenario for a debug console, a GPS receiver, or a Bluetooth module.

When to choose SPI or I2C instead: When you need to connect multiple peripherals (SPI/I2C scale better), when you need higher speed (SPI can run at 10-50 MHz vs UART's typical 1 Mbps), or when you are extremely pin-constrained (I2C uses only 2 wires for any number of devices). If you need multi-drop communication over long cables, UART over RS-485 with a protocol like Modbus is the standard approach, but it requires half-duplex management and transceiver direction control.

Multi-UART systems: Most MCUs provide 2-6 UART peripherals. When a design needs more UART ports than the MCU has, options include:

  • Using a UART multiplexer IC
  • Using USB-to-UART bridge ICs (e.g., FTDI FT2232 provides 2 UARTs over a single USB port)
  • Using a software UART (bit-banging) on GPIO pins — adequate for low baud rates but consumes significant CPU time
  • Multiplexing at the protocol level (time-division or addressing) on a single physical UART with RS-485

What interviewers want to hear: You understand the UART frame structure and why asynchronous communication requires tight baud rate matching. You can explain the difference between the UART peripheral and the electrical standards (RS-232/RS-485). You know when to use flow control and which type. You can reason about polling vs interrupt vs DMA trade-offs for different throughput requirements. You understand that overrun is the most dangerous UART error because it is silent.

Interview Focus

Classic UART Interview Questions

Q1: "How do you configure UART for different baud rates, and what factors affect baud rate accuracy?"

Model Answer Starter: "I configure UART baud rates by calculating the baud rate divider value based on the UART clock frequency and desired baud rate. The formula is Baud_Rate_Divider = UART_Clock / (16 * Baud_Rate) for most UART implementations. Key factors affecting accuracy include the UART clock frequency stability, crystal oscillator accuracy, temperature effects, and the granularity of the baud rate divider. For high accuracy, I use precise crystal oscillators with low temperature coefficients, ensure the UART clock is derived from a stable source, and verify that the baud rate error is within acceptable limits (typically less than 3% for reliable communication). I also consider using oversampling techniques and error correction methods for critical applications."

Q2: "What's the difference between hardware and software flow control, and when would you use each?"

Model Answer Starter: "Hardware flow control uses dedicated RTS (Request to Send) and CTS (Clear to Send) signals to control data flow, providing immediate response and working at the hardware level. Software flow control uses XON/XOFF characters embedded in the data stream, requiring software interpretation and adding overhead. I use hardware flow control for high-speed communication, real-time systems, and when immediate flow control is needed. I use software flow control when hardware lines are not available, for simple applications, or when compatibility with legacy systems is required. Hardware flow control is more reliable and doesn't consume bandwidth, while software flow control is simpler to implement but can interfere with binary data transmission."

Q3: "How do you handle UART errors and data corruption, and what are the common error types?"

Model Answer Starter: "Common UART errors include parity errors (data corruption detected by parity bit), framing errors (incorrect start/stop bit detection), overrun errors (new data received before previous data was read), and timeout errors (no data received within expected time). I handle errors by implementing comprehensive error detection in interrupt handlers, using circular buffers to prevent data loss, implementing timeout mechanisms for reliable communication, and using checksums or CRC for data integrity verification. I also implement automatic retry mechanisms, proper buffer management to prevent overruns, and error logging for debugging. For critical applications, I use redundant communication paths or error correction codes."

Q4: "What causes UART communication failures, and how do you debug them?"

Model Answer Starter: "UART failures are typically caused by baud rate mismatches, incorrect frame format configuration, electrical issues like poor signal integrity or incorrect voltage levels, timing problems from clock inaccuracies, or buffer overruns from insufficient processing speed. I debug by using oscilloscopes to verify signal timing and voltage levels, checking baud rate calculations and clock accuracy, verifying frame format settings match between transmitter and receiver, implementing comprehensive error detection and logging, and using loopback tests to isolate hardware vs software issues. I also check for proper grounding, signal termination, and EMI interference that can cause communication failures."

Q5: "How do you implement UART with DMA, and what are the performance benefits?"

Model Answer Starter: "I implement UART with DMA by configuring DMA channels for both transmit and receive operations, setting up circular buffers for continuous data transfer, and using DMA interrupts for buffer management. DMA benefits include reduced CPU overhead by handling data transfer in hardware, improved system performance by allowing the CPU to perform other tasks, better real-time response by eliminating polling overhead, and more efficient buffer management for high-speed data streams. I configure DMA with appropriate transfer sizes, use double buffering for seamless data transfer, implement proper interrupt handling for buffer switching, and ensure proper synchronization between DMA and UART operations. This is especially beneficial for applications requiring continuous data logging or high-throughput communication."

Trap Alerts

  • Don't say: "UART is just serial communication" — it has specific timing and framing requirements
  • Don't forget: Baud rate accuracy requirements and clock stability considerations
  • Don't ignore: Error handling and buffer management for reliable communication

Follow-up Questions

  • "How would you implement UART communication in a system with multiple UART interfaces?"
  • "What's your strategy for UART communication recovery after errors or timeouts?"
  • "How do you handle UART communication in a system with mixed data types (binary and text)?"
💡Practice UART Interview Questions

Ready to test yourself? Head over to the UART Interview Questions page for a full set of Q&A with collapsible answers — perfect for self-study and mock interview practice.

Practice

What is the minimum number of signals required for UART communication?

What is the purpose of the start bit in UART communication?

What is the main advantage of DMA with UART communication?

What typically causes a UART framing error?

What is the key difference between UART and RS-232?

Real-World Tie-In

Wireless Sensor Network Gateway

In the field, I worked on a gateway communicating with multiple sensor nodes via UART at different baud rates. I used DMA with idle-line detection for efficient reception, hardware flow control on the high-speed links, and ring buffers sized to absorb burst traffic. Proper crystal-based clocking and isolated RS-485 transceivers for the long cable runs gave us 99.9% data integrity in an electrically noisy industrial environment.

Industrial Process Monitoring

On the job, we built a monitoring system that needed a UART debug console, a sensor bridge at 115200 baud, and a Modbus RS-485 link at 9600 baud — all running simultaneously. The key challenges were managing three UART peripherals with different baud rates and frame formats, preventing overrun on the sensor stream by using DMA, and ensuring the RS-485 transceiver direction control (DE pin) toggled at the right time to avoid bus contention.