How do you use SPI on an Arduino?

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

  • Nick Gammon

    Nick Gammon Correct answer

    5 years ago

    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.


    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.

    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

    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?

    Other point about the selection: sometimes you really have no choice because the sensor you want/need to use is only available as I2C.

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

    When is the audiobook of this answer coming out, and will you be reading it yourself ;)

    @AMADANONInc. Perhaps a music video? Or an animation? I'm not sure if my Australian accent would be understandable. :P

    @NickGammon, I live in NZ, and I've been learning from EEVBlog videos for some time, so I don't think I'll have any problems. A music video would be good, though - how's your circular breathing?

    I think sub section *"New functionality in IDE 1.6.0 onwards"* should explicitly answer the question *"What if I leave out SPI.beginTransaction?"* (etc.). Similary, answer the question "Will old sketches work?" (backwards compatibility (with some fixed defaults?)) - I think they do; that is what I observed yesterday when trying with Arduino Uno, software version 1.6.5, and a DAC (MCP4901); but there could be caveats that I don't know of. *"also"* could maybe be *"can also"*.

    New SPI has `SPI.transfer(buffer, size)` https://www.arduino.cc/en/Reference/SPITransfer

    @PeterMortensen using Arduino 1.8.8 setting SPI params the old way still works

    Is the slave interupt function triggered by the clock leading edge?

    No, it would be triggered by the **complete** transfer of one byte, as the ISR can immediately access that (SPDR), as in the code shown.

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM