Communication Protocols

Embedded devices talk over serial buses. Learn UART framing and baud rate, SPI modes, I²C addressing and pull-ups, CAN arbitration, RS-485 and Modbus, and how to choose the right protocol for each job.

No MCU works alone: it reads sensors, drives displays, talks to radios and joins vehicle or factory networks. Serial protocols send data one bit at a time over a few wires, each trading speed, distance, wire count, and robustness differently.

UART, SPI and I²C connect chips on a board; CAN, RS-485 and Ethernet connect devices across machines, vehicles and plants.

An analogy

UART is a phone call between two people; SPI is a teacher calling on students one at a time with a dedicated line to each; I²C is a meeting where everyone shares one line and speaks only when their name is called; CAN is a meeting with strict priority rules for who may speak first.

UART

Asynchronous: no shared clock. Both ends agree on a baud rate (9600, 115200…). Each frame: start bit (0), 5–9 data bits LSB-first, optional parity, 1–2 stop bits (1). 8N1 means 8 data bits, no parity, 1 stop bit — 10 bits per byte. Point-to-point, TX of one to RX of the other, common ground.

SPI

Synchronous and full duplex with four signals: SCLK, MOSI, MISO and one chip-select per device. Very fast (tens of MHz), simple, but needs a CS line per peripheral. Four modes (CPOL, CPHA) define clock idle level and sampling edge — master and slave must match.

I²C

Two open-drain wires (SDA, SCL) with pull-ups shared by up to ~100 devices, each with a 7-bit address. Transactions: START, address + R/W, ACK, data bytes with ACKs, STOP. Speeds 100 kHz, 400 kHz, 1 MHz. Pull-up values balance rise time (bus capacitance) against sink current.

CAN and RS-485

CAN uses a differential pair and bitwise arbitration: dominant (0) bits win, so the lowest-ID message gets the bus without collisions. It adds CRC, ACK and automatic retransmission — the standard in vehicles and industrial machines (1 Mbit/s classic, up to 5–8 Mbit/s CAN-FD). RS-485 is a differential multi-drop physical layer (up to ~1200 m) carrying protocols like Modbus RTU.

Choosing and hardening a link

Consider distance, speed, node count, noise, power and cost. Long or noisy links need differential signalling, termination (120 Ω at each end of CAN/RS-485), isolation and ESD/TVS protection. Add framing, checksums/CRCs, timeouts and retries in software — a protocol is only as reliable as its error handling.

Formulas

Worked example: UART throughput

Given: 115 200 baud, 8N1

Find: Bytes per second and time per byte.

Answer: 11.5 kB/s

Worked example: I²C pull-up range

Given: V_DD = 3.3 V, V_OL = 0.4 V, I_OL = 3 mA; Fast mode: t_r ≤ 300 ns; C_b = 200 pF

Find: Pull-up range.

Answer: ≈ 1.2–1.5 kΩ

SPI transfer and I²C register read (bare-metal style)

// SPI: full duplex — every byte sent clocks one byte in.
uint8_t spi_transfer(uint8_t out) {
    while (!(SPI1->SR & SPI_SR_TXE)) { }   // wait for TX empty
    *(volatile uint8_t *)&SPI1->DR = out;
    while (!(SPI1->SR & SPI_SR_RXNE)) { }  // wait for RX byte
    return *(volatile uint8_t *)&SPI1->DR;
}

// I2C register read: START, addr+W, reg, repeated START, addr+R, data, NACK, STOP
int i2c_read_reg(uint8_t addr7, uint8_t reg, uint8_t *val) {
    if (i2c_start() || i2c_write((addr7 << 1) | 0)) return -1;  // expect ACK
    if (i2c_write(reg)) return -1;
    if (i2c_start() || i2c_write((addr7 << 1) | 1)) return -1;  // repeated start
    *val = i2c_read(/*ack=*/0);                                  // NACK last byte
    i2c_stop();
    return 0;
}

SPI exchanges bytes simultaneously; I²C uses addresses, acknowledgements and a repeated start to switch from writing the register pointer to reading data.

Common mistakes

Applications

Key takeaways

Going further