With reference to the Arduino Uno, Mega2560, Leonardo and similar boards:
- How does SPI work?
- How fast is SPI?
- How do I connect between a master and a slave?
- How do I make an SPI slave?
Please note: This is intended as a reference question.
Arduino Stack Exchange is a question and answer site for developers of open-source hardware and software that is compatible with Arduino. It only takes a minute to sign up.
Sign up to join this communityWith reference to the Arduino Uno, Mega2560, Leonardo and similar boards:
Please note: This is intended as a reference question.
The Serial Peripheral Interface Bus (SPI) interface is used for communication between multiple devices over short distances, and at high speed.
Typically there is a single "master" device, which initiates communications and supplies the clock which controls the data transfer rate. There can be one or more slaves. For more than one slave, each one has its own "slave select" signal, described later.
In a full-blown SPI system you will have four signal lines:
When multiple slaves are connected to the MISO signal they are expected to tri-state (keep at high impedance) that MISO line until they are selected by Slave Select being asserted. Normally Slave Select (SS) goes low to assert it. That is, it is active low. Once a particular slave is selected it should configure the MISO line as an output so it can send data to the master.
This image shows the way that data is exchanged as one byte is sent:
Note that three signals are outputs from the master (MOSI, SCK, SS) and one is an input (MISO).
The sequence of events is:
SS
goes low to assert it and activate the slaveSCK
line toggles to indicate when the data lines should be sampledSCK
(using the default clock phase)SCK
(using the default clock phase), by changing MISO
/ MOSI
if necessarySS
goes high to de-assert itNote that:
Because data is sent and received on the same clock pulse it is not possible for the slave to respond to the master immediately. SPI protocols usually expect the master to request data on one transmission, and get a response on a subsequent one.
Using the SPI library on the Arduino, doing a single transfer looks like this in code:
byte outgoing = 0xAB;
byte incoming = SPI.transfer (outgoing);
Example of sending only (ignoring any incoming data):
#include <SPI.h>
void setup (void)
{
digitalWrite(SS, HIGH); // ensure SS stays high
SPI.begin ();
} // end of setup
void loop (void)
{
byte c;
// enable Slave Select
digitalWrite(SS, LOW); // SS is pin 10
// send test string
for (const char * p = "Fab" ; c = *p; p++)
SPI.transfer (c);
// disable Slave Select
digitalWrite(SS, HIGH);
delay (100);
} // end of loop
The above code (which sends only) might be used to drive an output serial shift register. These are output-only devices, so we don't need to worry about any incoming data. In their case the SS pin might be called the "store" or "latch" pin.
Examples of this are the 74HC595 serial shift register, and various LED strips, just to mention a couple. For example, this 64 pixel LED display driven by a MAX7219 chip:
In this case you can see that the board maker has used slightly different signal names:
Most boards will follow a similar pattern. Sometimes DIN is just DI (Data In).
Here is another example, this time a 7-segment LED display board (also based on the MAX7219 chip):
This uses exactly the same signal names as the other board. In both of these cases you can see that the board only needs 5 wires to it, the three for SPI, plus power and ground.
There are four way you can sample the SPI clock.
The SPI protocol allows for variations on the polarity of the clock pulses. CPOL is clock polarity, and CPHA is clock phase.
These are illustrated in this graphic:
You should refer to the datasheet for your device to get the phase and polarity correct. There will usually be a diagram which shows how to sample the clock. For example, from the datasheet for the 74HC595 chip:
As you can see the clock is normally low (CPOL = 0) and it is sampled on the leading edge (CPHA = 0) so this therefore is SPI mode 0.
You can change the clock polarity and phase in code like this (choose one only, of course):
SPI.setDataMode (SPI_MODE0);
SPI.setDataMode (SPI_MODE1);
SPI.setDataMode (SPI_MODE2);
SPI.setDataMode (SPI_MODE3);
This method is deprecated in versions 1.6.0 onwards of the Arduino IDE. For recent versions you change the clock mode in the SPI.beginTransaction
call, like this:
SPI.beginTransaction (SPISettings (2000000, MSBFIRST, SPI_MODE0)); // 2 MHz clock, MSB first, mode 0
The default is most-significant bit first, however you can tell the hardware to process the least significant bit first like this:
SPI.setBitOrder (LSBFIRST); // least significant bit first
SPI.setBitOrder (MSBFIRST); // most significant bit first
Again, this is deprecated in versions 1.6.0 onwards of the Arduino IDE. For recent versions you change the bit order in the SPI.beginTransaction
call, like this:
SPI.beginTransaction (SPISettings (1000000, LSBFIRST, SPI_MODE2)); // 1 MHz clock, LSB first, mode 2
The default setting for SPI is to use the system clock speed divided by four, that is, one SPI clock pulse every 250 ns, assuming a 16 MHz CPU clock. You can change the clock divider by using setClockDivider
like this:
SPI.setClockDivider (divider);
Where "divider" is one of:
The fastest rate is "divide by 2" or one SPI clock pulse every 125 ns, assuming a 16 MHz CPU clock. This would therefore take 8 * 125 ns or 1 µs to transmit one byte.
This method is deprecated in versions 1.6.0 onwards of the Arduino IDE. For recent versions you change the transfer speed in the SPI.beginTransaction
call, like this:
SPI.beginTransaction (SPISettings (4000000, MSBFIRST, SPI_MODE0)); // 4 MHz clock, MSB first, mode 0
However empirical testing shows that it is necessary to have two clock pulses between bytes, so the maximum rate at which bytes can be clocked out is 1.125 µs each (with a clock divider of 2).
To summarize, each byte can be sent at a maximum rate of one per 1.125 µs (with a 16 MHz clock) giving a theoretical maximum transfer rate of 1/1.125 µs, or 888,888 bytes per second (excluding overhead like setting SS low and so on).
Connecting via digital pins 10 to 13:
Connecting via the ICSP header:
Connecting via digital pins 50 to 52:
You can also use the ICSP header, similar to the Uno above.
The Leonardo and Micro do not expose the SPI pins on the digital pins, unlike the Uno and Mega. Your only option is to use the ICSP header pins, as illustrated above for the Uno. However the Leonardo and ProMicro don't expose the SS pin and thus cannot be used as an SPI slave.
A master can communicate with multiple slaves (however only one at a time). It does this by asserting SS for one slave and de-asserting it for all the others. The slave which has SS asserted (usually this means LOW) configures its MISO pin as an output so that slave, and that slave alone, can respond to the master. The other slaves ignore any incoming clock pulses if SS is not asserted. Thus you need one additional signal for each slave, like this:
In this graphic you can see that MISO, MOSI, SCK are shared between both slaves, however each slave has its own SS (slave select) signal.
The SPI spec does not specify protocols as such, so it is up to individual master/slave pairings to agree on what the data means. Whilst you can send and receive bytes simultaneously, the received byte cannot be a direct response to the sent byte (as they are being assembled simultaneously).
So it would be more logical for one end to send a request (eg. 4 might mean "list the disk directory") and then do transfers (perhaps just sending zeros outwards) until it receives a complete response. The response might terminate with a newline, or 0x00 character.
Read the datasheet for your slave device to see what protocol sequences it expects.
The earlier example shows the Arduino as the master, sending data to a slave device. This example shows how the Arduino can be a slave.
Connect two Arduino Unos together with the following pins connected to each other:
10 (SS)
11 (MOSI)
12 (MISO)
13 (SCK)
+5v (if required)
GND (for signal return)
On the Arduino Mega, the pins are 50 (MISO), 51 (MOSI), 52 (SCK), and 53 (SS).
In any case, MOSI at one end is connected to MOSI at the other, you don't swap them around (that is you do not have MOSI <-> MISO). The software configures one end of MOSI (master end) as an output, and the other end (slave end) as an input.
On some boards (like Leonardo, ProMicro — see above) the SS pin isn't exposed (rather it is connected to RX_LED). It is not possible to remap SS to another pin to be able to use those boards as an SPI slave as the AVR uses fixed SPI pins according to the datasheets (PB0-PB3 on 32U4).
#include <SPI.h>
void setup (void)
{
digitalWrite(SS, HIGH); // ensure SS stays high for now
// Put SCK, MOSI, SS pins into output mode
// also put SCK, MOSI into LOW state, and SS into HIGH state.
// Then put SPI hardware into Master mode and turn SPI on
SPI.begin ();
// Slow down the master a bit
SPI.setClockDivider(SPI_CLOCK_DIV8);
} // end of setup
void loop (void)
{
char c;
// enable Slave Select
digitalWrite(SS, LOW); // SS is pin 10
// send test string
for (const char * p = "Hello, world!\n" ; c = *p; p++)
SPI.transfer (c);
// disable Slave Select
digitalWrite(SS, HIGH);
delay (1000); // 1 seconds delay
} // end of loop
#include <SPI.h>
char buf [100];
volatile byte pos;
volatile bool process_it;
void setup (void)
{
Serial.begin (115200); // debugging
// turn on SPI in slave mode
SPCR |= bit (SPE);
// have to send on master in, *slave out*
pinMode (MISO, OUTPUT);
// get ready for an interrupt
pos = 0; // buffer empty
process_it = false;
// now turn on interrupts
SPI.attachInterrupt();
} // end of setup
// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR; // grab byte from SPI Data Register
// add to buffer if room
if (pos < sizeof buf)
{
buf [pos++] = c;
// example: newline means time to process buffer
if (c == '\n')
process_it = true;
} // end of room available
} // end of interrupt routine SPI_STC_vect
// main loop - wait for flag set in interrupt routine
void loop (void)
{
if (process_it)
{
buf [pos] = 0;
Serial.println (buf);
pos = 0;
process_it = false;
} // end of flag set
} // end of loop
The slave is entirely interrupt-driven, thus it can doing other stuff. The incoming SPI data is collected in a buffer, and a flag set when a "significant byte" (in this case a newline) arrives. This tells the slave to get on and start processing the data.
Following on from the code above which sends data from an SPI master to a slave, the example below shows sending data to a slave, having it do something with it, and return a response.
The master is similar to the example above. However an important point is that we need to add a slight delay (something like 20 microseconds). Otherwise the slave doesn't have a chance to react to the incoming data and do something with it.
The example shows sending a "command". In this case "a" (add something) or "s" (subtract something). This is to show that the slave is actually doing something with the data.
After asserting slave-select (SS) to initiate the transaction, the master sends the command, followed by any number of bytes, and then raises SS to terminate the transaction.
A very important point is that the slave cannot respond to an incoming byte at the same moment. The response has to be in the next byte. This is because the bits which are being sent, and the bits which are being received, are being sent simultaneously. Thus to add something to four numbers we need five transfers, like this:
transferAndWait ('a'); // add command
transferAndWait (10);
a = transferAndWait (17);
b = transferAndWait (33);
c = transferAndWait (42);
d = transferAndWait (0);
First we request action on number 10. But we don't get a response until the next transfer (the one for 17). However "a" will be set to the reply to 10. Finally we end up sending a "dummy" number 0, to get the reply for 42.
#include <SPI.h>
void setup (void)
{
Serial.begin (115200);
Serial.println ();
digitalWrite(SS, HIGH); // ensure SS stays high for now
SPI.begin ();
// Slow down the master a bit
SPI.setClockDivider(SPI_CLOCK_DIV8);
} // end of setup
byte transferAndWait (const byte what)
{
byte a = SPI.transfer (what);
delayMicroseconds (20);
return a;
} // end of transferAndWait
void loop (void)
{
byte a, b, c, d;
// enable Slave Select
digitalWrite(SS, LOW);
transferAndWait ('a'); // add command
transferAndWait (10);
a = transferAndWait (17);
b = transferAndWait (33);
c = transferAndWait (42);
d = transferAndWait (0);
// disable Slave Select
digitalWrite(SS, HIGH);
Serial.println ("Adding results:");
Serial.println (a, DEC);
Serial.println (b, DEC);
Serial.println (c, DEC);
Serial.println (d, DEC);
// enable Slave Select
digitalWrite(SS, LOW);
transferAndWait ('s'); // subtract command
transferAndWait (10);
a = transferAndWait (17);
b = transferAndWait (33);
c = transferAndWait (42);
d = transferAndWait (0);
// disable Slave Select
digitalWrite(SS, HIGH);
Serial.println ("Subtracting results:");
Serial.println (a, DEC);
Serial.println (b, DEC);
Serial.println (c, DEC);
Serial.println (d, DEC);
delay (1000); // 1 second delay
} // end of loop
The code for the slave basically does almost everything in the interrupt routine (called when incoming SPI data arrives). It takes the incoming byte, and adds or subtracts as per the remembered "command byte". Note that the response will be "collected" next time through the loop. This is why the master has to send one final "dummy" transfer to get the final reply.
In my example I am using the main loop to simply detect when SS goes high, and clear the saved command. That way, when SS is pulled low again for the next transaction, the first byte is considered the command byte.
More reliably, this would be done with an interrupt. That is, you would physically connect SS to one of the interrupt inputs (eg, on the Uno, connect pin 10 (SS) to pin 2 (an interrupt input), or use a pin-change interrupt on pin 10.
Then the interrupt could be used to notice when SS is being pulled low or high.
// what to do with incoming data
volatile byte command = 0;
void setup (void)
{
// have to send on master in, *slave out*
pinMode(MISO, OUTPUT);
// turn on SPI in slave mode
SPCR |= _BV(SPE);
// turn on interrupts
SPCR |= _BV(SPIE);
} // end of setup
// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR;
switch (command)
{
// no command? then this is the command
case 0:
command = c;
SPDR = 0;
break;
// add to incoming byte, return result
case 'a':
SPDR = c + 15; // add 15
break;
// subtract from incoming byte, return result
case 's':
SPDR = c - 8; // subtract 8
break;
} // end of switch
} // end of interrupt service routine (ISR) SPI_STC_vect
void loop (void)
{
// if SPI not active, clear current command
if (digitalRead (SS) == HIGH)
command = 0;
} // end of loop
Adding results:
25
32
48
57
Subtracting results:
2
9
25
34
Adding results:
25
32
48
57
Subtracting results:
2
9
25
34
This shows the timing between sending and receiving in the above code:
Version 1.6.0 of the IDE has changed the way SPI works, to an extent. You still need to do SPI.begin()
before using SPI. That sets up the SPI hardware. However now, when you are about to start communicating with a slave you also do SPI.beginTransaction()
to set up SPI (for this slave) with the correct:
When you are done communicating with the slave, you call SPI.endTransaction()
. For example:
SPI.beginTransaction (SPISettings (2000000, MSBFIRST, SPI_MODE0));
digitalWrite (SS, LOW); // assert Slave Select
byte foo = SPI.transfer (42); // do a transfer
digitalWrite (SS, HIGH); // de-assert Slave Select
SPI.endTransaction (); // transaction over
I would add one preliminary question: when/why would you use SPI? A need for multi-master configuration or a very large number of slaves would tilt the scale toward I2C.
This is an excellent question. My answers are:
Both methods have their place. I2C lets you connect many devices to a single bus (two wires, plus ground) so it would be the preferred choice if you needed to interrogate a substantial number of devices, perhaps fairly infrequently. However the speed of SPI could be more relevant for situations where you need to output rapidly (eg. a LED strip) or input rapidly (eg. an ADC converter).
My page about SPI - also has details about bit-banged SPI, and using the USART to gain a second hardware SPI on the Atmega328 chip.
Are you going to cover the weirdness that is the Due's SPI?
- I don't know anything about the Due's SPI (apart from presuming the overall protocol is the same). You are welcome to add a reply covering that aspect of it.
Thank you for the information.
Mr. Nick Gammon said
However the Leonardo and ProMicro don't expose the SS pin and thus cannot be used as an SPI slave.
but I could use ProMicro and Leonardo as SPI slave devides by wiring SS signal.
Reference: https://asukiaaa.blogspot.com/2023/05/atmega32u4-as-spi-slave.html