digPotCluster class is part of imbAVR.SynthControl library, in imbAVR.ArduinoSynth GitHub repository.
It controls 8 digpots (MPC410-10) using shift register (74CH595N).
Purpose of shift register is to reduce number of pins used for CS (chip selection):
Normally, you would use 2 (SCLK, MOSI) + 8 (CS for each MPC410-10) = 10 pins
Like this, you use 2 (SCLK, MOSI) + 3 (Latch clock, clock, data) = 5 pins
The complete example is also available in the repository.
Arduino example
#include <SPI.h> #include "digPotCluster.h" digPotCluster dp_cluster = digPotCluster(); #define PIN_DATA A2 #define PIN_LEACH A3 #define PIN_CLOCK A4 void setup() { SPI.begin(); dp_cluster.init(PIN_LEACH, PIN_CLOCK, PIN_DATA); Serial.begin(9600); } void loop() { for (size_t i = 0; i < 25; i++) { byte v = i * 10; dp_cluster.Write(v, v, v, v, v, v, v, v); delay(100); Serial.println(v); } delay(1000); }
digPotCluster.h
#define DEBUG_CODE #include <SPI.h> #include "inttypes.h" /// <summary> /// Controls 8 digpots (MPC410-10), using shift register (74CH595N) to save pins on arduino /// </summary> class digPotCluster { //Pin connected to ST_CP of 74HC595 byte latchPin = 8; //Pin connected to SH_CP of 74HC595 byte clockPin = 12; ////Pin connected to DS of 74HC595 byte dataPin = 11; void SetOut(byte data); public: /// <summary> /// Sets pins connected to shift register 74HC595N /// </summary> /// <param name="latch_pin">Latch pin.</param> /// <param name="clock_pin">The clock pin.</param> /// <param name="data_pin">The data pin.</param> void init(byte latch_pin, byte clock_pin, byte data_pin); /// <summary> /// Values set the last time digpots were updated /// </summary> byte lastValues[8]; /// <summary> /// Sets values to digpots - if specified value is different from values set last time /// </summary> /// <param name="dp1">Value for digpot 1</param> /// <param name="dp2">Value for digpot 2.</param> /// <param name="dp3">Value for digpot 3.</param> /// <param name="dp4">Value for digpot 4.</param> /// <param name="dp5">Value for digpot 5.</param> /// <param name="dp6">Value for digpot 6.</param> /// <param name="dp7">Value for digpot 7.</param> /// <param name="dp8">Value for digpot 8.</param> void Write(byte dp1, byte dp2, byte dp3, byte dp4, byte dp5, byte dp6, byte dp7, byte dp8); /// <summary> /// Checks if the value differs from stored last value for the digpot specified /// </summary> /// <param name="dp_id">What dig pot - from 0 to 7</param> /// <param name="dp_value">Value to set</param> void CheckAndWrite(byte dp_id, byte dp_value); #ifdef DEBUG_CODE void Debug(); #endif /// <summary> /// Updates value on the digpot, regardless if it differs from stored last value /// </summary> /// <param name="dp_id">What dig pot - from 0 to 7</param> /// <param name="dp_value">Value to set</param> void Write(byte dp_id, byte dp_value); };
digPotCluster.cpp
#include "digPotCluster.h" void digPotCluster::init(byte latch_pin, byte clock_pin, byte data_pin) { latchPin = latch_pin; clockPin = clock_pin; dataPin = data_pin; pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); for (size_t i = 0; i < 8; i++) { Write(i, 0); lastValues[i] = 0; } } void digPotCluster::Write(byte dp1, byte dp2, byte dp3, byte dp4, byte dp5, byte dp6, byte dp7, byte dp8) { CheckAndWrite(0, dp1); CheckAndWrite(1, dp2); CheckAndWrite(2, dp3); CheckAndWrite(3, dp4); CheckAndWrite(4, dp5); CheckAndWrite(5, dp6); CheckAndWrite(6, dp7); CheckAndWrite(7, dp8); } void digPotCluster::CheckAndWrite(byte dp_id, byte dp_value) { if (dp_id > 7) return; if (lastValues[dp_id] != dp_value) { Write(dp_id, dp_value); lastValues[dp_id] = dp_value; } } #ifdef DEBUG_CODE void digPotCluster::Debug() { byte bitsToSend = 0B00000000; for (size_t i = 0; i < 8; i++) { digitalWrite(latchPin, LOW); bitWrite(bitsToSend, i, 0); SetOut(bitsToSend); digitalWrite(latchPin, HIGH); delay(1000); digitalWrite(latchPin, LOW); bitWrite(bitsToSend, i, 1); SetOut(bitsToSend); delay(1000); digitalWrite(latchPin, HIGH); } } #endif void digPotCluster::SetOut(byte data) { int i = 0; int pinState; digitalWrite(dataPin, 0); digitalWrite(clockPin, 0); for (i = 7; i >= 0; i--) { digitalWrite(clockPin, 0); if (data & (1 << i)) { pinState = 1; } else { pinState = 0; } digitalWrite(dataPin, pinState); digitalWrite(clockPin, 1); digitalWrite(dataPin, 0); } digitalWrite(clockPin, 0); } void digPotCluster::Write(byte dp_id, byte dp_value) { if (dp_id > 7) return; byte bitsToSend = 0B11111111; digitalWrite(latchPin, LOW); bitWrite(bitsToSend, dp_id, 0); SetOut(bitsToSend); digitalWrite(latchPin, HIGH); SPI.transfer(B00010001); SPI.transfer(dp_value); digitalWrite(latchPin, LOW); bitWrite(bitsToSend, dp_id, 1); SetOut(bitsToSend); digitalWrite(latchPin, HIGH); }
Breadboard example with Arduino Nano:
Remarks:
For purposes of my project, the first two MCP410 chips (on left) are used as single digital potentiomenter with 20kOhm resistance (sharing the same CS line)
The LEDs are there just for debug purposes.
SPI line wire color code:
Yellow – SCK line of SPI
Orange – MOSI line of SPI