Go Down

Topic: SPI HOW-TO: MCP42xxx digital pot (Read 2553 times) previous topic - next topic

JimboZA

The sketch shown below is based on that of Tom Igoe (in turn acknowledging Heather Dewey-Hagborg) in this SPI tutorial.

The AD5206 in that tutorial is a 6-channel digital pot; I used a 2-channel one from Microchip, the MCP42XXX. The xxx indicates the maximum resistance in kilo-ohms, of 010, 050 or 100.

Although both products are SPI compliant digital pots, they differ subtly in the way they implement their commands, hence this how-to which shows how to handle the MCP version. The AD product receives two bytes: the first is the channel number in the 6-channel pot and the second is the 0-255 value to which that pot must be set. Two consecutive SPI.transfer commands in the sketch send those two bytes, and it seems the AD5206 is predisposed to receiving two bytes as a channel and data.

The MCP product, however, implements a Command Byte: the first byte sent is not merely the channel number, but also tells the chip what to do. See the datasheet for full details but for our purposes a Command Byte XX01XXpp is what we need to know:

XX = don't care
01 = the next byte is data (which seems to be pre-ordained in the AD product)
XX = don't care
pp = potentiometer to be accessed:
    00 = use neither, dummy command
    01 = use pot 0
    10 = use pot 1
    11 = use both pots

So taking the simple view that the X's will be 0, and discarding the dummy case, the Command Byte we would send would be:

00010001 = decimal 17 for pot0
00010010 = decimal 18 for pot1
00010011 = decimal 19 for both pots.

So the first SPI.transfer in the sketch below would send a 17, 18 or 19 rather than simply the raw channel; the second SPI-transfer sends the value.

The MCP product is physically different from the AD one, so here are the pin connections:

1 => Arduino 10 SS
2 => Arduino 13 SCK
3 => Arduino 11 MOSI
4 => ground
5 => ground
6 => LED with series resistor
7 => 5V
8 => 5V
9 => LED with series resistor
10 => ground
11 => not used here
12 => not used here
13 => not used here
14 => 5V

Here's a sketch, the behaviour of which is to cycle one LED's brightness, then the other, then both.

E&OE, YMMV

Code: [Select]
/*
  Digital Pot Control
 
  Based on the original sketch for AD5206....
 
  This example controls a Microchip digital potentiometer.
  The MCP42 has 2 potentiometer channels. Each channel's pins are labeled
  PAx - connect this to voltage
  PWx - this is the pot's wiper, which changes when you set it
  PBx - connect this to ground.

The MCP42 is SPI-compatible,and to command it, you send two bytes:

The first byte is the Command Byte which has this format when
the next byte is to be data: XX01XXpp
note these bits ...............^^....  the 01 means the next byte is data
(where pp = potentiometer selection, X= don't care)
pp= 00 = dummy code, no pot selected
pp= 01 = pot0
pp= 10 = pot1
pp= 11 = both pots

Simplest case is to have X= 0 so the Command Byte will be:
pp= 00: 00010000 = 16
pp= 01: 00010001 = 17
pp= 10: 00010010 = 18
pp= 11: 00010011 = 19

The second byte is the resistance value for the channel (0 - 255). 

The circuit:
  * All PA pins of MCP42 connected to +5V
  * All PB pins of MCP42 connected to ground
  * An LED and a 220-ohm resisor in series connected from each PW pin to ground
  * CS - to digital pin 10  (SS pin)
  * SI - to digital pin 11 (MOSI pin)
  * SCK - to digital pin 13 (SCK pin)

created 10 Aug 2010
by Tom Igoe

Thanks to Heather Dewey-Hagborg for the original tutorial, 2005

Version for MCP42xx April 2013, Jim Brown

*/


// include the SPI library:
#include <SPI.h>


// set pin 10 as the slave select for the digital pot:
const int slaveSelectPin = 10;

void setup() {
  // set the slaveSelectPin as an output:
  pinMode (slaveSelectPin, OUTPUT);
  // initialize SPI:
  SPI.begin();
}

void loop() {
  // go through pp=01, 10, 11  for pot0, pot1 and both together of the digital pot:
  for (int CommandByte = 17; CommandByte < 20; CommandByte++) {    // 17, 18 and 19
    // change the resistance on this pot from min to max:
    for (int level = 0; level < 255; level++) {
      digitalPotWrite(CommandByte, level);
      delay(10);
    }
    // wait at the top:
    delay(100);
    // change the resistance on this channel from max to min:
    for (int level = 0; level < 255; level++) {
      digitalPotWrite(CommandByte, 255 - level);
      delay(10);
    }
  }

}

int digitalPotWrite(int CommandByte, int value) {
  // take the SS pin low to select the chip:
  digitalWrite(slaveSelectPin,LOW);
  //  send in the address and value via SPI:
  SPI.transfer(CommandByte);
  SPI.transfer(value);
  // take the SS pin high to de-select the chip:
  digitalWrite(slaveSelectPin,HIGH);
}
meArm build blog:     http://jimbozamearm.blogspot.com/
Please don't PM for technical advice.
Beware the XY Problem..... http://xyproblem.info/

cdb0ewm

Thank you for this sketch

I have wired up my bread board, mcp4210 and leds as you outline.

Unfortunately, while the sketch uploads to my Uno correctly, my leds do not flash.

I have checking the wiring several times, but no success

I know this is an opened ended questions but I'd appreciate your assistance

edmundsj

#2
Mar 19, 2015, 08:09 am Last Edit: Mar 19, 2015, 08:25 am by edmundsj
EDIT: Ok, so I finally began to understand the datasheet for this digipot, and it appears the commandByte is implemented slightly differently, where the first four bits of the commandByte are the address and the last four bits are the command and some extra data slots. So channel 0 is actually decimal 0 for the commandByte, and channel 1 is decimal 16. (or so I think). Will update when I'm actually near my device to test this out.

So this setup also did not work for me, I'm using the mcp-4251, I will post the schematic soon, and I copy-pasted your code with some slight modifications. If I set the CommandByte to loop through 0-1', then only channel 0 works, and if I set the CommandByte to loop through 16-19, only channel 1 works. I can't get them both to work simultaneously.

One question I had was why are you using integers for digitalPotWrite if the digital potentiometer only wants to recieve a single command byte and a single value byte? Wouldn't it be more appropriate to use a 'char' datatype since it is actually only one byte? (while an integer is typically 4). Or is this automatically corrected for by the SPI library?

The datasheet for the mcp-4251 can be found here: http://ww1.microchip.com/downloads/en/DeviceDoc/22060b.pdf

Go Up