SPI library - bad bits in address sent to AD5206 chip

I'm having some difficulty with the SPI library and an AD5206 chip. I've created a basic test sketch based on the code in the SPIDigitalPot example in the tutorials on this site, and see that the same code ships with the SPI library in the Arduino IDE. The example code has this function:

void digitalPotWrite(int address, int value) {
  // take the SS pin low to select the chip:
  digitalWrite(slaveSelectPin,LOW);
  //  send in the address and value via SPI:
  SPI.transfer(address);
  SPI.transfer(value);
  // take the SS pin high to de-select the chip:
  digitalWrite(slaveSelectPin,HIGH); 
}

It just sends an address (which pot to activate) and a value (the amount of resistance to apply on that pot.) Unfortunately for me the address seems to get munged on the way to the chip. I cannot be sure which pot or pots is getting activated by this function. For instance, if I try to set address to 0 (the first pot) the resistance value shows up on both pot 0 and pot 2. I get various other results with the other addresses. The results seem consistent across my attempts (meaning if I send a particular address between attempts the pots that are affected seem to be the same) although I haven't tested the patterns out exhaustively. (I'm only using 3 of the 6 pots.) At first I thought I had the LSB/MSB send order backwards... except that wouldn't matter with address 0. I have it explicitly set to MSBFIRST now.

Does anyone have an idea of what could be causing this? The SPI lib is too simple to be hiding this problem. I would guess it might be something in the clock timing. I've tried it with two different Arduino Uno's and get the same results. I've tried setting SPI.setClockDivider(2), but again the same results. Do I need to set one of the SPI modes?

Any help would be appreciated.

Please show the code that initializes SPI, sets modes, bit orders etc.

Here's the whole of the setup:

void setup() {
   // set the slaveSelectPin as an output:
   pinMode (slaveSelectPin, OUTPUT);
   // initialize SPI:
   SPI.begin();
   //SPI.setClockDivider(2);
   Serial.begin (9600);
   SPI.setBitOrder(MSBFIRST);
   delay(100);
}

How about the whole sketch? I am getting this out of you in dribs and drabs. Now I want to see how you are calling digitalPotWrite.

Dribs and drabs or not I appreciate the help! I’m using the code pretty much as it is in the example except that I set the MSBFIRST directive and changed the integer type of the parameters to byte. Both things I did while trying to debug. I also tried using setClockDivider(2) to see if I would have any more luck, but it is commented out now.

// inslude the SPI library:
#include <SPI.h>

// set pin 10 as the slave select for the digital pot:
 const int slaveSelectPin = 10;
 const byte FRchannel1 = 0;
// const byte FRchannel1 = 0;
// const byte FRchannel2 = 3;
 const byte LRchannel1 = 1;
// const byte LRchannel2 = 4;
 const byte CTchannel = 2;
 const byte MAXV = 220;
 const byte MINV = 50;
 const byte MIDV = 127;
  
void setup() {
   // set the slaveSelectPin as an output:
   pinMode (slaveSelectPin, OUTPUT);
   // initialize SPI:
   SPI.begin();
   //SPI.setClockDivider(2);
   Serial.begin (9600);
   SPI.setBitOrder(MSBFIRST);
   delay(100);
}

void loop() {
     
     byte level = MIDV;
     
     digitalPotWrite(LRchannel1, level);
     Serial.print("Pausing for calibration: ");
     Serial.println(level);
     digitalPotWrite(FRchannel1, level);
     digitalPotWrite(CTchannel, MIDV);
     delay(20000);
    
     // change the resistance on this channel from min to max:
     for (level = MIDV; level < MAXV; level++) {
       digitalPotWrite(FRchannel1, level);
       Serial.println(level);
       delay(100);
     }
     // wait a second at the top:
     delay(1000);
     // change the resistance on this channel from max to min:
     for (level = MAXV; level > MINV; level--) {
       digitalPotWrite(FRchannel1, level);
       Serial.println(level);
       delay(100);
     }
     delay(1000);
     for (level = MINV; level < MIDV; level++) {
       digitalPotWrite(FRchannel1, level);
       Serial.println(level);
       delay(100);
     }
}

void digitalPotWrite(byte address, byte value) {
   // take the SS pin low to select the chip:
   digitalWrite(slaveSelectPin,LOW);
   //  send in the address and value via SPI:
   SPI.transfer(address);
   SPI.transfer(value);
   // take the SS pin high to de-select the chip:
   digitalWrite(slaveSelectPin,HIGH);
}

That won't do it. The device is set to use only 11 bits of the SPI send. You are sending 16, and the first 5 are zeros. Maybe you can fool it by shifting all that 5 bits left, or bit bang it. http://www.analog.com/static/imported-files/data_sheets/AD5204_5206.pdf

Eleven data bits make up the data-word clocked into the serial input register. The first three bits are decoded to determine which VR latch is loaded with the last eight bits of the data-word when the CS strobe is returned to logic high.

That is definitely something I'm thinking about since this is my first project using SPI, and the SPI library. However, if I understand it correctly the SPI lib sends data 1 entire byte at a time to the device. All of the unused MSBs get discarded... or at least they are supposed to get discarded. I presume the unused bits in the first byte get dropped off the end of the register when the bits from the second byte get pushed in?

Or am I misunderstanding the library?

Here are the timing diagrams from the datasheet. It looks to me like bit transfer completes on the falling edge of the clock. Do I need to set the data mode to something other than the default?

ad5206_timing.PNG

The SPI library does not discard any bits. It sends them all, including the 5 zero bits in your address. The problem is the device does not discard them according to the datasheet. It uses the first 3 bits as the address, and the next 8 bits as the data, for a total of 11 bits. Page 5 of the datasheet and the pic in your previous post.

This code sends 16 bits. If you are lucky, it will ignore the last 5 bits and set your digital pot correctly

void digitalPotWrite(byte address, byte value) {
// set sendBuf to address
   int sendBuf = address;
// shift address into the high byte
   sendBuf = sendBuf << 8;
// bitwise OR with the value
   sendBuf = sendBuf | value;
// shift all bits left 5
   sendBuf = sendBuf << 5;

   // take the SS pin low to select the chip:
   digitalWrite(slaveSelectPin,LOW);
   //  send the address and value via SPI:
   SPI.transfer(highByte(sendBuf));
   SPI.transfer(lowByte(sendBuf));
   // take the SS pin high to de-select the chip:
   digitalWrite(slaveSelectPin,HIGH);
}

If not, you will need to bit bang it.

The way you are sending it now is (A7 to A3 are zero bits)
A7 - A6 - A5 - A4 - A3 - A2 - A1 - A0 - D7 - D6 - D5 - D4 - D3 - D2 - D1 - D0
The way it should be is
A2 - A1 - A0 - D7 - D6 - D5 - D4 - D3 - D2 - D1 - D0
And the way the code above will try is
A2 - A1 - A0 - D7 - D6 - D5 - D4 - D3 - D2 - D1 - D0 - 0 - 0 - 0 - 0 - 0

I'll give this code a try and report what I find. I'm certainly open to the explanation. If it doesn't work I'll try bit-banging the values.

The only thing I'm scratching my head about is how the code I'm using now found it's way into the SPI library example for the AD5206 chip?

Unfortunately, the replacement digitalPotWrite function above sends the AD5206 to crazy town. :-) At least with the original function the value seemed to come across with reasonable levels. I mean... I don't have a 'bus pirate' or signal analyzer to verify with... but the voltage values coming out of the pots seemed to be close to what was requested in the code. With this version the voltages jump all over the place and the target pots are just as off as they were before.

I'm starting to search for bit banging examples now and will let you know how that goes.

http://www.gammon.com.au/spi

Bit bang library near the end of that.

Thanks for the link… that code looks good. I had to use the code inline though… since I needed to change the bits to transfer to 11 instead of 16. Unfortunately I’m not getting any movement on the pots now. It will take me a little time to diagnose what I’m doing wrong with the code. It does seem like something should be going through.

I found the problem.

I couldn't get anything going with bit-banging code. I'm sure I was doing something wrong. But I was getting tired so I dropped back to the SPI library code and worked to isolate the wiring. This is what I should have done in the first place!

While running my tests I've had the chip connected to the device that I want it to control. It's the joystick port of an electric wheelchair controller. It turns out the voltages that I'm seeing on the different pots are bleeding in through that controller. If I disconnect it the AD5206 works perfectly with the demo code function in the example.

I believe this means that it's fine to send the extra 0 bits on the MSB side of the value. Reading the datasheet a little closer it does describe the chip as having a shift register... so the MSBs get shifted out once the 8 bits of the value get shifted in.

Thanks for your help guys.