11. UART Serial Communication
To exchange human-readable text with a PC you need communication; the most basic is UART (serial) — two wires (TXD/RXD) carrying text. UART is asynchronous, so both sides must agree on the baud rate. The 8051 sets serial mode via SCON, baud via Timer1, and moves a byte through SBUF. We send a string at 9600bps·8N1 and learn why 11.0592MHz is used for accuracy.
What you'll learn
- 1Explain sync vs async and the UART frame (start·data·stop)
- 2Set Mode1 (8-bit UART) and receive enable (REN) via SCON
- 3Generate baud with Timer1 (Mode2 auto-reload) and compute TH1
- 4Send/receive a byte via SBUF/TI/RI
- 5Know why 11.0592MHz crystal is used for UART
Introduction
Serial sends data one bit at a time over one wire. UART works without a clock line (asynchronous), so both sides must use the same baud (e.g. 9600bps). Typical wiring: TXD(P3.1)·RXD(P3.0)·GND.
Key concepts
1) The 8N1 frame
idle(1) → [start=0] → [d0..d7] → [stop=1] → idle(1)
↑ data goes LSB (d0) first!
/* data8 · no parity (N) · stop1 = 8N1 */2) SCON and baud (Timer1)
Standard `SCON = 0x50` → SM1=1 (Mode1, 8-bit UART) + REN=1 (receive enable). The 8051 puts Timer1 in Mode2 (8-bit auto-reload) and makes baud from its overflow rate.
baud = Fosc / (384 × (256 - TH1)) (SMOD=0)
/* 11.0592MHz, 9600bps: n = 11059200/(384*9600) = 3 (integer!) → TH1 = 256-3 = 0xFD */11.0592MHz makes n an integer for standard bauds (zero error). 12MHz gives ~8.5% error at 9600, breaking comms — that's why UART boards use 11.0592MHz.
3) SBUF send/receive
void uart_tx(unsigned char c){ SBUF=c; while(TI==0); TI=0; }
unsigned char uart_rx(void){ while(RI==0); RI=0; return SBUF; }
/* TI/RI are only set by hardware — software must clear them to 0 each time */Core example
#include <reg52.h>
void uart_init(void){
SCON = 0x50; /* Mode1 + REN */
TMOD &= 0x0F; TMOD |= 0x20; /* Timer1 Mode2 (baud) */
TH1 = 0xFD; TL1 = 0xFD; /* 9600 @ 11.0592MHz */
TR1 = 1;
}
void uart_tx(unsigned char c){ SBUF=c; while(TI==0); TI=0; }
void uart_puts(const char *s){ while(*s) uart_tx((unsigned char)*s++); }
void main(void){ uart_init(); while(1){ uart_puts("Hello, 8051!\r\n"); } }Baud reload check (11.0592MHz): 1200→0xE8, 2400→0xF4, 4800→0xFA, 9600→0xFD. Watch output in the simulator's View → Serial Windows → UART #1.
Common mistakes
Q. The first char appears, then it stops.
A. You didn't clear TI. Hardware only sets it, so clear TI=0; after each send (and RI for receive).
Q. Garbled characters.
A. Baud mismatch. Match the terminal baud to 9600 and set Xtal to 11.0592MHz. At 12MHz, TH1=0xFD yields ~10417bps and breaks.
Q. TX works but RX doesn't.
A. REN (receive enable) in SCON may be off (0x50 includes REN=1). Also check you clear RI after reading.
Summary
- UART is clock-less async serial — both sides agree on baud
- Frame: start(0) → data 8 (LSB first) → stop(1); no parity = 8N1
- SCON=0x50 (Mode1·REN), Timer1 Mode2 (TMOD|=0x20) for baud
- TH1 = 256 - Fosc/(384×baud). 11.0592MHz gives integer values
- Write SBUF to send·read to receive. Clear TI/RI every time
Exercises
- Write an echo program that sends back received characters (uart_rx → uart_tx)
- Compute TH1 for 4800bps·19200bps and verify in pc_test
- Redefine putchar as UART send to print numbers with printf (and explain why it's heavy on 8051)
All lecture materials and example code (with homework and answers) are openly available on GitHub.
View on GitHub ↗