67

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.

1

2 Answers 2

112

Introduction to SPI

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.


SPI signals

In a full-blown SPI system you will have four signal lines:

  • Master Out, Slave In (MOSI) - which is the data going from the master to the slave
  • Master In, Slave Out (MISO) - which is the data going from the slave to the master
  • Serial Clock (SCK) - when this toggles both the master and the slave sample the next bit
  • Slave Select (SS) - this tells a particular slave to go "active"

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:

SPI protocol showing 4 signals

Note that three signals are outputs from the master (MOSI, SCK, SS) and one is an input (MISO).


Timing

The sequence of events is:

  • SS goes low to assert it and activate the slave
  • The SCK line toggles to indicate when the data lines should be sampled
  • The data is sampled by both master and slave on the leading edge of SCK (using the default clock phase)
  • Both master and slave prepare for the next bit on the trailing edge of SCK (using the default clock phase), by changing MISO / MOSI if necessary
  • Once the transmission is over (possibly after multiple bytes have been sent) then SS goes high to de-assert it

Note that:

  • The most significant bit is sent first (by default)
  • Data is sent and received at the same instant (full duplex)

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);

Sample code

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

Wiring for output-only SPI

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.

SPI protocol showing 3 signals

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:

64 pixel LED display

In this case you can see that the board maker has used slightly different signal names:

  • DIN (Data In) is MOSI (Master Out, Slave In)
  • CS (Chip Select) is SS (Slave Select)
  • CLK (Clock) is SCK (Serial Clock)

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):

7-segment LED display

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.


Clock phase and polarity

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.

  • Mode 0 (the default) - clock is normally low (CPOL = 0), and the data is sampled on the transition from low to high (leading edge) (CPHA = 0)
  • Mode 1 - clock is normally low (CPOL = 0), and the data is sampled on the transition from high to low (trailing edge) (CPHA = 1)
  • Mode 2 - clock is normally high (CPOL = 1), and the data is sampled on the transition from high to low (leading edge) (CPHA = 0)
  • Mode 3 - clock is normally high (CPOL = 1), and the data is sampled on the transition from low to high (trailing edge) (CPHA = 1)

These are illustrated in this graphic:

SPI clock phase and polarity

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:

74HC595 clock

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

Data order

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

Speed

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:

  • SPI_CLOCK_DIV2
  • SPI_CLOCK_DIV4
  • SPI_CLOCK_DIV8
  • SPI_CLOCK_DIV16
  • SPI_CLOCK_DIV32
  • SPI_CLOCK_DIV64
  • SPI_CLOCK_DIV128

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 to Arduino

Arduino Uno

Connecting via digital pins 10 to 13:

Arduino Uno SPI pins

Connecting via the ICSP header:

ICSP pinouts - Uno

ICSP header

Arduino Atmega2560

Connecting via digital pins 50 to 52:

Arduino Mega2560 SPI pins

You can also use the ICSP header, similar to the Uno above.

Arduino Leonardo

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.


Multiple slaves

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:

Multiple SPI slaves

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.


Protocols

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.


How to make an SPI slave

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.

Hardware setup

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).

Master example

#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

Slave example

#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.

Example of connecting master to slave using SPI

Arduino SPI master and slave


How to get a response from a slave

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.

Master (example)

  #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.

Slave (example)

// 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

Example output

Adding results:
25
32
48
57
Subtracting results:
2
9
25
34
Adding results:
25
32
48
57
Subtracting results:
2
9
25
34

Logic analyzer output

This shows the timing between sending and receiving in the above code:

SPI master and slave timing


New functionality in IDE 1.6.0 onwards

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:

  • Clock speed
  • Bit order
  • Clock phase and polarity

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

Why use SPI?

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:

  • Some devices (quite a few) only support the SPI transfer method. For example the 74HC595 output shift register, the 74HC165 input shift register, the MAX7219 LED driver, and quite a few LED strips that I have seen. So, you might use it because the target device only supports it.
  • SPI is really the fastest method available on the Atmega328 (and similar) chips. The fastest rate quoted above is 888,888 bytes per second. Using I2C you can only get around 40,000 bytes per second. The overhead of the I2C is quite substantial, and if you are trying to interface really quickly, SPI is the preferred choice. Quite a few chip families (eg. MCP23017 and MCP23S17) actually support both I2C and SPI so you often can choose between speed, and the ability to have multiple devices on a single bus.
  • The SPI and I2C devices are both supported in hardware on the Atmega328 so you could conceivably be doing a transfer via SPI simultaneously with I2C which would give you a speed boost.

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).


References

15
  • Are you going to cover the weirdness that is the Due's SPI? Where the configuration of the SPI port is tied to the SS pin used, and there are (IIRC) 4 hardware SS pins assigned to the SPI port?
    – Majenko
    Sep 26, 2015 at 10:49
  • Other point about the selection: sometimes you really have no choice because the sensor you want/need to use is only available as I2C. Sep 26, 2015 at 11:34
  • 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.
    – Nick Gammon
    Sep 26, 2015 at 21:22
  • 3
    When is the audiobook of this answer coming out, and will you be reading it yourself ;) Sep 27, 2015 at 20:52
  • 3
    @AMADANONInc. Perhaps a music video? Or an animation? I'm not sure if my Australian accent would be understandable. :P
    – Nick Gammon
    Sep 27, 2015 at 21:23
1

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

enter image description here enter image description here enter image description here enter image description here

Not the answer you're looking for? Browse other questions tagged or ask your own question.