Electronics From Zero Search/

Explainer · Intermediate · about 40 minutes

UART, I2C and SPI

Microcontrollers talk to sensors, displays and memory chips by sending bits one after another down a few wires. See exactly what is on each wire, bit by bit, as a logic analyser would show it, for the three buses you will meet in almost every project.

Sending bits one at a time

A byte is eight bits. Sending them one after another on a single wire is called serial communication. The receiver must know when each bit starts, so every bus has a way to keep time:

  • UART agrees a speed in advance (the baud rate) and marks the start of each byte with a start bit.
  • I2C and SPI send a clock on a separate wire: each tick says “read the data now”.

The chip that starts each conversation is the controller (usually your microcontroller); the chips it talks to are peripherals. Older documents call them master and slave.

A logic analyser records several digital wires at once and shows them as rows of highs and lows over time, decoding the bytes for you. The view below works the same way.

Logic analyser

Logic analyser

sent by the controller sent by the peripheral not driven Hover over a bit for what it means.

What the receiver decodes

    What is happening

    Model: ideal digital signals with instant edges. The decoded lines are produced by reading the drawn waveforms back the way a real receiver samples them. Real signals have slopes, and I2C rises more slowly through its pull-up resistors.

    Which bus?

    UARTI2CSPI
    Wires (plus ground)TX and RXSDA and SCL, shared by every chipSCK, MOSI, MISO, and one CS per chip
    TimingAgreed baud rateClock from the controllerClock from the controller
    Choosing a chipOne device per pair of wires7-bit address in every messagePull that chip’s CS low
    Typical speed9600 to 115200 baud100 or 400 kHz1 to tens of MHz
    Typical partsUSB serial to a computer, GPS, Bluetooth modulesSmall displays, temperature and motion sensors, clocksSD cards, flash memory, fast displays

    Wiring and code

    UART

    Cross the wires: TX of one goes to RX of the other. On an Arduino Uno, pins 0 and 1 are the same UART the USB uses, so disconnect anything on them while uploading. The Serial monitor shows what a sketch sends.

    Serial.begin(9600);
    Serial.print("Hi");

    I2C

    Each line needs a pull-up resistor to the supply (often 4.7 kΩ; many modules have them already). Chips only ever pull a line low or let it go: that is what lets many chips share two wires safely. On an Uno, SDA is A4 and SCL is A5.

    #include <Wire.h>
    Wire.begin();
    Wire.beginTransmission(0x3C);
    Wire.write(0x00); Wire.write(0xAF);
    Wire.endTransmission();   // 0 means the device answered

    SPI

    SCK, MOSI and MISO are shared; each chip gets its own CS (chip select) line, held high when idle. Newer documents name MOSI and MISO as COPI and CIPO. On an Uno they are pins 13 (SCK), 11 (MOSI) and 12 (MISO); any free pin can be a CS.

    #include <SPI.h>
    SPI.begin();
    SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
    digitalWrite(CS_PIN, LOW);
    SPI.transfer(0x9F);
    byte maker = SPI.transfer(0x00);
    digitalWrite(CS_PIN, HIGH);
    SPI.endTransaction();
    Logic levels

    A 5 V Arduino can damage a 3.3 V chip. Check the chip’s supply and input voltages, and use a level shifter (or a module that has one) between 5 V and 3.3 V parts. Always connect the grounds.

    Check your understanding