11

Interfacing with SPI Devices

The Serial Peripheral Interface (SPI) bus is yet another serial bus standard that you can use to connect peripherals to your Arduino. It is fast but uses four pins compared with the two that I2C uses. SPI is not actually a true bus, as the fourth pin is a Save Select (SS) pin. One Arduino pin must be used for SS for each peripheral on the bus. This setup effectively addresses the right peripheral on the bus by turning all the other peripherals off.

A wide range of SPI devices are available, including many of the same type of devices available for I2C. It is not uncommon for peripherals to have both I2C and SPI interfaces.

Bit Manipulation

SPI interfacing tends to involve a lot of bit manipulation to get data on and off the bus. The first example project (using an MCP3008 ADC IC), in particular, requires a good understanding of how to shuffle bits along and mask the ones you don’t want in order to extract an integer value for the analog reading. For this reason, you may wish to review Chapter 8 before continuing with this chapter.

SPI Hardware

Figure 11-1 shows a typical configuration for an Arduino with two slave devices.

Images

Figure 11-1   Arduino and two slave SPI devices.

On the Arduino, the System Clock (SCLK), Master Out Slave In (MOSI), and Master In Slave Out (MISO) are linked to the Arduino pins of the same name, which map to pins D13, D11, and D12 on an Arduino Uno. Table 11-1 lists the pin assignments on the most common Arduino boards.

Images

Table 11-1   SPI Connections on Arduino Boards

The Slave select pins can be any pins on the Arduino. They are used to enable a particular slave just before data transmission and then disable it after communication is complete.

No pull-up resistors are required on any of the lines.

Because some Arduino boards, including the Leonardo, only have SPI connectors that are accessible from the ICSP header pins, shields that use SPI often have a socket header, which you can use to gain access to the SPI pin that meets the ICSP male header. Figure 11-2 shows the ICSP header with the ICSP headers labeled.

Images

Figure 11-2   Arduino Uno and ICSP connections.

Note that the Arduino Uno has a second ICSP header near the reset button. This is for programming the USB interface and doesn't relate to SPI use for the main processor.

The SPI Protocol

The SPI protocol is, at first sight, confusing because data is transmitted and received at the same time by both the master and the currently selected slave. At the same time that the master (Arduino) sends a bit from its MOSI pin to the corresponding MOSI pin on the slave, another bit is being sent back from the Slave’s MISO pin to the Arduino’s MISO pin.

Typically, the Arduino sends a byte’s worth of bits and then sends eight zeros while, at the same time, reading the results coming back from the slave.

The SPI Library

The SPI library is included with Arduino IDE, so you do not need to install anything to use it. It only supports Arduino-as-master scenarios. The library also only directly supports transmission of whole bytes. For most peripherals, this setup is just fine; however, some devices expect 12-bit messages, which can result in some complicated bit manipulation as you’ll see in the example in the next section of this chapter.

The first step is to include the SPI library:

#include <SPI.h>

Next, you need to start SPI by issuing the SPI.begin command in your “startup” function.

Images

You also need to set up digital outputs for each of the SS pins to the slave devices. These outputs can be any Arduino pins. Having set them to be outputs, you need to set them to HIGH immediately because the slave select logic is inverted, so a LOW means the slave is selected.

The Due has extended the SPI library so you can specify one pin to be used for slave selecting, and then the library automatically sets this LOW before transmission and then HIGH after transmission is complete. You can use this feature simply by specifying the pin to use as the only argument to SPI.begin. The disadvantage of doing it this way, however, is that it breaks compatibility with other Arduino boards. In the examples in this chapter, all the slave select pins are controlled manually and are, therefore, suitable for all Arduino boards.

A number of utility functions allow you to configure the SPI connection. However, the defaults will normally work, so you only need to change these settings if the datasheet for the slave device leads you to believe they might need changing. These functions are summarized in Table 11-2.

Images

Table 11-2   Configuration Functions

The combined data send and receive happens in the transfer function. This function transfers a byte of data and returns the byte of data that it received during the send operation.

byte sendByte = 0x23;byte receive
Byte = SPI.transfer(sendByte);

Because a conversation with a peripheral usually takes the form of the master requesting something from the slave and the slave responding, you’ll often have two transfers in order: one to request the data and the other (a send, probably of 0s) to pull back the data from the peripheral. You’ll see this in the next example.

SPI Example

This example interfaces a MCP3008 eight-channel ADC IC to an Arduino, adding another eight 10-bit analog inputs to your Arduino. The chip is low cost and easy to wire.

Figure 11-3 shows the chip wired to the Arduino using breadboard and jumper wires. The variable resistor (pot) is used to vary the voltage to analog input 0 between 0 and 5V.

Images

Figure 11-3   Wiring diagram for SPI example.

Following is the sketch for this example:

Images

Images

The function printByte was just used during development to display the binary data. Although Serial.print can display binary values, it does not include leading zeros, which makes interpreting the data difficult, whereas the printByte function always prints all 8 bits.

To see the data coming from the MCP3008, you can remove the // before the two calls to printByte and the binary data you are interested in will be displayed.

All the interesting code happens in the readADC function, which takes the ADC channel (0 to 7) as its parameter. The first thing you need to do is to use some bit manipulation to create the configuration byte that specifies the kind of analog conversion you want to perform and also the channel you want to use.

The chip is capable of two ADC operation modes. One mode is to compare two analog channels, and the second mode (which this example uses) returns the single-ended reading from the channel specified, just like an Arduino analog input. The datasheet for the MCP3008 (http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf) specifies that the configuration command needs to set four bits: the first bit needs to be 1 for single-ended mode; the next three bits determine the channel (0 to 7) to use.

The MCP3008 is not designed for the byte-at-a-time way in which the SPI library works. In order for the MCP3008 to recognize these 4 bits, we have to split them across 2 bytes. Here’s the code for doing this:

Images

The first byte of the configuration message contains two 1s, the first of which may not be needed and the second 1 corresponding to the mode bit (single-ended). The other 2 bits in this byte are the most significant 2 bits of the analog channel number. The remaining bit of this number is in the second configuration byte as its most significant bit.

The next line sets the SS line for the chip LOW to enable it.

digitalWrite(chipSelectPin, LOW);

After that, the first configuration byte is sent:

Images

The analog data will not start arriving until the second configuration byte is sent. The 10 bits of data from the ADC are split across 2 bytes, so to flush out the remaining data, a call is made to “transfer” sending a byte load of zeros.

The SS output is now set HIGH as the communication is now complete.

Finally, the actual 10-bit analog reading value is calculated using the following line:

Images

Each of the 2 bytes has 5 of the 10 bytes of data in it. The first byte contains these bits in its least significant 5 bits. All the bits apart from those 5 are masked out and shifted five positions up in the 16-bit int. The lower byte contains the remainder of the reading in its most significant five digits. These must be masked and shifted right by three bit positions before they can also be added into the 16-bit int.

To test this, open the Serial Monitor. You should see some data appear. If you sweep the slider of the pot clockwise from 0 to 5V, you should see something similar to what’s shown in Figure 11-4. The first two binary numbers are the 2 bytes from the MCP3008 and the final decimal number is the analog reading between 0 and 1023.

Images

Figure 11-4   Viewing the messages in binary.

Summary

Interfacing with SPI when no library is available is by no means easy. You will sometimes need to perform a little trial and error to get things going. As with any type of debugging, always start by gathering evidence and examining the data that you are receiving. You will slowly get a picture of what is happening and then be able to tailor your code to produce the desired results.

The next chapter examines the final interface standard supported by the Arduino, that of TTL Serial. This standard is a point-to-point interface rather than a bus, but nonetheless a much-used and handy mechanism for sending and receiving data.