Lab: SPI Communication With A Digital Potentiometer

Introduction

In this lab, you’ll see synchronous serial communication in action using the Serial Peripheral Interface (SPI) protocol. You’ll communicate with a digital potentiometer chip from a microcontroller. You’ll use the digital pot to control the loudness of a speaker playing a tone from the microcontroller.

Related videos: Intro to Synchronous Serial, SPI

What You’ll Need to Know

To get the most out of this Lab, you should be familiar with the basics of programming an Arduino microcontroller. If you’re not, review the Digital Input and Output Lab, and perhaps the Getting Started with Arduino guide. You should also understand asynchronous serial communication and how it differs from synchronous serial communication.

Things You’ll Need

Three 22AWG solid core hookup wires. Each is about 6cm long. The top one is black; the middle one is red; the bottom one is blue. All three have stripped ends, approximately 4 to 5mm on each end.
22AWG solid core hookup
An Arduino Uno. The USB connector is facing to the left, so that the digital pins are on the top of the image, and the analog pins are on the bottom.
An Arduino
This component is a small rectangle with several pins on each of the two long sides. There is a small round depression on the upper left corner of the rectangle, and a semicircular dip in the middle of the top.
Digital Potentiometer
An 8 ohm speaker with 2 wires solder to the speakers leads
An 8 ohm speaker
Resistors. Shown here are 220-ohm resistors. You can tell this because they have two red and one brown band, followed by a gold band.
Resistors. Shown here are 220-ohm resistors. You can tell this because they have two red and one brown band, followed by a gold band.

If you did the tone lab on this site, you know that it’s not possible to vary the loudness of a tone generated from a microcontroller. In order to change the loudness, you’d need to change the voltage of the tone pin. If you put a potentiometer in series with the speaker as shown below, you can modify the tone’s loudness:

Electrical diagram of speaker. The speaker's negative pin is connected to ground and the positive pin is connected to a 10k potentiometer

 

This circuit requires that you modify the loudness manually, by turning the pot. But with a digital potentiometer, you can modify the loudness from your program.

Connect the digital potentiometer

The digital potentiometer used in this lab,  an Analog Devices AD5206, is an integrated circuit (IC) that can perform one specific function: it has six potentiometers that you can control. Each of its pins has a specific function, as shown below. It’s in a Dual Inline Package, or DIP. DIP package ICs typically have their pins enumerated in a U-shape, starting with pin 1 on the top left, and the highest number pin on the top right.

AD5206 Digital Potentiometer. The pins on the left side of the chip are pins for potentiometer 6, followed by pins for power and SPI connections, followed by pins for potentiometer 5. The potentiometer pins are labeled A, W, and B. The power and SPI connection pins are labeled Ground, CS, Vdd, SDI, CLK, and Vss. The pins on the right side of the chip are for potentiometers 4, 2, 1, and 3. Each of the potentiometer pins are labeled A, W, and B.
AD5206 Digital Potentiometer.

The potentiometers in this chip are labeled A1, B1, W1 through A6, B6, W6. The A and B pins are the  fixed end pins of the potentiometer, and if you measure resistance across them with a multimeter, you’ll measure 10 kilohms. The W pins are the wipers, and the resistance is programmable via the SPI connection. You can use any of these potentiometers in a circuit just like you would a regular potentiometer.

First, connect the digital potentiometer’s power and ground connections, and the connections for clock, chip select, and serial data in:

Schematic of an Arduino attached to a AD5206 Potentiometer. The Arduino's ground pin is attached the the potentiometer's A5, Vss, and Ground pins. The Arduino's D10, D11, and D13 pins are attached to the potentiometer's CS, SDI, and CLK pins. The potentiometer's Vdd pin is connected to 5 volts.
Schematic of an Arduino attached to a AD5206 Potentiometer.
Schematic of an Arduino attached to a AD5206 Potentiometer. The Arduino's ground pin is attached the the potentiometer's A5, Vss, and Ground pins. The Arduino's D10, D11, and D13 pins are attached to the potentiometer's CS, SDI, and CLK pins. The potentiometer's Vdd pin is connected to 5 volts.
Schematic of an Arduino attached to a AD5206 Potentiometer.

Next, add a speaker to the wiper of the fifth channel of the digital pot. Include a 100-ohm resistor in series with the speaker. Then connect the A5 pin to digital pin 9 of the Arduino, which you’ll use to generate a tone. You can use any channel of the digital pot if you want, but the code below uses the fifth channel.

Schematic of an Arduino attached to a AD5206 Potentiometer and a speaker. The Arduino's ground pin is attached the the potentiometer's A5, Vss, and Ground pins. The Arduino's D10, D11, and D13 pins are attached to the potentiometer's CS, SDI, and CLK pins. The potentiometer's Vdd pin is connected to 5 volts. The Arduino's D9 pin is connected to the negative terminal of a speaker. The speaker's positive terminal is connected to a 100 Ohm resistor, which is connected to the potentiometer's W5 pin.
Schematic of an Arduino attached to a AD5206 Potentiometer and a speaker.
Schematic of an Arduino attached to a AD5206 Potentiometer and a speaker. The Arduino's ground pin is attached the the potentiometer's A5, Vss, and Ground pins. The Arduino's D10, D11, and D13 pins are attached to the potentiometer's CS, SDI, and CLK pins. The potentiometer's Vdd pin is connected to 5 volts. The Arduino's D9 pin is connected to the negative terminal of a speaker. The speaker's positive terminal is connected to a 100 Ohm resistor, which is connected to the potentiometer's W5 pin.
Schematic of an Arduino attached to a AD5206 Potentiometer and a speaker.

The circuit is now complete, and you’re ready to write a program to control it.

Program the Microcontroller

The AD5206 has a simple command protocol, detailed in the “Operation” section of the data sheet. You send two bytes: the channel number you wish to control, and the level for the channel. Setting a channel to 0 makes the resistance between the wiper and the B pin 0 ohms, and setting it to 255 makes the resistance between the wiper and B 10 kilohms. There’s one slightly confusing issue: the channels are addressed as numbers 0 through 5 in code, yet numbered 1 through 6 on the pin diagram. Just remember that the first channel is channel 0, the second channel is channel 1, and so forth (just like array elements in most programming languages).

You can find Arduino-compatible libraries for many SPI devices that use the SPI library, but never expose it directly in the API. Instead, these libraries will include device-specific commands that use the SPI library to transfer data. Because the AD5206’s protocol is so simple, however, you don’t need a device-specific library, you can just use the SPI library.

At the beginning of your code, include the SPI library and set up a pin number for the chip select pin. The other SPI pins are set by the microcontroller you’re using (see the Arduino SPI reference page for the pin numbers). Then in the setup, call SPI.begin() to initialize communications:

// include the SPI library:
#include <SPI.h>
const int CSPin = 10;   // chip select pin number

void setup() {
  // initialize SPI:
  SPI.begin();
}

In your main loop, first make a tone on pin 9.  Then make two for loops to fade the loudness up and down. The actual work of controlling the digital potentiometer will be handled by a function you’ll write called digitalPotWrite():

void loop() {
 tone(9, 440); // play a tone on pin 9

 // fade the loudness up:
 for (int loudness = 100; loudness <= 255; loudness++) { digitalPotWrite(4, loudness); delay(20); } delay(1000); // delay 1 second // fade the loudness down: for (int loudness = 255; loudness >= 100; loudness--) {
 digitalPotWrite(4, loudness);
 delay(20);
 }

 delay(1000); // delay 1 second
}

Finally, write the digitalPotWrite() function to control the digital potentiometer via SPI. Because SPI data can go both directions at the same time, i.e. from controller to peripheral and from peripheral to controller, there is a single command, SPI.transfer(), to transfer data instead of the read() and write() commands you’re used to from asynchronous serial communication. In order to put the digital pot in listening mode, you take its chip select pin low, then when you’re finished communicating with it, you take the pin high again:

void digitalPotWrite(int address, int value) {
 // take the SS pin low to select the chip:
 digitalWrite(CSPin, LOW);
 // send in the address and value via SPI:
 SPI.transfer(address);
 SPI.transfer(value);
 // take the SS pin high to de-select the chip:
 digitalWrite(CSPin, HIGH);
}

Once you upload this code, you’re done!  You should hear the tone (middle A, 440Hz) fading in and out. You may have noticed that the for loops for from 100 to 255. The digital pot is changing the resistance across the wiper on the fifth potentiometer (aka channel 4) from approximately 3921 ohms (100/255 * 10k) to 10 kilohms. These are the levels that were determined from experiment to be the edges of audibility. But if your hearing is better, you may try adjusting the lower limit to see if you can hear it at a lower level.

Conclusion

This is a very simple use of SPI. Data only goes from controller peripheral in this example, and only two bytes are transferred. However, it gives you an indication of how the process works. Each SPI-based device will have its own command protocol, and data will be transferred from controller to peripheral (and vice versa) using the SPI transfer command.  To open communications with a given SPI device, you take its chip select pin low, and to close communications, you take the chip select pin low again. This same procedure will work on all SPI devices once you know the command protocol.  Keep in mind that most device-specific libraries will handle the SPI communication for you without you having to see it, just like the digitalPotWrite() command does in this example.

For another example of SPI in action with the AD5206, see the digital pot example on the Arduino site. There’s also an example with a barometric pressure sensor. The SD card library, which allows you to communicate with SD cards, uses the SPI library as well.

Originally written on November 2, 2014 by Tom Igoe
Last modified on September 11, 2018 by Jim Schmitz