SPI, Faster and More Wires
| Difficulty | Intermediate |
|---|---|
| Category | Concepts |
| Supported boards | Arduino Uno R3 and compatible ATmega328P boards |
| Time needed | 40 minutes |
| Prerequisites | I2C, Two Wires and Many Devices |
| Tested board | Not yet verified |
| Tested on | Not yet verified |
| Arduino IDE version | Not yet verified |
| Library | Not 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.
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
D10master-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.

