Beginner questions: Using the MCP4261 library

Hi,

Me and another guy are currently doing a project in mechatronics class, where we will control an RC car via PC. We have chosen to replace the analog potentiometers (steering and throttle inputs) in the RC transmitter with digital potentiometers, more exactly MCP4161-103E/P 10kohms. We chose this one because it is compatible with the MCP4261 library found on the Arduino playground.

While waiting for the pots to be delivered, I was thinking about setting up the code. We don't really have any experience with the Arduino and only limited experince with coding, so we will need some help to get started.

  • First, it says on the Arduino playground (http://arduino.cc/playground/Code/Mcp4261) that the Spi library by Cam Thompson is required, but the download link only sends me to a page telling me that an SPI library is already included in the Arduino software. On the other hand, the readme for the MCP4261 library says that the official SPI.h library wont work. So where can I download the Spi library by Cam Thompson? I tried searching a bit but could not find it.

  • Second, where do I put the MCP4261 library files (Mcp4261.cpp and Mcp4261.h)? I'm guessing they go into the .../arduino/libraries folder, should I create a subfolder for them there?

So can anyone help us out with this please?

I'm guessing they go into the .../arduino/libraries folder

That depends on what '...' represents. If it is where you have the IDE installed, then that's not the right place. Downloaded libraries go into a libraries subfolder of your sketchbook folder, which is where your sketches are saved. It is usually in your user folder.

See http://arduino.cc/en/Guide/Environment#libraries

Thanks for the link, quite a bit of useful info there. I actually thought they would go where the IDE is installed.

Just to make it clear, I go into the sketchbook folder (Documents/Arduino/ in my case) and create subfolders libraries/Mcp4261/ and put the .cpp and .h files there?

That looks ok.

OK so while searching for the Spi.h library I found this thread: http://arduino.cc/forum/index.php?topic=91497.0

Apparently there is an updated version of the Mcp4261 library which uses the standard Arduino SPI.h library. I will try my luck with that one and see if I can manage to compile the example.

I am glad my thread might give you some help! For testing, I used LEDs with a series resistor, on the wipers of the digital pot to show the change in pot value. This allowed easy testing and when you slow the sketch down you can also take voltage readings with your meter.

Hi again guys,

I tried the updated library and the example compiled perfectly. Now we have recieved the digital pots and are about to start hooking them up. That brings up a new question.

The pot in the example is a two-channel one, connected to 3 pins: Slave Clock, SDI and SDO. Now, since we are going to use two single-channel pots independently, I guess each pot should be connected to their own SDI/SDO pins. According to the example, pins 11 and 12 are used. Are there any other pins that are ready to use for our second pot, or do we have to do some declaration?

Look in my code that is modified from the example. I put the pin out and connections in the code. It worked for me.
If you need more information let us know.

// This example demonstrates control over SPI to the Microchip McpDigitalPot Digital potentometer mcp42xxx family
// SPI Pinouts are for Arduino Uno and Arduino Duemilanove board (will differ for Arduino MEGA)

/*mcp42xxx family
///////////////////////commands from data sheet////////////////
00000000 to write to pot 2
00010000 to write to pot 1 /////////////32?
11 Read Data 16-Bits
00 Write Data 16-Bits
01 Increment 8-Bits
10 Decrement 8-Bits 
////////////////////////////Chip pins////////////////////////
1  -CS to DIGITAL PIN 10 (same as latch)
2  -SCK to DIGITAL PIN 13 (clock)
3  -SDI to DIGITAL PIN 11 (data IN)
4  -VSS GROUND
5  -P1B GROUND
6  -P1W (channel 2 wiper)
7  -P1A +5V
8  -P0A +5v
9  -P0W (channel 1 wiper)
10 -P0B GROUND
11 -WP on mine +5v, good luck
12 -SHDN +5v
13 -SDO to DIGITAL PIN 12 (data OUT)
14 -VDD +5v
//////////////////////////////////////////information sources///////////////////////////
Base code and library came from here https://github.com/teabot/McpDigitalPot
http://pdf1.alldatasheet.com/datasheet-pdf/view/306650/MICROCHIP/MCP4251-502E/MS.html
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1269918888/all
*/
#include <SPI.h>

// McpDigitalPot library available from https://github.com/dreamcat4/McpDigitalPot
#include <McpDigitalPot.h>

// Wire up the SPI Interface common lines:
// #define SPI_CS               10 //arduino   <->   SPI Chip Select           -> CS  (Pin 01 on McpDigitalPot DIP)
// #define SPI_CLOCK            13 //arduino   <->   SPI Slave Clock Input     -> SCK (Pin 02 on McpDigitalPot DIP)
// #define SPI_MOSI             11 //arduino   <->   SPI Master Out Slave In   -> SDI (Pin 03 on McpDigitalPot DIP)
// #define SPI_MISO             12 //arduino   <->   SPI Master In Slave Out   -> SDO (Pin 13 on McpDigitalPot DIP)

// Then choose any other free pin as the Slave Select (pin 10 if the default but doesnt have to be)
#define MCP_DIGITAL_POT_SLAVE_SELECT_PIN 10 //arduino   <->   Chip Select               -> CS  (Pin 01 on McpDigitalPot DIP)

// Its recommended to measure the rated end-end resistance (terminal A to terminal B)
// Because this can vary by a large margin, up to -+ 20%. And temperature variations.
float rAB_ohms = 10000.00; // 10k Ohm
float rW_ohms  = 94;       // 94 Ohm
// Instantiate McpDigitalPot object, with default rW (=117.5 ohm, its typical resistance)
//McpDigitalPot digitalPot = McpDigitalPot( MCP_DIGITAL_POT_SLAVE_SELECT_PIN, rAB_ohms );

// rW - Wiper resistance. This is a small additional constant. To measure it
// use the example, setup(). Required for accurate calculations (to nearest ohm)
// Datasheet Page 5, gives typical values MIN=75ohm, MAX @5v=160ohm,@2.7v=300ohm
// Usually rW should be somewhere between 100 and 150 ohms.
// Instantiate McpDigitalPot object, after measuring the real rW wiper resistance
 McpDigitalPot digitalPot = McpDigitalPot( MCP_DIGITAL_POT_SLAVE_SELECT_PIN, rAB_ohms, rW_ohms );

void setup()
{
   Serial.begin(57600);
  // initialize SPI:
  SPI.begin(); 
  
  // First measure the the wiper resistance, called rW
  digitalPot.setPosition(0, 0); // rAW = rW_ohms
  digitalPot.setPosition(1, 0); // rAW = rW_ohms
  //delay(5000);
  
  // (optional)
  // Scale to 100.0 for a percentage, or 1.0 for a fraction
  // Eg if scale=100, then setResistance(0, 100) = max rAW resistance
  // Eg    scale=1.0, then setResistance(0, 1.0) = max rAW resistance
  // digitalPot.scale = 1.0;

  digitalPot.scale = 100.0; // For the timeout example, below

  // digitalPot.setResistance(0, 40); // set pot0 rAW = 40% of max value
  // digitalPot.setResistance(1, 80); // set pot1 rAW = 80% of max value
  // 
  // delay(5000);
  // 
  // digitalPot.setResistance(0, 5);  // set pot0 rAW =  5% of max value
  // digitalPot.setResistance(1, 50); // set pot1 rAW = 50% of max value

  // Go back to using ohms
  // digitalPot.scale = McpDigitalPot.rAB_ohms;
}




// Cycle the wipers around at 20% increments, changing every 2 seconds
long timeoutInterval = 200;
long previousMillis = 0;
float counter = 0.0;

void timeout()
{
  if(counter > 100.0)
    counter = 0.0;

  // These resistances are just percentages of 100
  digitalPot.setResistance(0, 100 - counter);
  digitalPot.setResistance(1,  counter); // Invert the wiper1

  counter += 1.0;
}

void loop()
{
    
    
  if (  millis() - previousMillis > timeoutInterval )
  {
    timeout();
    previousMillis = millis();
    Serial.print((counter - 20) * 100);
    Serial.println("ohms");
      }
  // Loop.
}

Oops, sorry, so there are definitions for the pins, I must have missed it because those lines where commented out. So I guess those are the default pins because their definition is commented out?

So if I got this right, I need to declare 2 more pins for the other pot. The SPI_CLOCK is the same for both, and the SPI_CS is also the same since both pots are single-channel. Something like this maybe? (Pins 11 and 12 for SPI/SPO on pot 1 and pins 6 and 7 for pot 2)

#define SPI_CS               10 //arduino   <->   SPI Chip Select           -> CS  (Pin 01 on McpDigitalPot DIP)
#define SPI_CLOCK            13 //arduino   <->   SPI Slave Clock Input     -> SCK (Pin 02 on McpDigitalPot DIP)
#define SPI_MOSI_1             11 //arduino   <->   SPI Master Out Slave In   -> SDI (Pin 03 on McpDigitalPot DIP)
#define SPI_MISO_1             12 //arduino   <->   SPI Master In Slave Out   -> SDO (Pin 13 on McpDigitalPot DIP)
#define SPI_MOSI_2             6 //arduino   <->   SPI Master Out Slave In   -> SDI (Pin 03 on McpDigitalPot DIP)
#define SPI_MISO_2             7 //arduino   <->   SPI Master In Slave Out   -> SDO (Pin 13 on McpDigitalPot DIP)

CS == Chip Select. Each chip has to have one output pin from the Arduino. You can use pin 10 for one chip and any other unused pin for the second chip.

The connections to MISO, MOSI, and SCK are shared between all SPI Chips. You can have multiple SPI chips connected to the Arduino and the CS pin selects which chip is being used.

My sketch is written for a single chip with 2 channels. It has channel 0 and channel 1. If you are using 2 single channel chips you will have to modify the code some for you purpose.

Here is the Define for the first chip.

#define MCP_DIGITAL_POT_SLAVE_SELECT_PIN 10 //arduino   <->   Chip Select

To add a second chip you would #define a second output pin. I named it with a 2 at the end and chose pin 9 for output. You could have a different pin for chip 2.

#define MCP_DIGITAL_POT_SLAVE_SELECT_PIN2 9 //arduino   <->   Chip Select

You will probably need other code changes but, that is all I can do for you at the moment.

The wiring I am tring to refer to is found in this link: http://tronixstuff.wordpress.com/2011/06/15/tutorial-arduino-and-the-spi-bus-part-ii/

You may also find information about your digital pot there but, you will need to look.

cyclegadget: The wiring I am tring to refer to is found in this link: http://tronixstuff.wordpress.com/2011/06/15/tutorial-arduino-and-the-spi-bus-part-ii/

You may also find information about your digital pot there but, you will need to look.

Well now I think I finally got it. That was some useful info right there, thanks a lot. I had not really understood the function of the slave selection pin until now.

I have modified the example into a test code with 2 chip selection pins. In the setup part I define these pins as outputs, and then set their values to HIGH. In the I loop set the resistance of each pot in turn by setting its selection pin to LOW, setting a value with the setResistance() function, and then setting the selection pin back to HIGH. If I understood this correctly, it should work. I'll try it out tomorrow.

With the advice you gave me I got it working easily. On the single-channel version of the chip the SDI and SDO is multiplexed on one pin, but we only connected the SDI pin on the Arduino to it and left the SDO pin on the Arduino unconnected (according to the advice I got this is okay since we don’t want to read anything from the chip anyway).

However to get the same response (steering and throttle response of the RC car) as with the analog potentiometer, we have to use a much wider resistance range with the digital pots than the range provided by the analog pot, which I found strange. Isn’t the digital pot to supposed to work pretty much exactly the same as the analog once it is set up? As the transmitters pulse signal going through the pot is only 50 Hz, I don’t think the frequency should cause problems.

Anyway, for now we managed to tune the response by trying out different values. So it works, but not exactly like I expected it to.

Part of your issue may be wiper resistance. Basically the wiper on the chip has a given amount of resistance all of the time. On my chip it was around 74 ohms. Basically, you have to set the pot to minimum ohms and then check what the reading is. You would expect to get 0 ohms but, it will be somewhere between 0 and 100, every chip is a little different.

Hi,
I have been using the MCP4261 library successfully to set the wiper0 and wiper1, so all is good in that area. In fact, I have the SPI running over RS485 Full-Duplex at 4Mbs over 3 meter RJ45 cabling.
My problem has to do with the READ function of the chip.
I send a 16bit word in two 8 bit sends, as in the “send16BitCmd(uint8_t addr, uint8_t cmd, int data)” function in the Library.
I had to add the below function to the library, to send the #define CMD_READ_DATA 0b11 value to tell the MCP4261 to READ.

void MCP4261::readWiper(int wiper, int value) {
  uint8_t wiperAddr;
  //int retData;
  if ((value >= 0) && (value <= 256)) {
    wiperAddr = getWiperAddr(wiper);
    send16BitCmd(wiperAddr, CMD_READ_DATA, value);
  }
  //return retData;
}

This all seems to send out the correct bits, as…
outByte0 = b0001 1100 (for Wiper address 1, and the 1100 sets the mode to READ.
outByte1 = b0000 0000 (really, i think that you can send anything for the data Byte)
each Byte gets sent out by SPI.transfer(), again using the “send16BitCmd(uint8_t addr, uint8_t cmd, int data)” function.

Now the problem comes in as I expect that the Return values, inByte1 and inByte0, from "inByte1 = SPI.transfer(outByte1);" calls in the library will return the previously written Bytes.

It kind of does, but I ALWAYS get a return from inByte0 = 1111 1111
and inByte1 seems to be shifted by 1 bit lopping off the LSB.

Can anyone help me to understand where the LSB has gone in my READ?

I basically am doing this to check that the transmission was taken, as an error check.

Thank for any help.

Update…
So, I can get the LSB if I brute-force the method as below…
But I would rather use the SPI library, as I suspect that it is more efficient than this approach.
BTW, as you can see, this is just a quick test with the Pot addrs and cmd hard coded into the commandbits parameter.

    Serial.println("***********readWiper1 brut force******************");
     //set pin modes 
     pinMode(DIGIPOT1_CS, OUTPUT); 
     pinMode(DATAOUT, OUTPUT); 
     pinMode(DATAIN, INPUT); 
     pinMode(SPICLOCK, OUTPUT); 
     //disable device to start with 
     digitalWrite(DIGIPOT1_CS,HIGH); 
     digitalWrite(DATAOUT,LOW); 
     digitalWrite(SPICLOCK,LOW); 
    
    digitalWrite(DIGIPOT1_CS,LOW); //Enable Pot1
    byte commandbits = B00011100; //command bits - XXXX address, YY cmd, ZZ Error & data
    for (int i=7; i>=0; i--){
      digitalWrite(DATAOUT,(unsigned int)commandbits>>i);
      //cycle clock
      digitalWrite(SPICLOCK,HIGH);
      digitalWrite(SPICLOCK,LOW);    
    }
    byte ReadPotValue = 0b00000000;
    for (int i=7; i>=0; i--){
      ReadPotValue += digitalRead(DATAIN)<<i;
      //cycle clock
      digitalWrite(SPICLOCK,HIGH);
      digitalWrite(SPICLOCK,LOW);    
    }
    Serial.print("ReadPotValue = ");
    Serial.println(ReadPotValue, BIN);
    Serial.print("ReadPotValue = ");
    Serial.println(ReadPotValue);

But, I would still like to know why SPI.transfer() return parameter returns a value that is shifted right 1 bit and removes the LSB.

Any thoughts?

Thanks,