In this tutorial, we will learn what SPI is and how it works. We will also assemble a simple project using an Arduino Uno to control an MCP4131 Digital Potentiometer using SPI communication.

What is SPI?

SPI is the acronym for Serial Parallel Interface. It is a type of synchronous serial communication which means that data sending is timed with clock pulses. SPI communication is useful for short-distances.

Parts of an SPI network

The SPI network consists of a master device and one or more slave devices connected on a bus.

SPI Master Device

The master device sends and receives data from slave devices. There is only one master device in an SPI network.

SPI Slave Device

The slave device receives commands from and sends data to the master device. There can be one or more slave devices in an SPI network.

SPI Bus

The SPI bus refers to the wires that interconnect the master device to the slave devices. Each master and slave device has the following pins: Master Out Slave In (MOSI), Master In Slave Out (MISO), Serial Clock (SCLK), and Slave Select/Chip Select (SS/CS).

  • Master Out Slave In (MOSI) – MOSI pin is the communication path from the master device to the slave device.
  • Master In Slave Out (MISO) – MISO pin is the communication path from the slave device to the master device.
  • Serial Clock (SCLK) – SCLK pin is the communication path for the clock pulse coming from the master device to all slave devices.
  • Slave Select/Chip Select (SS/CS) – SS/CS is the connection used by the master device to inform the slave device that it will send/request data. SS/CS pin’s state should be set to LOW to inform the slave that the master will send/request data. Otherwise, it is always HIGH.

Connecting the Master Device to a Single Slave Device

If there is only one slave device in the network, its connection to the master device is easy and straightforward. The slave device’s pins are connected to the corresponding pins on the master device with the same label. Figure 1 shows an example of this connection.

Sending Data to a Single Slave Device

For the master device to send data to the slave device, it will first need to set the SS/CS pin to LOW and keep it low for the whole duration of data exchange. This will cause the slave device to be ready to receive/send data. After setting the SS/CS pin, the master sends data via MOSI pin and produces clock pulses via the SCLK pin simultaneously.

single-slave-config
Figure 1: Single Slave Connection

Connecting the Master Device to Multiple Slave Devices

For a network that consists of multiple slave devices, SPI connection to the master device can be one of two configurations. These are the Independent Slave Configuration and Daisy Chain Configuration.

Independent Slave Configuration

As seen in Figure 2, the master device has multiple SS/CS pins. One SS/CS pin corresponds to one slave device.

independent-slave-config
Figure 2: Independent Slave Configuration

Sending Data in an Independent Slave Configuration

When a master device needs to communicate with a particular slave device, it pulls the corresponding SS/CS pin LOW. This informs the slave device that the master will send data to it. The master device keeps the SS/CS pin LOW for the whole duration of the data exchange.

After setting the SS/CS pin, the master sends data via the MOSI pin. At the same time, it also sends clock pulses via the SCLK pin. If the master device expects a response from the slave device, it will continue to send additional clock pulses for the data to be received on the MISO pin.

Daisy Chain Configuration

In the Daisy Chain Configuration, the master device only needs one SS/CS pin to communicate with all slave devices. Figure 3 shows an example of the daisy chain configuration.

daisy-chain-config
Figure 3: Daisy Chain Configuration

Sending Data in a Daisy Chain Configuration

In the daisy chain configuration, the master device sends the data and the data overflows from one slave to the next slave in the chain. To initiate communication, the master device first pulls the SS/CS pin to LOW. This will signal the slave devices to be ready to receive data from their respective MOSI pins. Similar to the other SPI network configurations, the SS/CS pins are kept LOW for the whole duration of data transmission.

The master device sends data via its MOSI pin to the first slave device in the chain. At the same time, the master device produces clock pulses via the SCLK pin for proper communication timing. The data overflows from one slave device to the next in this configuration.

The master needs to send enough clock pulses for the data to reach the last slave device in the chain. If a response from a slave device is expected, the master device will continue to send clock pulses until all of the response data is received through its MISO pin.

For further reading about SPI communication, check out our article on Basics of the SPI Communication Protocol.

Using an Arduino Uno to Control an MCP4131 Digital Potentiometer via SPI

Now, let’s demonstrate how we can use an Arduino to communicate via SPI by building a simple project. For this project, we will use an Arduino Uno to control an MCP4131 digital potentiometer IC via SPI.

Hardware Components

We will need the following components for our project.

Arduino Uno SPI Interface

Arduino Uno has built-in hardware support for SPI communication. The SPI’s SS/CS, MOSI, MISO, and SCLK connections are found on digital pins 10 to 13 respectively.

There is another location on the board for the MOSI, MISO, SCLK pins. It is on the ICSP header near the ATMEGA chip. When using SPI, remember that the SPI’s MOSI, MISO, SCLK are on pins 11-13, and on the ICSP header are one and the same. Using the ICSP header doesn’t free up pins 11-13 for other purposes.

arduino-uno-SPI-pins
Figure 4: Arduino Uno SPI pin locations

MCP4131 Digital Potentiometer IC

The MCP4131 IC is basically a resistor network with potentiometer pinouts. Figure 5 shows the pinout diagram of the MCP4131 IC. We can control digitally the output resistance at pin 6 by sending commands via the SPI interface using pins 1,2, and 3.

MCP4131-pinout
Figure 5: MCP4131 Pin Out Diagram

After looking at the pinout diagram above, you might be wondering where are the MOSI, MISO, and SCLK pins. The SPI interface pins are sometimes labeled differently depending on the device manufacturer. Here are the SPI counterparts on the MCP4131 IC:

  • MISO = Serial Data Input (SDI) at pin 3 (combined with MOSI)
  • MOSI = Serial Data Output (SDO) at pin 3 (combined with MISO)
  • SCLK = Serial Clock (SCK) at pin 2

More information about the MCP4131 can be learned by reading its datasheet.

Connection Diagram

After collecting all the components, connect them by following the diagram shown in Figure 6.

We interface the Arduino Uno to the MCP4131 IC via their respective SPI interface pins.

We also connect the Arduino pins GND, A0, +5V to the MCP4131 pins 5, 6, and 7 respectively. This set-up will let us see in the Arduino IDE Serial Monitor the changes in the IC’s resistance output when we send a command via SPI.

arduino-spi-tutorial-wiring-diagram
Figure 6: Connection Diagram

SPI Library

To utilize Arduino Uno’s SPI interface, we need to use the SPI Library. This is a standard library that comes with Arduino IDE software. We add the SPI library by inserting the code #include <SPI.h> at the beginning of our sketch.

Please note that the SPI library only supports the Arduino being the master device. Because of this, pin 10 needs to be configured as OUTPUT to avoid conflicts in the operation of the library. We do this by adding the code pinMode(10,OUTPUT); inside setup().

// Arduino SPI tutorial

#include <SPI.h>

void setup() {
  pinMode(10,OUTPUT); // set SS pin as output pin
}

void loop() {

}

SPI.begin()

After including the SPI library in our code, the next step is to initialize the SPI bus. We do this by using the function SPI.begin()inside setup(). Calling SPI.begin()sets the MOSI, MISO, and SS/CS pin modes to outputs. It also sets MOSI and SCLK to LOW, and sets SS/CS pin to HIGH.

// Arduino SPI tutorial

#include <SPI.h>

void setup() {
  pinMode(10,OUTPUT); // set SS pin as output pin
  SPI.begin();        // Initialize SPI library
}

void loop() {

}

The first thing to do before sending any data to a slave device is to pull the master device’s SS/CS pin connected to the slave device to LOW. In our project, this is Arduino’s pin 10. We pull this pin LOW by adding the code digitalWrite(10, LOW); inside loop(). This will cause the MCP4131 IC to be ready to receive data.

// Arduino SPI tutorial

#include <SPI.h>

void setup() {
  pinMode(10,OUTPUT); // set SS pin as output pin
  SPI.begin();        // Initialize SPI library
}

void loop() {

  digitalWrite(10, LOW);  // set SS pin connected to MCP4131 to LOW
	
}

SPI.transfer()

To send commands to the MCP4131 IC, we will use the function SPI.transfer(val). The val function parameter is the data that we want to send via the SPI. This function also returns the received data.

Based on the MCP4131 datasheet, we need to send the wiper’s register address 0x00 followed by any value between zero and 128 to change the resistance output at pin 6 of the IC.

We will use a FOR loop to iterate the wiper’s value from zero to 128, save this value to the variable wiper_value and send it to MCP4131 IC over SPI.

// Arduino SPI tutorial

#include <SPI.h>

void setup() {
  pinMode(10,OUTPUT); // set SS pin as output pin
  SPI.begin();        // Initialize SPI library
}

void loop() {

  digitalWrite(10, LOW);          // set SS pin connected to MCP4131 to LOW
  
  for(byte wiper_value = 0; wiper_value <= 128; wiper_value++) {

    SPI.transfer(0x00);             // send write command to MCP4131 to write at registry address 0x00
    SPI.transfer(wiper_value);      // send new wiper value

  }
}

After sending the values to MPC4131 IC, we need to pull the SS pin to HIGH to inform the IC that we are finished communicating with it. We do this by using digitalWrite(10,HIGH).

// Arduino SPI tutorial

#include <SPI.h>

void setup() {
  pinMode(10,OUTPUT); // set SS pin as output pin
  SPI.begin();        // Initialize SPI library

}

void loop() {

  digitalWrite(10, LOW);          // set SS pin connected to MCP4131 to LOW
  
  for(byte wiper_value = 0; wiper_value <= 128; wiper_value++) {

    SPI.transfer(0x00);             // send write command to MCP4131 to write at registry address 0x00
    SPI.transfer(wiper_value);      // send new wiper value

  }

  digitalWrite(10, HIGH);         // set SS pin connected to MCP4131 to HIGH
}

To see the effect of our SPI communication with the MCP4131 IC, we will check the value read in the Arduino’s analog input and send it over the serial monitor. We will also add a one-second delay in FOR loop to slow down the iteration of wiper_value.

To do this, we will add Serial.begin(9600) inside setup() and then we will insert the code Serial.println(analogRead(A0))and delay(1000) inside the FOR loop contained in loop().

// Arduino SPI tutorial

#include <SPI.h>

void setup() {
  pinMode(10,OUTPUT); // set SS pin as output pin
  SPI.begin();        // Initialize SPI library
  Serial.begin(9600);
}

void loop() {

  digitalWrite(10, LOW);          // set SS pin connected to MCP4131 to LOW
  
  for(byte wiper_value = 0; wiper_value <= 128; wiper_value++) {

    SPI.transfer(0x00);             // send write command to MCP4131 to write at registry address 0x00
    SPI.transfer(wiper_value);      // send new wiper value

    Serial.println(analogRead(A0)); // read value from analog pin A0 and send to serial
    delay(1000); 
  }

  digitalWrite(10, HIGH);         // set SS pin connected to MCP4131 to HIGH
}

Finally, we have finished our sketch. Save it as arduino_SPI_tutorial.ino and upload it to your board.

Project Testing

Connect the Arduino Uno to your PC via USB and open your Arduino IDE’s Serial Monitor to see the value of A0.

The Arduino continuously sends commands to the MCP4131 IC via the SPI interface to change the IC’s wiper value from zero to 128. After reaching 128, the value goes back to zero, and the process repeats. By changing the IC’s wiper value, the voltage level sensed on the Arduino’s pin A0 changes from zero to 1023.