← Back to Embedded C series
🔌
8051
8051 · Prereq: lesson 10

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.

UARTserialbaudSCONSBUF11.0592MHz
Duration
~1.5–2 hours
Level
📊 Intermediate
Prerequisite
🎯 Lesson 10
OUTCOME
Initialize a 9600bps 8N1 UART with SCON·Timer1·SBUF to send strings to a PC and compute the baud reload (TH1).

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

c
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.

c
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

c
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

c
#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

  1. Write an echo program that sends back received characters (uart_rx → uart_tx)
  2. Compute TH1 for 4800bps·19200bps and verify in pc_test
  3. Redefine putchar as UART send to print numbers with printf (and explain why it's heavy on 8051)
Example code / lecture materials

All lecture materials and example code (with homework and answers) are openly available on GitHub.

View on GitHub ↗