Skip to content

Instantly share code, notes, and snippets.

@SolarDude
Created August 10, 2016 23:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SolarDude/9df3b6cce94b8a2e63d3f4d357adc0e5 to your computer and use it in GitHub Desktop.
Save SolarDude/9df3b6cce94b8a2e63d3f4d357adc0e5 to your computer and use it in GitHub Desktop.
MCP4151
#include "Particle.h"
// This example demonstrates control over SPI to the Microchip MCP4261 Digital potentometer
// SPI Pinouts are for Arduino Uno and Arduino Duemilanove board (will differ for Arduino MEGA)
// Download these into your Sketches/libraries/ folder...
// The Spi library by Cam Thompson. It was originally part of FPU library (micromegacorp.com)
// Available from http://arduino.cc/playground/Code/Fpu or http://www.arduino.cc/playground/Code/Spi
// Including Spi.h vv below initializea the MOSI, MISO, and SPI_CLK pins as per ATMEGA 328P
#include "Mcp4261.h"
/*************** SPARK CORE ***************/
// Wire up the SPI Interface common lines:
// #define SPI_CLOCK A3 //arduino <-> SPI Slave Clock Input -> SCK (Pin 02 on MCP4261 DIP)
// #define SPI_MOSI A5 //arduino <-> SPI Master Out Slave In -> SDI (Pin 03 on MCP4261 DIP)
// #define SPI_MISO A4 //arduino <-> SPI Master In Slave Out -> SDO (Pin 13 on MCP4261 DIP)
// Then choose any other free pin as the Slave Select (pin A2 if the default but doesnt have to be)
#define MCP4261_SLAVE_SELECT_PIN A2 //Spark Core <-> Chip Select -> CS (Pin 01 on MCP4261 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 = 50000.00; // 50k Ohm
// Instantiate Mcp4261 object, with default rW (=117.5 ohm, its typical resistance)
MCP4261 Mcp4261 = MCP4261( MCP4261_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 Mcp4261 object, after measuring the real rW wiper resistance
// MCP4261 Mcp4261 = MCP4261( MCP4261_SLAVE_SELECT_PIN, rAB_ohms, rW_ohms );
void setup()
{
// Set explicitly for Core. Not sure if this is also needed or recommended for Photon.
SPI.setClockDivider(SPI_CLOCK_DIV8); // Default is 9MHz. Set to DIV16 (4.5MHz) is not reliably working
SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(MSBFIRST);
SPI.begin(MCP4261_SLAVE_SELECT_PIN);
//First measure the the wiper resistance, called rW
//Mcp4261.wiper0_pos(0); // rAW = rW_ohms
//Mcp4261.wiper1_pos(0); // rAW = rW_ohms
// (optional)
// Scale to 100.0 for a percentage, or 1.0 for a fraction
// Eg if scale=100, then wiper(100) = max rAW resistance
// Eg scale=1.0, then wiper(1.0) = max rAW resistance
// Mcp4261.scale = 1.0;
Mcp4261.scale = 100.0; // For the timeout example, below
// Mcp4261.wiper0(40); // set pot0 rAW = 40% of max value
// Mcp4261.wiper1(80); // set pot1 rAW = 80% of max value
//
// Mcp4261.wiper0(5); // set pot0 rAW = 5% of max value
// Mcp4261.wiper1(50); // set pot1 rAW = 50% of max value
// Go back to using ohms
// Mcp4261.scale = Mcp4261.rAB_ohms;
uint32_t currentFrequency;
Serial.begin(115200); // faster communication means faster tracking
}
void loop()
{
//Mcp4261.wiper0(12); // Code used to change the Digital POT Value.
delay(1000);
}
// MCP4261 2-channel Digital Potentiometer
// ww1.microchip.com/downloads/en/DeviceDoc/22059b.pdf
// The default SPI Control Register - SPCR = B01010000;
// interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
// sample on leading edge of clk,system clock/4 rate (fastest).
// Enable the digital pins 11-13 for SPI (the MOSI,MISO,SPICLK)
#if defined (SPARK)
#include "Mcp4261.h"
#else
#include <Spi.h>
#include "Mcp4261.h"
#endif //SPARK
//---------- constructor ----------------------------------------------------
MCP4261::MCP4261(uint8_t slave_select_pin, float rAB_ohms)
{
setup_ss(slave_select_pin);
setup_resistance(rAB_ohms, rW_ohms_typical);
}
MCP4261::MCP4261(uint8_t slave_select_pin, float rAB_ohms, float rW_ohms)
{
setup_ss(slave_select_pin);
setup_resistance(rAB_ohms, rW_ohms);
}
//------------------ protected -----------------------------------------------
uint16_t MCP4261::byte2uint16(byte high_byte, byte low_byte)
{
return (uint16_t)high_byte<<8 | (uint16_t)low_byte;
}
byte MCP4261::uint16_high_byte(uint16_t uint16)
{
return (byte)(uint16>>8);
}
byte MCP4261::uint16_low_byte(uint16_t uint16)
{
return (byte)(uint16 & 0x00FF);
}
void MCP4261::setup_ss(uint8_t slave_select_pin)
{
// Set slave select (Chip Select) pin for SPI Bus, and start high (disabled)
::pinMode(slave_select_pin,OUTPUT);
::digitalWrite(slave_select_pin,HIGH);
this->slave_select_pin = slave_select_pin;
}
void MCP4261::setup_resistance(float rAB_ohms, float rW_ohms)
{
this->rAB_ohms = rAB_ohms;
this->rW_ohms = rW_ohms;
this->rAW_ohms_max = rAB_ohms - rW_ohms;
this->scale = rAW_ohms_max;
}
float MCP4261::step_increment()
{
return (rAW_ohms_max - rW_ohms) / resolution;
}
unsigned int MCP4261::ohms2wiper_pos(float ohms)
{
if(ohms <= 0.0)
return 0;
else if(scale != rAW_ohms_max)
ohms = ohms * rAW_ohms_max / scale;
return (unsigned int)((ohms - rW_ohms) / step_increment() ) + 0.5;
}
float MCP4261::wiper_pos2ohms(unsigned int wiper_pos)
{
float ohms = rW_ohms + ( (float)wiper_pos * step_increment() );
if(scale != rAW_ohms_max)
ohms = ohms * scale / rAW_ohms_max;
return ohms;
}
void MCP4261::write(byte cmd_byte, byte data_byte)
{
cmd_byte |= kCMD_WRITE;
::digitalWrite(slave_select_pin, LOW);
byte high_byte = SPI.transfer(cmd_byte);
byte low_byte = SPI.transfer(data_byte);
::digitalWrite(slave_select_pin, HIGH);
boolean result = ~low_byte;
}
uint16_t MCP4261::read(byte cmd_byte)
{
cmd_byte |= kCMD_READ;
::digitalWrite(slave_select_pin, LOW);
byte high_byte = SPI.transfer(cmd_byte);
byte low_byte = SPI.transfer(0xFF);
::digitalWrite(slave_select_pin, HIGH);
return byte2uint16(high_byte, low_byte);
}
void MCP4261::wiper_pos(byte pot, unsigned int wiper_pos)
{
byte cmd_byte = 0x00;
byte data_byte = 0x00;
cmd_byte |= pot;
// Calculate the 9-bit data value to send
if(wiper_pos > 255)
#if defined (SPARK)
cmd_byte |= 0B00000001; // Table 5-1 (page 36)
#else
cmd_byte |= B00000001; // Table 5-1 (page 36)
#endif
else
data_byte = (byte)(wiper_pos & 0x00FF);
write(cmd_byte|kADR_VOLATILE, data_byte);
if(non_volatile)
{
// EEPROM write cycles take 4ms each. So we block with delay(5); after any NV Writes
write(cmd_byte|kADR_NON_VOLATILE, data_byte);
delay(5);
}
}
//---------- public ----------------------------------------------------
float MCP4261::wiper0()
{
return wiper_pos2ohms( wiper0_pos() );
}
float MCP4261::wiper1()
{
return wiper_pos2ohms( wiper1_pos() );
}
unsigned int MCP4261::wiper0_pos()
{
return (unsigned int)( 0x01FF & this->read(kADR_WIPER0|kADR_VOLATILE) );
}
unsigned int MCP4261::wiper1_pos()
{
return 0x01FF & this->read(kADR_WIPER1|kADR_VOLATILE);
}
void MCP4261::wiper0(float ohms)
{
wiper0_pos( ohms2wiper_pos(ohms) );
}
void MCP4261::wiper1(float ohms)
{
wiper1_pos( ohms2wiper_pos(ohms) );
}
void MCP4261::wiper0_pos(unsigned int wiper_pos)
{
this->wiper_pos(kADR_WIPER0, wiper_pos);
}
void MCP4261::wiper1_pos(unsigned int wiper_pos)
{
this->wiper_pos(kADR_WIPER1, wiper_pos);
}
// // Not implemented
// bool MCP4261::pot0_connected(bool terminal_a, bool wiper, bool terminal_b)
// {
//
// }
//
// bool MCP4261::pot1_connected(bool terminal_a, bool wiper, bool terminal_b)
// {
//
// }
//
// void MCP4261::pot0_connect(bool terminal_a, bool wiper, bool terminal_b)
// {
//
// }
//
// void MCP4261::pot1_connect(bool terminal_a, bool wiper, bool terminal_b)
// {
//
// }
//
// bool MCP4261::pot0_shutdown()
// {
//
// }
//
// bool MCP4261::pot1_shutdown()
// {
//
// }
//
// void MCP4261::pot0_shutdown(bool shutdown)
// {
//
// }
//
// void MCP4261::pot1_shutdown(bool shutdown)
// {
//
// }
//
// bool MCP4261::hw_shutdown()
// {
//
// }
// MCP4261 2-channel Digital Potentiometer
// ww1.microchip.com/downloads/en/DeviceDoc/22059b.pdf
#ifndef Mcp4261_h
#define Mcp4261_h
#if defined (SPARK)
#include "application.h"
#endif
class MCP4261
{
public:
// You must at least specify the slave select pin and the rated resistance
MCP4261(uint8_t slave_select, float rAB_ohms);
// If you have measured wiper resistance, rW
MCP4261(uint8_t slave_select, float rAB_ohms, float rW_ohms);
// The resistance scaling, defaults to rAB_ohms
float scale;
// Read potentiometer values
float wiper0();
float wiper1();
unsigned int wiper0_pos();
unsigned int wiper1_pos();
// Write potentiometer values
void wiper0(float ohms);
void wiper1(float ohms);
void wiper0_pos(unsigned int wiper_pos);
void wiper1_pos(unsigned int wiper_pos);
// // Not implemented
// // Connect / disconnect potentiometers
// bool pot0_connected(bool terminal_a, bool wiper, bool terminal_b);
// bool pot1_connected(bool terminal_a, bool wiper, bool terminal_b);
// void pot0_connect(bool terminal_a, bool wiper, bool terminal_b);
// void pot1_connect(bool terminal_a, bool wiper, bool terminal_b);
//
// bool pot0_shutdown();
// bool pot1_shutdown();
// void pot0_shutdown(bool shutdown);
// void pot1_shutdown(bool shutdown);
//
// bool hw_shutdown();
protected:
#if defined (SPARK)
const float rW_ohms_typical = 117.50f;
static const unsigned int resolution_7bit = 128;
static const unsigned int resolution_8bit = 256;
#else
const static float rW_ohms_typical = 117.50f;
const static unsigned int resolution_7bit = 128;
const static unsigned int resolution_8bit = 256;
#endif //SPARK
// Other devices can be configured below vv as per the device numbering scheme:
// MCP4N-- N=1 single pot, N=2 dual pot
// MCP4--N N=1 potentiometer, N=2 rheostat
// MCP4-N- N=3 7-bit volatile, N=4 7-bit non-volatile, N=5 8-bit volatile, N=6 8-bit non-volatile
const static boolean non_volatile = true;
const static unsigned int resolution = resolution_8bit;
float rW_ohms;
float rAB_ohms;
float rAW_ohms_max;
uint8_t slave_select_pin;
#if defined (SPARK)
const static uint8_t kADR_WIPER0 = 0B00000000;
const static uint8_t kADR_WIPER1 = 0B00010000;
const static uint8_t kCMD_READ = 0B00001100;
const static uint8_t kCMD_WRITE = 0B00000000;
const static uint8_t kADR_VOLATILE = 0B00000000;
const static uint8_t kADR_NON_VOLATILE = 0B00100000;
const static uint8_t kTCON_REGISTER = 0B01000000;
const static uint8_t kSTATUS_REGISTER = 0B01010000;
#else
const static uint8_t kADR_WIPER0 = B00000000;
const static uint8_t kADR_WIPER1 = B00010000;
const static uint8_t kCMD_READ = B00001100;
const static uint8_t kCMD_WRITE = B00000000;
const static uint8_t kADR_VOLATILE = B00000000;
const static uint8_t kADR_NON_VOLATILE = B00100000;
const static uint8_t kTCON_REGISTER = B01000000;
const static uint8_t kSTATUS_REGISTER = B01010000;
#endif //SPARK
uint16_t byte2uint16(byte high_byte, byte low_byte);
byte uint16_high_byte(uint16_t uint16);
byte uint16_low_byte(uint16_t uint16);
void setup_ss(uint8_t slave_select_pin);
void setup_resistance(float rAB_ohms, float rW_ohms);
float step_increment();
unsigned int ohms2wiper_pos(float ohms);
float wiper_pos2ohms(unsigned int wiper_pos);
uint16_t read(byte cmd_byte);
void write(byte cmd_byte, byte data_byte);
void wiper_pos(byte pot, unsigned int wiper_pos);
};
#endif // Mcp4261_h
@SolarDude
Copy link
Author

Code used to run the MCP4151 Digital 50k Pot on Particle Photon

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment