Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/manish04-mu/TheEmbeddedInsights/llms.txt

Use this file to discover all available pages before exploring further.

SPI on the Arduino UNO (ATmega328P) uses four dedicated hardware pins. Unlike software (bit-banged) SPI, the hardware peripheral is tied to specific port pins and cannot be remapped — correct physical wiring is a prerequisite before any sketch will produce valid transfers. Get the pinout right once and every SPI library on the Arduino ecosystem will work without modification.

Pin Mapping

SignalArduino UNO PinDirectionNotes
SCK13Output (master)Serial clock; driven by the master and shared by all slaves on the bus
MOSI11Output (master)Master Out Slave In — data transmitted from master to slave
MISO12Input (master)Master In Slave Out — data returned from slave to master
CS10Output (master)Chip Select; active LOW — one dedicated CS pin required per slave device
Pin 10 is used as CS in all example sketches in this series. Technically any digital output pin can serve as CS, but Pin 10 is also the hardware SS pin on the ATmega328P. If Pin 10 is configured as an input while SPI is active, the hardware can automatically switch the device into slave mode — always set it as OUTPUT even if you choose a different CS pin.

Configuring CS in Code

The setup sequence below is taken directly from the Arduino UNO sketches in this series. Three things happen in setup() before any SPI transfer takes place: CS is declared as an output, it is driven HIGH to keep the slave deselected during initialisation, and then SPI.begin() is called to configure the hardware peripheral.
#include <SPI.h>

#define CS 10  // Chip Select pin

void setup() {
  pinMode(CS, OUTPUT);          // Declare CS as a digital output
  digitalWrite(CS, HIGH);       // Keep slave deselected initially
  SPI.begin();                  // Configure SCK (13), MOSI (11), and SS (10) as outputs
}

void loop() {
  SPI.beginTransaction(SPISettings(100000, MSBFIRST, SPI_MODE0));

  digitalWrite(CS, LOW);        // Select slave — begin transaction
  SPI.transfer(0x62);           // Shift one byte out on MOSI, receive one byte on MISO
  digitalWrite(CS, HIGH);       // Deselect slave — end transaction

  SPI.endTransaction();         // Release the SPI bus
}
SPI.begin() automatically configures the SCK (Pin 13), MOSI (Pin 11), and hardware SS (Pin 10) pins as outputs. You only need to call pinMode() and digitalWrite() manually for your CS pin — and only because you want explicit control over the initial state before the library takes over.

Multiple Slaves

SCK, MOSI, and MISO are shared across every slave on the SPI bus — all three lines connect to every device in parallel. Only CS is unique per slave: each slave device gets its own dedicated CS line connected to a separate Arduino output pin. When the master asserts one slave’s CS LOW, all other slaves see their CS line remain HIGH and ignore traffic on MOSI and SCK. Critically, unselected slaves must tri-state (high-impedance) their MISO output so they do not drive the shared MISO line and corrupt the response from the selected slave. Most SPI peripherals do this automatically when CS is HIGH, but always confirm in the datasheet.
#define CS_SENSOR  10  // Slave 1 — e.g. SPI temperature sensor
#define CS_FLASH    9  // Slave 2 — e.g. SPI flash memory

void setup() {
  pinMode(CS_SENSOR, OUTPUT);
  pinMode(CS_FLASH,  OUTPUT);
  digitalWrite(CS_SENSOR, HIGH);  // Both slaves deselected at startup
  digitalWrite(CS_FLASH,  HIGH);
  SPI.begin();
}

Logic Analyzer Probe Points

When verifying SPI signals with a logic analyzer, connect probes to the following pins. The channel assignment below matches the decoding configuration used in all captured waveforms in this series.
ChannelSignalArduino UNO Pin
CH0SCK13
CH1CS / Enable10
CH2MOSI11
CH3MISO12
Connect the logic analyzer’s ground clip to any GND pin on the Arduino UNO. Set your analyzer’s sample rate to at least 10× the SPI clock frequency — at 100 kHz that means a minimum of 1 MHz sample rate.

Common Wiring Mistakes

MOSI and MISO swapped between master and slave. MOSI on the master connects to MOSI (sometimes labelled SDI or DI) on the slave — not to the slave’s MISO. Both lines are named from the master’s perspective. Double-check the slave datasheet: SDI = data in to the slave = connect to master MOSI (Pin 11); SDO = data out from the slave = connect to master MISO (Pin 12).
CS not pulled HIGH before SPI.begin(). If CS floats or is left LOW when SPI.begin() executes, the slave may interpret the bus initialisation sequence as the start of a transaction, latching garbage configuration data. Always call digitalWrite(CS, HIGH) before SPI.begin() as shown in the setup snippet above.
Forgetting pinMode(CS, OUTPUT). Without pinMode(CS, OUTPUT), the CS pin defaults to a high-impedance input. digitalWrite() has no effect on an input pin (it only enables the weak internal pull-up), so CS will float and can transition LOW unpredictably due to noise or bus capacitance. The slave will see spurious CS pulses, triggering unintended transfers and corrupting its internal state machine.

Build docs developers (and LLMs) love