A UART (Universal Asynchronous Receiver/Transmitter) is a hardware peripheral that serializes and deserializes data over two wires (TX and RX) without a shared clock signal, using a pre-agreed baud rate and frame format to synchronize sender and receiver. It is one of the most common communication interfaces found on microcontrollers, used for everything from debug consoles to sensor communication and inter-processor links.
In practice
UARTs transmit data in frames: a start bit, a configurable number of data bits (typically 8), an optional parity bit, and one or more stop bits. Both sides must be configured to the same baud rate, word length, parity, and stop-bit count before communication is possible. Common baud rates range from 9600 to 115200 bps for debug and low-speed sensor links; some UARTs support 1 Mbps or higher, though signal integrity and cable length impose practical limits. Because there is no clock line, each receiver re-synchronizes on every start-bit falling edge, which makes the format simple but sensitive to baud-rate mismatch -- even a few percent error can cause framing errors under adverse conditions.
On most MCUs the UART peripheral feeds a small hardware FIFO or a single receive data register. If firmware does not read received bytes quickly enough, overrun errors occur and data can be lost; most UARTs raise an overrun flag or interrupt when this happens, though the exact behavior varies by device. Interrupt-driven or DMA-driven reception is the standard mitigation; polling is usually only acceptable for very low baud rates or when the CPU has nothing else to do. Many real-world designs layer a packet framing protocol on top of the raw byte stream -- because UART gives you a stream, not messages. The blog post "Help, My Serial Data Has Been Framed: How To Handle Packets When All You Have Are Streams" covers this problem in detail.
Bit-banging a UART in software is possible on any GPIO-equipped MCU, which is sometimes useful when hardware UART peripherals are all occupied or unavailable. Timing accuracy is critical: the bit period must be held consistently, which typically requires disabling interrupts during transmission or using a timer to schedule each bit. "Bit-Banged Async Serial Output And Disciplined Engineering" explores the tradeoffs of this approach. A worked hardware UART example on a specific MCU family is covered in "MSP430 LaunchPad Tutorial - Part 4 - UART Transmission."
UART lines are logic-level signals (commonly 3.3 V or 5 V CMOS/TTL on most MCUs, though exact voltage levels depend on the device and board design). They are not the same as RS-232, which uses +/-12 V signaling, or RS-485, which uses differential pairs. A level-shifter or line driver is required when interfacing to equipment using those physical layer standards. Confusing logic-level UART with RS-232 and connecting them directly is a common and occasionally damaging mistake for beginners.
Discussed on EmbeddedRelated
Frequently asked
What is the difference between a UART and a USART?
A
USART (Universal Synchronous/Asynchronous Receiver/Transmitter) adds a synchronous mode where a shared clock line is provided alongside data, which can be useful for certain point-to-point links where the sender controls the clock. In asynchronous mode a USART behaves similarly to a
UART, though feature sets and implementation details can differ beyond just the clock line. Many STM32, AVR, and other MCUs label their peripherals USART even when the synchronous mode is rarely used in practice. The blog post 'An Iterative Approach to USART HAL Design using ChatGPT' touches on HAL design for such peripherals.
How much baud-rate mismatch is tolerable?
A common rule of thumb is that the receiver can tolerate up to roughly 2-3% total
baud-rate error before framing errors become likely, though the exact margin depends on the
UART implementation, frame length, oversampling ratio, stop-bit configuration, and the receiver's sampling point. Errors compound across a full frame, so longer frames (9 data bits, no stop bit padding) are less tolerant than shorter ones. Always measure actual clock accuracy, especially on MCUs running from an internal RC oscillator, which may drift several percent from nominal.
What causes overrun errors and how do I prevent them?
An overrun error occurs when a new byte arrives in the receive register before firmware has read the previous one. The new byte overwrites the old, and data is lost. The fix is to read received bytes promptly using an RX
interrupt or
DMA transfer into a ring buffer, rather than polling. Increasing the
baud rate or receiving longer bursts of data makes overruns more likely if the CPU is busy with other work.
Is UART full-duplex?
Yes, a standard two-wire
UART (TX + RX) is full-duplex: both sides can transmit simultaneously because the lines are independent. Half-duplex operation over a single wire is also possible -- some MCUs support a single-wire mode where TX and RX share one pin -- but this requires software or hardware arbitration to avoid collisions.
Does byte order (endianness) matter for UART communication?
Within a single byte, UARTs transmit the least-significant bit first by convention in the vast majority of implementations. When multi-byte values (16-bit integers, floats, etc.) are sent across a
UART link, the
byte order depends entirely on the application protocol -- not the UART hardware. Both sides must agree on whether multi-byte values are sent
big-endian or
little-endian. The blog post 'Endianness and Serial Communication' covers this in detail.
Differentiators vs similar concepts
UART is often conflated with
RS-232 and
RS-485, but those are physical layer standards that define voltage levels and cable characteristics, not the framing protocol. A logic-level UART signal (3.3 V or 5 V) requires a level-shifter (e.g., MAX3232 for RS-232, MAX485 for RS-485) to drive those buses. UART is also distinct from
USART: a USART adds an optional synchronous clock line, and while the two behave similarly in async mode, implementations can differ in feature set beyond just the clock line.
SPI and
I2C are clocked (synchronous) serial protocols and are not UARTs despite also being serial interfaces.