SPI Series · Part 4 of 5
SPI Programming with Arduino: A Hands-On Guide
With the theory out of the way, it's time to write actual SPI code. Arduino makes this straightforward thanks to the built-in SPI library. This article walks through wiring and code for a simple SPI transaction.
🔌 Wiring on a Typical Arduino Uno
| Signal | Uno Pin |
|---|---|
| SCLK | 13 |
| MISO | 12 |
| MOSI | 11 |
| CS (your choice) | 10 (or any digital pin) |
SCLK, MISO, and MOSI are fixed by the hardware SPI peripheral on the Uno, but CS can be any free digital pin — you just tell the library which pin you're using in your own code.
⚙️ Basic Setup Code
#include <SPI.h> const int CS_PIN = 10; void setup() { pinMode(CS_PIN, OUTPUT); digitalWrite(CS_PIN, HIGH); // deselect by default SPI.begin(); SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0)); }
SPISettings lets you set the clock speed (4 MHz here), bit order, and SPI mode all in one call — matching whatever your slave device's datasheet requires.
📤 Writing a Byte and Reading a Response
byte readRegister(byte regAddress) { digitalWrite(CS_PIN, LOW); // select the slave SPI.transfer(regAddress); // send register address byte result = SPI.transfer(0x00); // dummy byte, read response digitalWrite(CS_PIN, HIGH); // deselect the slave return result; }
This pattern — select, transfer, deselect — is the backbone of almost every SPI transaction you'll write. SPI.transfer() always sends and receives a byte simultaneously since SPI is full duplex, even if you only care about one direction.
📦 Reading Multiple Bytes (Burst Read)
void readBurst(byte startAddress, byte* buffer, int length) { digitalWrite(CS_PIN, LOW); SPI.transfer(startAddress); for (int i = 0; i < length; i++) { buffer[i] = SPI.transfer(0x00); } digitalWrite(CS_PIN, HIGH); }
🧩 Handling Multiple SPI Devices
Since MOSI, MISO, and SCLK are shared, you can put several SPI devices on the same three wires — each just needs its own dedicated CS pin. Always deselect (CS HIGH) every device except the one you're currently talking to. It's good practice to call SPI.beginTransaction() right before each transfer if different devices need different clock speeds or modes.
💡 Note on ESP32/STM32: The same core concepts — SPISettings, transfer(), CS handling — carry over to ESP32 and STM32 platforms, though pin assignments and maximum clock speeds differ. Always check your specific board's SPI pinout before wiring.
Up Next in This Series
Part 5 (final): Common SPI Problems and How to Fix Them — a full troubleshooting checklist.
Comments
Post a Comment