I'd like to see if anybody can assist me with getting a CAT525 digital pot to work with Arduino over SPI. I have run through all of the things I can think of to run through, and it's not working. Specifically, under no set of conditions can I make the voltage or resistance between ground and the output pin change in in a controllable way. Also, the chip is supposed to output the old pot value when a new pot value is input, but SPI.transfer() always returns zero. Finally, the datasheet indicates that the "ready/busy" pin should be either low or high to indicate whether the pin is ready to receive commands, but it doesn't seem to have any voltage on it at all--my multimeter just "hunts" when I put it on that pin.
The data sheet for the chip indicates that chip select should be held high to select the chip, so I have jumpered it to 5v. I have tried it both ways, however.
Here is my code:
#include "SPI.h"
const int slaveSelectPin = 10;
void setup() {
pinMode(slaveSelectPin, OUTPUT);
digitalWrite(slaveSelectPin, HIGH); // data sheet indicates that "select" is HIGH, contrary to Arduino SPI documentation I've read
SPI.begin();
SPI.setBitOrder(LSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV32); // max 1 MHz clock -- Uno has 16 MHz clock
SPI.setDataMode(SPI_MODE0);
Serial.begin(9600);
}
void loop() {
byte SFD = 0x04; // command to the chip starts with a 1 as start delimiter, then followed by two-bit pot number (in this case 00 binary)
byte i;
for (i=0; i < 256; i+=32) {
Serial.print("i = ");
Serial.print(i);
SPI.transfer(SFD);
Serial.print("\tSPI.transfer() returns: ");
Serial.println(SPI.transfer(i));
delay(2000);
}
}
I have also attached the datasheet for the chip, if anybody wants to see it.
Having a quick look at the datasheet, the first thing that struck me is that the protocol for the chip is a form of Microwire ("a simple 3-wire, Microwire-like serial interface"), not SPI. While microwire and SPI are conceptually similar, they aren't the same thing. For example, you need to send a start bit before sending data, which you don't do with SPI. And the DO on the chip only becomes actives once you've sent the start bit, and then another 2 bits specifying the address, on the DI. It's tri-stated until that happens. None of this corresponds to anything happening with straight SPI.
It may ultimately be more straightforward to bit-bang the interface, rather than try to use the Arduino hardware SPI library to communicate with it. It shouldn't be hard to do, assuming you have the patience, since the datasheet provides the timing diagrams. And because it's a slow speed interface, digitalWrite() would probably be fine to implement. Indeed, you may need to put some timing delays in your routines to ensure you aren't writing too fast.
Ah. Thanks. Not having heard of MicroWire (I hadn't heard of SPI either, until recently!) I assumed that it was analogous to, or a version of, SPI. I"ll have a go at bit-banging the interface, then.
When I shopped for this chip, I filtered in Mouser on, "Digital (3-wire)" interface, which I hoped would give me only SPI chips. Is there something else I should have filtered on, or will MicroWire and SPI chips always be interspersed with each other?
I'd just google "spi digital potentiometer", read up on some of the many chips available that would bring up, and make up a short list based on what I read. You might even find someone has already written code you can use for a particular chip, which would be a bonus.
Then, shop around to get the chips at the best prices at Digikey, Mouser, eBay, wherever.
But now you've got the CAT525 chips, you might as well fiddle around trying to get a bit-banging solution to work. That can be satisfying too.
Ok, thanks. I did a little searching and I see that there doesn't seem to be a 100% consistent definition of the chips' interface in Mouser. For example, one chip that definitely supports SPI shows "Serial" as its data interface, while "SPI" is also an option. Looks like I'll either have to poke around in data sheets or, like you suggested, google to find somebody else's solution.
Once I've done some work on bit-banging the interface, I'll post back here. From the timing diagrams, it looks like what I will need to do is:
Clock HIGH (pre-stage)
Chip Select HIGH
...
Wait 1/2 cycle
CLOCK LOW
MOSI HIGH/LOW (to set 1 or 0 bit)
Wait 1/2 cycle
CLOCK HIGH (to shift in the bit and start the next cycle)
Pretty much. (Also have to manage CS when appropriate, and read from DO if required.) Just make sure you respect the timing values specified in the data sheet, and things should go smoothly.
With bit-banging, of course, you can use any pins that are convenient, if you want to leave the SPI pins for a different purpose later.
Holy cow I just realized something. I have been using 0x04 as the control byte, but the bytes are transmitted LSB first, so if I want the chip to receive %0000 0100 (big-endian), I have to transmit %0010 0000 (big-endian). Using that control byte, it appears to be working.
Here is the new code, for posterity:
#include "SPI.h"
const int slaveSelectPin = 10;
void setup() {
pinMode(slaveSelectPin, OUTPUT);
digitalWrite(slaveSelectPin, LOW); // deactivate chip
SPI.begin();
SPI.setBitOrder(LSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV32); // max 1 MHz clock -- Uno has 16 MHz clock
SPI.setDataMode(SPI_MODE0);
Serial.begin(9600);
}
void loop() {
// command to the chip starts with a 1 as start delimiter, then followed by two-bit pot number (in this case 00 binary)
// since bytes are transmitted LSB, this means that the delimiter bit must be in the 3rd most-significant position
// and the pot number in the 1st and 2nd most-significant position
byte SFD = B00100000;
byte i;
digitalWrite(slaveSelectPin, HIGH);
for (i=0; i < 256; i+=32) {
Serial.print("i = ");
Serial.println(i);
SPI.transfer(SFD);
SPI.transfer(i);
delay(5000);
}
}
And here is a very exciting video of the chip appearing to work. It's changing values every five seconds.
BTW, I will say, in case anybody comes along months from now and is researching this chip, that I would not recommend it for use in an SPI-based system. The chip is working with the SPI library, but it has all these dumb quirks. For example, the chip-select works backwards (HIGH is selected and LOW is idle), which means I have to write custom routines to select devices instead of just writing LOW to the appropriate chip-select pin. Another dumb thing is the way the chip manages its registers. The registers are stored in non-volatile memory, which sounds cool at first, but I don't actually need that feature. The down-side is that as soon as chip-select goes LOW, the chip reverts its pot values back to the stored values. This is sold as a feature--allowing the user to change values temporarily without overwriting stored values, in case of the need for a revert. But it's dumb because in any system with more than one SPI device, you're usually going to be reading from each device at least once per loop(), so basically I have to write to non-volatile memory, with the associated power draw and delay, every loop. I would much rather be using a chip that holds values in volatile memory, but continues to retain them regardless of the state of chip-select, and that doesn't require a non-volatile write every single loop().
I have been thinking of options for expanding my analog inputs, and I have been thinking of using a A/D converter because, even though it's more expensive, it will work over SPI, and it will save me digital pins compared to a mux, which will use three digital pins to select the output. But now I'm thinking maybe it would be smarter to use the mux, so that I could always leave the digital pot selected, so that I wouldn't have to constantly write its values to non-volatile memory. Or maybe just use a different digital pot chip...
FWIW, the chip appears to pull about 0.5 mA when running normally, and about 2 mA when writing to non-volatile memory, so maybe that's not so bad, but given that the project is running off batteries, it still seems like a waste.
... no wait. Actually, that's a terrible power draw. The whole Arduino draws something like 25-35 mW, so this chip is between a 2x and a 6x power draw penalty. Also, the non-volatile memory is rated at 100k writes. Which sounds like a lot until you figure that I will have to end up writing to it every time I go away to check another SPI device, which may be... let's say no less than 5 times a second. So... 5 writes a second gets us to 100k writes in... 5 hours.
The more I think about it, the more I think this chip is not designed for real-time update applications. It would be great for, like, a trimpot situation where you change the value, check the results, revert if you don't like them, and write if you like them, then LEAVE IT ALONE. But I'm not leaving it alone.