I2C Potentiometer requires greater writing scale than datasheet specifies

I wrote a test sketch to learn how to write to the Dallas Semiconductor DS1882 Dual Digital Potentiometer. Datasheet directly from the manufacturer website is here.

There are a couple of libraries for this chip already on Github, but one depends on a library that's broken, and the other one isn't documented or commented well. I also wanted to figure out how to talk to this chip on my own, and to be successful I need to understand the code better than just snipping bits from other libraries and sketches

The sketch writes to both pots, increasing the wiper setting/attenuation (thus reducing the volume) with each loop, pauses on the highest setting (position 63 from the datasheet) for 2 seconds, then starts over again from the lowest wiper setting (0).

It mostly works, except when I started it would not get all the way down to the mute setting. When I experimented with the variable check at the end, I found if I step all the way up to 255, I get the desired behavior, with the chip reducing the volume all the way to mute.

But...the datasheet specifies 0-63 wiper positions. :confused: You'll see where I increase the wiper setting at the very bottom of the loop.

I suspect something is askew in my bitmath, but haven't been able to wrap my head around it.

Here's the sketch--I made some notes on the chip communication specs at the beginning of it:

[code]
/*
DS1882 Test Sketch

---------------------------------
DS1882 I2C Command Byte Structure
---------------------------------
SLAVE ADDRESS BYTE
        Base        A2 A1 A0 ... R/W (LSB=1 for Read request)
Bin     0  1  0  1  0  0  0  ... 0
Bitval  64 32 16 8  4  2  1
Hex        0x28                (<-----This is the slave address base)
Dec         40

COMMAND BYTE
Bin     0  0  0  0  0  0  0  0
POT0    0  0  0  POT0WiperPos   <---BIT6 toggles POT0 or POT1
POT1    0  1  0  POT1WiperPos   <┘
CONFIG  1  0  X  X  X  0  0  0  <---BIT7 flags CONFIG register
                       |  |  |
                       |  |  POTConfig(BIT0): 0(0x0)=63 positions & mute, 1(0x1)=33 positions & mute(default)
                       |  POTZeroCrossing(BIT1): 0=disabled, 1=enabled(default)
                       POTNVM Volatile/Nonvolatile(BIT2): 0=nonvolatile, 1=volatile(default, potentiometer powers up in mute pos.)

Read Protocol: Start SLAVEADDR POT0BYTE POT1BYTE CONFIG Stop
Write Protocol: Start SLAVEADDR (POT0BYTE, POT1BYTE, CONFIG can be sent in any order after SLAVEADDR) Stop

*/

#include <Wire.h>

byte CONFIG = B10000010; //set configuration byte
byte POT0 = 00;          //flag for pot 0
byte POT1 = 01;          //flag for pot 1
int POT0SET = 1;        //initialize pot 0 wiper set  (63 = mute, 0 = no attenuation)
int POT1SET = 1;        //initialize pot 1 wiper set
byte POT0CMD;             //initialize pot 0 command byte
byte POT1CMD;             //initialize pot 1 command byte

byte POT0RD;              //initialize pot 0 read byte
byte POT1RD;              //initialize pot 1 read byte
byte CONFIGRD;            //initialize configuration byte read
int POT0GET;             //initialize pot 0 wiper position reading
int POT1GET;             //initialize pot 1 wiper position reading


void setup()
{
  Wire.begin(); // join i2c bus (address optional for master)
  Serial.begin(9600);
}


void loop()
{ 
  POT0SET++;        // increase attenuation 
  POT1SET++;
 
  POT0CMD = ((POT0 << 6) | (POT0SET >> 2));  //assemble pot 0 command byte
  POT1CMD = ((POT1 << 6) | (POT1SET >> 2));  //assemble pot 1 command byte  
  
  Wire.beginTransmission(40); // transmit to device #40 (0x28)
  Wire.write(POT0CMD);       // sends pot 0 command byte  
  Wire.write(POT1CMD);       // sends pot 1 command byte
  Wire.write(CONFIG);         // sends configuration byte  
  Wire.endTransmission();     // stop transmitting

  Serial.print("Pot0 WiperSetValue = ");
  Serial.println(POT0SET);
  Serial.print("Pot1 WiperSetValue = ");
  Serial.println(POT1SET);

/* Not working yet - returns "255" on every POT#GET print
  Wire.requestFrom(40, 3);     //request 3 bytes from device #40
  POT0RD = Wire.read();         //read pot0 byte
  POT1RD = Wire.read();         //read pot1 byte
  CONFIGRD = Wire.read();       //read configuration register byte
  POT0GET = POT0RD & B00111111;  //mask pot0 flag and extract wiper value
  POT1GET = POT1RD & B00111111;  //mask pot1 flag and extract wiper value 
  Serial.print("Pot0 Position = ");
  Serial.println(POT0RD);
  Serial.print("Pot1 Position = ");
  Serial.println(POT1RD);
  Serial.println();
*/
  
  if(POT0SET == 255)  // if reached position 63(???!) (mute)
  {
    delay(2000);   // hold mute for 2 seconds
    POT0SET = 0;   // start over from full volume
    POT1SET = 0; 
  }
  delay(100);
}

[/code]

You shift the values right two bits, so they need to run from 0x00 to 0xFC

  POT0CMD = ((POT0 << 6) | (POT0SET >> 2));  //assemble pot 0 command byte
  POT1CMD = ((POT1 << 6) | (POT1SET >> 2));  //assemble pot 1 command byte

BTW constants are traditionally all UPPER case, variables are lower_case or mixedCase (aka
camelCase)

Most programmers will assume an all upper case name is a constant (if its not a 'mathematical'
one-letter variable)

So

  pot0cmd = ((POT0 << 6) | (pot0set & 0x3F));  //assemble pot 0 command byte
  pot1cmd = ((POT1 << 6) | (pot1set & 0x3F));  //assemble pot 1 command byte

Would be less deceptive to read and allows full volume with the variables at 63 (0x3F)
as the shift is removed and the value just masked to 6 bits.

Any chance that you share your wiring diagram of the chip? Mine seems to have a few problems.... would appreciate any help.

Problem description and wiring on reddit

The two MSBs (Most Significant Bits) are the command and determine what is going to be written to. the six LSB are our value. Excerpt from page 8 of the data sheet: The Command Byte determines both the potentiometer wiper settings and the configuration of both potentiometers. This is done by setting the two MSBs of the Command Byte to one of three values. If 00 is set as the value for the two MSBs, then the wiper setting for Potentiometer 0 is to be programmed. If 01 is set as the value, then the wiper setting of Potentiometer 1 is to be programmed.

Why?
This simply means that for every 4 step your code makes the hardware is only turned down one notch.

1 Like

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.