Learning   Examples | Foundations | Hacking | Links

Controlling a Digital Potentiometer Using SPI

In this tutorial you will learn how to control the AD5206 digital potentiometer using Serial Peripheral Interface (SPI). For an explanation of SPI see the SPI EEPROM tutorial. Digital potentiometers are useful when you need to vary the resistance in a ciruit electronically rather than by hand. Example applications include LED dimming, audio signal conditioning and tone generation. In this example we will use a six channel digital potentiometer to control the brightness of six LEDs. The steps we will cover for implementing SPI communication can be modified for use with most other SPI devices.

Materials Needed:

  • AD5206 Digital Potentiometer
  • Arduino Microcontroller Module
  • 6 Light Emitting Diodes (LEDs)
  • Hookup Wire

Introduction to the AD5206 Digital Potentiometer

The AD5206 is a 6 channel digital potentiometer. This means it has six variable resistors (potentiometers) built in for individual electronic control. There are three pins on the chip for each of the six internal variable resistors, and they can be interfaced with just as you would use a mechanical potentiometer. The individual variable resistor pins are labeled Ax, Bx and Wx, ie. A1, B1 and W1.

For example, in this tutorial we will be using each variable resistor as a voltage divider by pulling one side pin (pin B) high, pulling another side pin (pin A) low and taking the variable voltage output of the center pin (Wiper).

The AD5206 is digitally controlled using SPI. The device is enabled by pulling the Chip Select (CS) pin low. Instructions are sent as 11 bit operational codes (opcodes) with the three most significant bits (11-9) defining the address of which potentiometer to adjust and the eight least significant bits (8-1) defining what value to set that potentiometer to from 0-255. Data is shifted in Most Significant Bit (MSB) first on the rising edge of the data clock. The data clock is idle when low, and the interface runs much faster than the Arduino, so we don't need to worry about pre-scaling to slow down the transmission.

Prepare the Breadboard

Insert the AD5206 chip into the breadboard. Connect 5V power and ground from the breadboard to 5V power and ground from the microcontroller. Connect AD5206 pins 3, 6, 10, 13, 16, 21 and 24 to 5v and pins 1, 4, 9, 12, 15, 18, 19, and 22 to ground. We are connecting all the A pins to ground and all of the B pins to 5v to create 6 voltage dividers.

Connect AD5206 pin 5 to Arduino pin 10 (Slave Select - SS), AD5206 pin 7 to Arduino pin 11 (Master Out Slave In - MOSI), and AD5206 pin 8 to Arduino pin 13 (Serial Clock - SCK).

Finally, connect an LED between each Wiper pin (AD5206 pins 2, 11, 14, 17, 20 and 23) and ground so that the long pin of the LED connects to the wiper and the short pin, or flat side of the LED connects to ground.

Program the Arduino

Now we will write the code to enable SPI control of the AD5206. This program will sequentially pulse each LED on and then fade it out gradually. This is accomplished in the main loop of the program by individually changing the resistance of each potentiometer from full off to full on over its full range of 255 steps.

We will walk through the code in small sections.

We define the pins we will be using for our SPI connection, DATAOUT, DATAIN, SPICLOCK and SLAVESELECT. Although we are not reading any data back out of the AD5206 in this program, pin 12 is attached to the builtin SPI so it is best not to use it for other programming functions to avoid any possible errors:

#define DATAOUT 11//MOSI
#define DATAIN 12//MISO - not used, but part of builtin SPI
#define SPICLOCK  13//sck
#define SLAVESELECT 10//ss

Next we allocate variables to store resistance values and address values for the potentiometers:

byte pot=0;
byte resistance=0;

First we set our input and output pin modes and set the SLAVESELECT line high to start. This deselects the device and avoids any false transmission messages due to line noise:

void setup()
{
  byte clr;
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT,OUTPUT);
  digitalWrite(SLAVESELECT,HIGH); //disable device

Now we set the SPI Control register (SPCR) to the binary value 01010000. In the control register each bit sets a different functionality. The eighth bit disables the SPI interrupt, the seventh bit enables the SPI, the sixth bit chooses transmission with the most significant bit going first, the fifth bit puts the Arduino in Master mode, the fourth bit sets the data clock idle when it is low, the third bit sets the SPI to sample data on the rising edge of the data clock, and the second and first bits set the speed of the SPI to system speed / 4 (the fastest). After setting our control register up we read the SPI status register (SPSR) and data register (SPDR) in to the junk clr variable to clear out any spurious data from past runs:

  SPCR = (1<<SPE)|(1<<MSTR);
  clr=SPSR;
  clr=SPDR;
  delay(10);

We conclude the setup function by setting all the potentiometers to full on resistance states thereby turning the LEDs off:

  for (i=0;i<6;i++)
  {
    write_pot(i,255);
  }
}

In our main loop we iterate through each resistance value (0-255) for each potentiometer address (0-5). We pause for 10 milliseconds each iteration to make the steps visible. This causes the LEDs to sequentially flash on brightly and then fade out slowly:

void loop()
{
     write_pot(pot,resistance);
     delay(10);
     resistance++;
     if (resistance==255)
     {
       pot++;
     }
     if (pot==6)
     {
       pot=0;
     }
}

The spi_transfer function loads the output data into the data transmission register, thus starting the SPI transmission. It polls a bit to the SPI Status register (SPSR) to detect when the transmission is complete using a bit mask, SPIF. An explanation of bit masks can be found here. It then returns any data that has been shifted in to the data register by the EEPROM:

char spi_transfer(volatile char data)
{
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  return SPDR;                    // return the received byte
}

The write_pot function allows us to control the individual potentiometers. We set the SLAVESELECT line low to enable the device. Then we transfer the address byte followed by the resistance value byte. Finally, we set the SLAVSELECT line high again to release the chip and signal the end of our data transfer.

byte write_pot(int address, int value)
{
  digitalWrite(SLAVESELECT,LOW);
  //2 byte opcode
  spi_transfer(address);
  spi_transfer(value);
  digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer
}

LED video

For easy copy and pasting the full program text of this tutorial is below:

#define DATAOUT 11//MOSI
#define DATAIN 12//MISO - not used, but part of builtin SPI
#define SPICLOCK  13//sck
#define SLAVESELECT 10//ss

byte pot=0;
byte resistance=0;

char spi_transfer(volatile char data)
{
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  return SPDR;                    // return the received byte
}

void setup()
{
  byte i;
  byte clr;
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT,OUTPUT);
  digitalWrite(SLAVESELECT,HIGH); //disable device
  // SPCR = 01010000
  //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
  //sample on leading edge of clk,system clock/4 (fastest)
  SPCR = (1<<SPE)|(1<<MSTR);
  clr=SPSR;
  clr=SPDR;
  delay(10);
  for (i=0;i<6;i++)
  {
    write_pot(i,255);
  }
}

byte write_pot(int address, int value)
{
  digitalWrite(SLAVESELECT,LOW);
  //2 byte opcode
  spi_transfer(address);
  spi_transfer(value);
  digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer
}

void loop()
{
     write_pot(pot,resistance);
     delay(10);
     resistance++;
     if (resistance==255)
     {
       pot++;
     }
     if (pot==6)
     {
       pot=0;
     }
}

code, tutorial and photos by Heather Dewey-Hagborg