Using an MCP4131

Hiya,

I want to control a digital potentiometer (MCP4131-103) from my photon, but while I’ve found a few libraries (this one looks quite promising), I’m not sure how to wire it up and what pins to use.

I’m also planning to use an I2C display at the same time - is it possible to run both together?

Cheers for the help :slight_smile:

I2C is very easy to wire - connect the SDA/SCL/GND pins of the two devices, with a pull-up (10K) resistor to 3V3 on both SDA/SCL and you are good to go.
I2C uses device addresses, so as long as the display and digital pot do not share an address, you should be good to go.

@peekay123 Adapted that library for Spark Core so should be able to help you if you get stuck.

The MCP4131 uses SPI and not I2C. You will need to connect one of the Photon SPI ports to the device. Are you using a breakout board for the MCP4131 and if so, which one?

I don’t have a breakout board, just the chip

I’ve had another crack at setting this up - followed this guide.

When using the code and wiring below, the LED doesn’t change at all, it just stays off, so I’ve hit a bit of a wall - any help?


byte address = 0x00;
int CS = A2;

void setup()
{
    pinMode (CS, OUTPUT);
    SPI.begin();
    digitalWrite(CS, LOW);

}

void loop()
{
    for (int i = 0; i <= 128; i++)
    {
    digitalPotWrite(i);
    delay(10);
    }
    delay(500);
    for (int i = 128; i >= 0; i--)
    {
    digitalPotWrite(i);
    delay(10);
    }
}

void digitalPotWrite(int value)
{
    SPI.transfer(address);
    SPI.transfer(value);
}

Edit: LED polarity is wrong in diagram, but right in real life - connecting resistor to 3.3v lights up LED

You are using 10 as CS but have it wired to A2 (which would be 12) - we usually advise to use the pin labels instead of anonymous pin numbers for exactly such reasons.

Whoops, pasted the wrong code, edited OP - I am using A2, still no luck

Can you measure the voltage at P0W?
You could even have a jumper wire to A0 and analogRead(A0) to see whether there is any change at all (I’d remove the LED for that).

BTW, what is the resistance between P0A and P0B?

Might I enquire why you are not using the library to drive this IC via SPI?
Not sure which version of the MCP4131 you have - some are 7 bit and some 8 bit control of the potentiometer. That means for 7bit the maximum value you can send it is 7Fh.

I would take a look at this library for Arduino and you will see what you need to be doing in trying to drive it.

For example; CS is active low so when you use the chip you digitalWrite(A2, LOW); to enable the MCP4131 on the SPI bus and after you have written you set digitalWrite(A2, HIGH); This also means setup() should contain digitalWrite(A2, HIGH); to disable the chip not LOW as you have. Pin A2 is the default CS pin for SPI bus.

Also, I would be using SPI.beginTransaction(); where you set the SPI bus speed and other settings and SPI.endTransaction();

Reading A0 ranges from 2044-2047 - not too sure if that’s a significant change or not.

Don’t have my multimeter on me at the mo, but hooking up the LED to P0A has the LED shining bright, and P0B doesn’t light it up at all, which is what I’d expect. If it’s helpful, I can grab my multimeter some time tomorrow and get some more accurate figures

Had a few issues porting the library to Spark, and the guide I followed seemed to fit the use case perfectly. I’ve added in the begin and end transaction, and logic to manage enabling/disabling the chip, but no luck - cheers for the feedback though :slight_smile:

The datasheet says the 4131 is 7-bit (129 steps) - I’ve got the 10k version

That indicates that the pot is set - and stuck - at the center position.

However, it's no surprise that the LED does not come on for half the range (and possibly more) as you need at least 1.2V for a red LED to even start switching through and you are dividing the 3.3V supply via the internal voltage divider plus the external current limiter.

BTW, the chip only accepts up to 10MHz SPI speed, try setting a lower speed.

Like everyone else and being required to stay in I thought I would have a go at porting to Particle this device driver. I don’t have a MCP4131 so can’t test this code.

MCP4131.h

// Acknowledgement to original author Written by: Derek Duncan 04/03/2018 MIT licence
// Modified for Particle by Armor/W Steen 29/03/20
//
#ifndef MCP4131_H
#define MCP4131_H

#include "Particle.h"

class MCP4131
{
    public:
        MCP4131(uint16_t slave_pin);
        void begin();
        byte readWiper();
        void writeWiper(byte wiperValue);
        void decrementWiper();
        void incrementWiper();

    private:
        const byte ADDRESS_WIPER0 = 0x0;
        const byte ADDRESS_WIPER1 = 0x1;
        const byte COMMAND_MASK = 0x00;
    
        const byte COMMAND_WRITE = 0x0;
        const byte COMMAND_READ = 0x3;
        const byte COMMAND_INCREMENT = 0x1;
        const byte COMMAND_DECREMENT = 0x2;
    
        pin_t slaveSelectPin;
        void enableChip();
        void disableChip();
        byte sendCommand(byte address, char command, byte data);
        void sendCommand(byte address, char command);
};

#endif

MCP4131.cpp

// Acknowledgement to original author Written by: Derek Duncan 04/03/2018 MIT licence
// Modified for Particle by Armor/W Steen 29/03/20
//

#include "MCP4131.h"

// constructor and initialiser for MCP4131 object
MCP4131::MCP4131(uint16_t slavePin)
{
    slaveSelectPin = slavePin;
}

// Begin the SPI comms and initalise the CS pin to disabled
void MCP4131::begin()
{
	pinMode(slaveSelectPin, OUTPUT);
    disableChip();
    SPI.begin();
}

// Read the current value of the potentiometer (0-127)
byte MCP4131::readWiper()
{
    return sendCommand(ADDRESS_WIPER0, COMMAND_READ, 255);
}

// Send potentiometer setting (0-127)
void MCP4131::writeWiper(byte wiperValue)
{  
    sendCommand(ADDRESS_WIPER0, COMMAND_WRITE, wiperValue);
}

// sendCommand address and command with no return
void MCP4131::sendCommand(byte address, char command)
{
    SPI.beginTransaction(__SPISettings(250000, MSBFIRST, SPI_MODE0));     // Adjust SPI settings to fit MCP4131
    enableChip();                                                       // take the CS/SS pin low to select the chip
    byte msb = (address << 4) | ((command << 2) | COMMAND_MASK);
    SPI.transfer(msb);
    disableChip();                                                      // take the CS/SS pin high to de-select the chip
    SPI.endTransaction();                                               // Stop using the SPI bus to allow other chips to use SPI
}

// Overloaded sendCommand address, command and data returns byte
byte MCP4131::sendCommand(byte address, char command, byte data)
{
    SPI.beginTransaction(__SPISettings(250000, MSBFIRST, SPI_MODE0));     // Adjust SPI settings to fit MCP4131
    enableChip();                                                       // take the CS/SS pin low to select the chip
    byte msb = (address << 4) | ((command << 2) | COMMAND_MASK);
    SPI.transfer(msb);
    byte result = SPI.transfer(data);
    disableChip();                                                      // take the CS/SS pin high to de-select the chip
    SPI.endTransaction();                                               // Stop using the SPI bus to allow other chips to use SPI
    return result;                                                      // Return byte result from 2nd transfer
}

// -1 to Wiper value down to minimum 0
void MCP4131::decrementWiper()
{
    sendCommand(ADDRESS_WIPER0, COMMAND_DECREMENT);
}

// +1 to Wiper value up to maximum 127
void MCP4131::incrementWiper()
{
    sendCommand(ADDRESS_WIPER0, COMMAND_INCREMENT);
}

// Set to LOW to enable
void MCP4131::enableChip()
{
    pinResetFast(slaveSelectPin);
}

// Set to HIGH to disable
void MCP4131::disableChip()
{
    pinSetFast(slaveSelectPin);
}

Example sketch to test

/*
 * Project MCP4131-test
 * Description: Example to Use MCP4131 device driver
 * Author: W Steen/Armor 
 * Date: 29/03/20
 */

#include "MCP4131.h"

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);

MCP4131 pot(A2);

// setup() runs once, when the device is first turned on.
void setup()
{
  pot.begin();
}

// loop() runs over and over again, as quickly as it can execute.
void loop()
{
  for (byte i = 0; i < 127; i++)
  {
    pot.writeWiper(i);
    delay(100);
  }
  delay(1000);
  for (byte i = 0; i < 127; i++)
  {
    pot.decrementWiper();
    delay(100);
  }
  delay(1000);
}

Thanks so much for putting this together - with nothing else to do I’ve also been working on this quite solidly getting nowhere - unfortunately, I tried it out with the chip, but still no joy.

I’ve gone ahead and removed the LED, and instead just hooked it up to A0 and read that, values are still stuck on 2046ish. I’ve switched out all the jumper cables but that didn’t help either.

I’ve gone ahead and ordered a couple more ICs, just in case I’ve somehow managed to fry the current one.

Here’s a couple of pics of the setup IRL, I’ve double checked it against the datasheet but as far as I can tell it’s all wired up right - may have made a glaring mistake, though

Thanks again all for the help on this so far :slight_smile:

I ordered a new IC, which arrived this morning. I switched it out, but no joy.

I’m at my wits end with this, so here’s the code one last time, any help would be appreciated:

double a; // Used to report back value of A0

byte address = 0x00;
int CS = A2;

void setup()
{
    pinMode(CS, OUTPUT);
    SPI.begin();
    SPI.setClockSpeed(10, MHZ);
    digitalWrite(CS, HIGH);
    Particle.variable("A0", a);

}

void loop()
{
    for (int i = 0; i <= 128; i++)
    {
        digitalPotWrite(i);
        a = analogRead(A0);
        delay(10);
    }
}

void digitalPotWrite(int value)
{
    SPI.beginTransaction();
    digitalWrite(CS, LOW);
    SPI.transfer(address);
    SPI.transfer(value);
    digitalWrite(CS, HIGH);
    SPI.endTransaction();
}

A0 is still reading 2046ish.

I’m not entirely sure that address is the right value, but I’ve been playing around with it and nothing I enter seems to do anything

Cheers both for the advice so far, any more help would be very welcome indeed

From the driver I supplied earlier - I think that the SPI bus speed was set much lower - 250KHz. Have you tried that? __SPISettings(250000, MSBFIRST, SPI_MODE0)

Also, variable a is an integer, doesn’t need double precision.

1 Like

I have just done a dig in the parts tray and found a MCP4162-103 - an 8bit rheostat version of the one you have. I will have a go this evening using the library and see if I can get it to work.

2 Likes

For testing I'd take things a bit slower (e.g. 500ms instead of 10ms) and also use Serial.println(a) to see the variance of the reading - with a Particle.variable() you are not very likely to catch more than 2 of your 128 readings :wink:

Just for nit-picking, I'd not use the blue jumper wire for 3v3 to P0A and orange as GND to P0B.
In one distracted moment one might just go with the colours without double checking and off goes the magic smoke :sunglasses:

1 Like

It works!

Think the trick was slowing down the loop and using the slower SPI transfer - thanks so much to both of you for helping me get there :slight_smile:

I guess the upside to the past few days it that I now know way more about SPI than if it had worked first time :exploding_head:

video-1585755920

1 Like