SPI, Faster and More Wires

Draft. This tutorial has not been checked against the physical product yet. Confirm wiring, pin assignments and voltage limits against the product documentation before building it.
DifficultyIntermediate
CategoryConcepts
Supported boardsArduino Uno R3 and compatible ATmega328P boards
Time needed40 minutes
PrerequisitesI2C, Two Wires and Many Devices
Tested boardNot yet verified
Tested onNot yet verified
Arduino IDE versionNot yet verified
LibraryNot yet verified

What this covers

The other bus. I2C connects many devices with two wires and addresses; SPI connects them with four wires and no addresses at all, and is many times faster.

Five parts in this library use it — the SD card module, the RC522 reader, the MAX7219 matrix, the NRF24L01 radio and the 74HC595 shift register — so sooner or later you will wire one, and the question that catches everybody is why three wires can be shared and the fourth cannot.

Before you begin

Nothing to buy. You need I2C, Two Wires and Many Devices first, because the useful way to understand SPI is by what it does differently.

How it works

Four wires, and the names say what each does:

Wire Full name Direction What it carries
SCK Serial Clock Arduino to device The tick that paces every bit
MOSI Master Out, Slave In Arduino to device Data going out
MISO Master In, Slave Out Device to Arduino Data coming back
SS or CS Slave Select, Chip Select Arduino to device Which device is being talked to

The first three are shared by every device on the bus. The fourth is not: each device gets its own chip select line, and that is the whole addressing scheme.

Newer documentation often writes these as COPI/CIPO (Controller Out Peripheral In, and the reverse). The silkscreen on the parts Siratek sells says MOSI and MISO, so this library uses those and you should expect to meet both.

On an Uno

SPI signal Uno pin Also known as
SCK D13 The pin with the onboard LED
MISO D12
MOSI D11
SS D10 The default, but any spare pin works

D13 carrying the clock is why the onboard LED flickers while an SPI device is being read. That is the bus working, not a fault, and it is worth knowing before it puzzles you.

D10 is the default chip select, and you may use any pin for it — but see the warning below, because D10 has a peculiarity even when you are not using it.

Three SPI wires shared by every device on the bus, with a separate chip select line from the Arduino to each one, so adding a device costs a pin rather than an address
Three wires shared, one per device. I2C spends addresses; SPI spends pins.

Chip select is active LOW

A device listens only while its chip select is held LOW. Taking it HIGH makes the device ignore the shared wires entirely, including MISO, which it releases so another device can drive it.

So a transaction is always: pull CS low, exchange bytes, put CS high again. The library does this for you in most cases, which is why you rarely see it written out.

There are no addresses, and that cuts both ways

I2C gives each device a number, so two devices with the same number clash and there is nothing you can do except change one. You met that with the MPU-6050 and the DS3231, both at 0x68.

SPI has no such problem — three devices on one bus is three chip select pins and no conflict possible — and in exchange it has a different cost: every extra device costs a pin. Four SPI devices on an Uno is seven pins before anything else is connected.

That is the trade in one line. I2C spends addresses; SPI spends pins.

Faster, and why that matters here

I2C on an Uno usually runs at 100 kHz, sometimes 400. SPI on the same board commonly runs at 4 MHz or 8 MHz, which is ten to eighty times quicker.

For a temperature sensor read once a second, that difference is irrelevant. For the three parts where it is not:

  • SD cards move blocks of 512 bytes. At I2C speed a log line would take long enough to matter.
  • Displays push a whole frame of pixels. The MAX7219 and most graphical screens are SPI for this reason.
  • Radios have to move a packet while the other end is still listening.

The reason it can be fast is that SPI has no addressing, no acknowledgement and no bus arbitration to do — the clock comes from the Arduino and every bit is simply shifted out on it.

The four things that go wrong

MISO and MOSI swapped. The single most common SPI fault, because the names are nearly the same word and the correct wiring is not crossed: the Arduino’s MOSI goes to the device’s MOSI pin on every module in this library, because the module’s silkscreen is already written from the Arduino’s point of view. Nothing is damaged by swapping them; the device simply never answers.

Chip select left low, or two devices sharing one. Two devices selected at once both try to drive MISO, and the data is nonsense. Every device needs its own pin, and every pin must be set HIGH in setup() before anything else happens.

D10 left as an input. On an ATmega328P, if D10 is an input and something pulls it low, the SPI hardware switches itself out of master mode and the bus stops working. This is true even if you are using a different pin for chip select. The fix is one line: pinMode(10, OUTPUT) in setup(), always, whether or not you use the pin.

Voltage. The RC522 is a 3.3 V part, the NRF24L01 is a 3.3 V part, and most small SD modules are too — though the Siratek SD module is an exception with a level shifter on board. An Uno drives 5 V on SCK and MOSI. Mixing 3.3 V and 5 V Safely is the page, and the pattern is always the same: the two lines going to the device need converting, the one coming back does not.

Two devices, one bus

// Siratek: the shape of a two-device SPI bus. Both chip selects are
// set HIGH before either device is spoken to.

#include <SPI.h>

const int SD_CS    = 4;      // the SD module
const int RADIO_CS = 7;      // something else

void setup() {
  Serial.begin(9600);

  // Every chip select HIGH, so nothing is listening yet.
  pinMode(SD_CS, OUTPUT);
  pinMode(RADIO_CS, OUTPUT);
  digitalWrite(SD_CS, HIGH);
  digitalWrite(RADIO_CS, HIGH);

  // Required even though neither device uses pin 10. If 10 is an
  // input and goes low, the SPI hardware leaves master mode.
  pinMode(10, OUTPUT);

  SPI.begin();

  Serial.println(F("Siratek: bus ready, nothing selected."));
}

void loop() {
}

That setup() is the whole of what SPI asks of you before a library takes over. Almost every “my SD card worked until I added the radio” question is one of those four lines missing.

What a transaction looks like underneath

Libraries hide this, but it is worth seeing once:

// Siratek: one byte out, one byte back, by hand.

byte transfer(int csPin, byte value) {
  SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
  digitalWrite(csPin, LOW);       // this device, and only this one

  byte reply = SPI.transfer(value);

  digitalWrite(csPin, HIGH);      // release the bus
  SPI.endTransaction();
  return reply;
}

SPI.transfer() sends and receives at the same time. There is no separate read: a byte goes out on MOSI while a byte comes back on MISO, on the same eight clock ticks. To read without sending anything meaningful, you send a dummy byte, which is why you will see SPI.transfer(0x00) in library code and wonder what the zero is for.

SPISettings names three things — the speed, whether the most significant bit goes first, and the mode, which is one of four combinations of clock polarity and phase. Devices differ, the datasheet says which, and a wrong mode gives you data that is consistently wrong rather than absent. SPI_MODE0 is much the most common.

Check yourself

Three questions whose answers are the page.

Two devices, same model, on one bus. Does it work? Yes, with two chip select pins. The same two devices on I2C would clash at the same address and could not.

Can the onboard LED be used while an SD card is connected? Not usefully. D13 is the clock.

Why is pinMode(10, OUTPUT) in a sketch that uses pin 4 for chip select? Because the hardware drops out of master mode if D10 is an input and gets pulled low, regardless of which pin you chose.

Troubleshooting

Symptom Likely cause Check or fix
Device never answers MISO and MOSI swapped They are not crossed on these modules
Device never answers Chip select on the wrong pin It is whatever the constructor was told
Nonsense data Two devices selected at once Every CS HIGH in setup()
Worked alone, fails with a second device A chip select left low Each device gets its own pin
Worked, then stopped when a wire was added D10 floating as an input pinMode(10, OUTPUT)
Data consistently wrong, never absent Wrong SPI mode The datasheet names it; MODE0 is usual
Onboard LED flickers D13 is the clock Expected
3.3 V device dies 5 V on its supply or signals See Mixing 3.3 V and 5 V Safely
SD card works, radio does not Both on one CS Two devices, two pins
Running out of pins The cost of SPI Shift register, or move to I2C parts

Next steps

  • Easier first: I2C, Two Wires and Many Devices, which this page compares itself to.
  • Related: Mixing 3.3 V and 5 V Safely, because most SPI parts here are 3.3 V.
  • Use it: Logging Data to an SD Card, Reading RFID Cards with an RC522 and An 8×8 Dot Matrix with MAX7219 are the three places this bus turns up first.

Sources and attribution

All text and code in this tutorial are original Siratek work.

  • The Uno’s SPI pin assignment, the D10 master-mode behaviour and the four SPI modes are properties of the ATmega328P described in its datasheet.
  • The per-device notes — the RC522’s and NRF24L01’s 3.3 V supplies, and the level shifter on the Siratek SD module — are as established in those tutorials from the product photographs.

Bus speeds are the usual figures for an Uno rather than measurements. No third-party tutorial text, diagrams, photographs or code has been reproduced here.

Shop
Sidebar
0 Wishlist
0 Cart