Why UART Prints Garbage: Baud Rate, Voltage Levels, and Ground
Series navigation: Episode 1: The Roadmap · Episode 7: Reading Arduino Pin-Current Specifications · Episode 8
The problem: A terminal shows random symbols even though both devices say “UART.”
The goal: Understand what must match electrically and logically before bytes become readable text.
The result: A debugging order for TX/RX, ground, voltage, baud rate, frame format, buffering, and signal capture.

AI-generated illustration of a low-voltage UART bench. It is not a measurement record or exact wiring guide.
The terminal opened, but the text looked broken
UART is often the first communication interface I use on a board. It prints boot messages, accepts commands, connects two controllers, or talks to a GPS, modem, Bluetooth module, serial display, or USB-to-serial adapter.
Then the terminal shows this:
��H�l�
The program may be sending Hello, but UART does not transmit software strings. It places timed voltage levels on a wire. The receiver must agree about voltage, timing, direction, and frame format before those levels become the same byte.
What 9600 8N1 means
Asynchronous UART has no shared clock wire. Both ends agree on a baud rate and reconstruct a frame from its edges.
9600 8N1 means 9,600 symbols per second, eight data bits, no parity, and one stop bit. A typical character uses ten bit times: one start, eight data, and one stop. At 9,600 baud, one bit lasts about 104 ยตs and framing limits the theoretical rate to roughly 960 characters per second before protocol overhead.
The line normally idles HIGH. A LOW start bit begins the frame, data bits commonly arrive least-significant bit first, and the stop bit returns HIGH.
Wire direction before code
Device A TX → Device B RX
Device A RX ← Device B TX
Device A GND — Device B GND
TX crosses to RX. The shared ground gives both devices the reference used to decide HIGH and LOW. Never connect two actively driven TX outputs together.
Also, “serial” does not identify the electrical standard:
| Interface | Difference |
|---|---|
| 3.3 V or 5 V logic UART | Single-ended logic; voltage compatibility must be checked |
| RS-232 | Different, often bipolar levels; requires a transceiver |
| RS-485 | Differential bus; requires a transceiver and bus design |
| USB | Packet-based differential interface; a bridge converts it to UART |
RS-232 connected directly to GPIO can cause damage. A 5 V TX can exceed a 3.3 V input rating. Check both data sheets.
Debug from electricity upward
1. Identify both endpoints
Record the board or chip, UART instance, pin mapping, I/O voltage, idle polarity, baud rate, data bits, parity, stop bits, and flow control.
2. Check ground and idle voltage
TX should normally idle HIGH. Verify the ground path and voltage compatibility. If TX never changes, confirm that firmware selected the correct peripheral and pin multiplexer.
3. Send a known pattern
Repeated U characters (0x55) create alternating bits that are easy to recognize. Avoid debugging a complex binary protocol and the physical link simultaneously.
4. Match the complete frame
A 115200 terminal cannot decode a 9600 transmitter. Baud, character size, parity, and stop bits must all match.
5. Measure one bit
A logic analyzer decodes frames and flags errors. An oscilloscope reveals actual voltage, bit time, slow edges, ringing, and noise. If a 9,600-baud bit is far from about 104 ยตs, inspect the clock and baud configuration.
6. Check software buffering
Only after the electrical frame is valid, investigate receive-buffer overflow, blocking code, binary-versus-text interpretation, delimiters, and packet framing.
Common symptoms
| Symptom | Likely cause | First check |
|---|---|---|
| Nothing received | TX/RX direction, wrong pin, no ground, or no transmission | Probe the transmitting TX pin |
| Consistent garbage | Baud or frame mismatch, inverted signal | Measure bit time and idle polarity |
| First characters missing | Receiver starts late or resets | Capture boot and reset timing |
| Works one direction only | One wire, input level, or receiver configuration | Test directions separately |
| Fails at high baud | Clock error, wiring, noise, or slow level shifter | Lower baud and inspect edges |
| Bytes disappear under load | Buffer overrun or blocking firmware | Count errors and service RX promptly |
Adding a delay may change the symptom, but it does not prove that timing or voltage is valid.
Make diagnostic output useful
BOOT rev=3 reset=power_on
UART baud=115200 format=8N1
COUNT 0001
COUNT 0002
A counter reveals missing or duplicated messages. Firmware revision and reset cause distinguish corruption from an unexpected restart.
For chip-to-chip binary data, add a frame boundary, length, message type, sequence number, and integrity check appropriate to the risk. UART carries bytes; it does not define the packet protocol.
Completion checklist
- [ ] TX crosses to RX and both endpoints share a valid reference.
- [ ] I verified logic UART versus RS-232, RS-485, or USB.
- [ ] Voltage levels and input limits are compatible.
- [ ] Baud rate, data bits, parity, and stop bits match.
- [ ] I can estimate and measure one bit time.
- [ ] I use a logic analyzer for frames and a scope for electrical quality.
- [ ] I inspect buffering only after the wire-level frame is valid.
Next experiment
UART needs no shared clock. An SPI LCD adds a clock, chip select, command/data state, and controller-specific startup sequence. Next, I will debug why a powered display can remain completely white.
Comments
Post a Comment