mbeals 74 Posted November 23, 2012 Share Posted November 23, 2012 I'm attempting to talk to a digipot over SPI with energia. The pot in question is a microchip mcp4261 (http://ww1.microchip...eDoc/22059b.pdf). To communicate with it, you pull the CS pin active (low), send it a 2 byte command then pull the CS pin back high. Without quoting the entire the data sheet, the basic command sequence is a 4 bit register address, 2 bit command, and 9 data bits. When you issue the read command, it is supposed to return 9 bits of data (the value of the register). I've bread boarded this up using the SPI examples with the following layout: CS -> P2.0 SCK -> P1.5 SDI -> P1.7 SDO -> P1.6 I also applied VDD/VSS across P1A/P1B and connected the wiper up to P1.0. Here is the code I'm using to talk to the pot: /* SCP1000 Barometric Pressure Sensor Display Shows the output of a Barometric Pressure Sensor on a Uses the SPI library. For details on the sensor, see: http://www.sparkfun.com/commerce/product_info.php?products_id=8161 http://www.vti.fi/en/support/obsolete_products/pressure_sensors/ This sketch adapted from Nathan Seidle's SCP1000 example for PIC: http://www.sparkfun.com/datasheets/Sensors/SCP1000-Testing.zip Circuit: SCP1000 sensor attached to pins 6, 7, 10 - 13: DRDY: pin 6 CSB: pin 8 MOSI: pin 14/15 MISO: pin 15/14 * need to level convert this SCK: pin 7 created 31 July 2010 modified 14 August 2010 by Tom Igoe modified 2 May 2012 Rick Kimball - changed pin # for msp430 modified 23/11/12 Matt Beals - adapted for MCP4261 */ const int WIPER0 = 0x00; const int WIPER0_default = 0x02; const int WIPER1 = 0x01; const int WIPER1_default = 0x03; const int TCON = 0x04; const int STATUS = 0x05; const int DATA0 = 0x06; const int DATA1 = 0x07; const int DATA2 = 0x08; const int DATA3 = 0x09; const int DATA4 = 0x0A; const int DATA5 = 0x0B; const int DATA6 = 0x0C; const int DATA7 = 0x0D; const int DATA8 = 0x0E; const int DATA9 = 0x0F; //Command bits const byte READ = 0b11; const byte WRITE = 0b00; const byte UP = 0b01; const byte DOWN = 0b10; // the sensor communicates using SPI, so include the library: #include <SPI.h> #include <TimerSerial.h> TimerSerial TS; // pins used for the connection with the sensor // the other you need are controlled by the SPI library): const int chipSelectPin = 8; // P2.0 unsigned int stat; byte i = 0; int sensorPin = A0; // select the input pin for the potentiometer int sensorValue = 0; void setup() { TS.begin(9600); // start the SPI library: SPI.begin(); // initialize the data ready and chip select pins: pinMode(chipSelectPin, OUTPUT); delay(100); } void loop() { stat = readRegister(WIPER0); sensorValue = analogRead(sensorPin); TS.print("Sensor Value: "); TS.print(sensorValue); TS.println(" "); sendData(WIPER0, i); i += 10; if(i > 257){ i = 0; } delay(10000); TS.println(" "); } //Read from or write to register unsigned int readRegister(int Register) { int inByte = 0; // incoming byte from the SPI unsigned int result = 0; // result to return TS.print("Reading Register: "); TS.print(Register); // now combine the register address and the command int dataToSend = (Register << 12) | (READ << 10); TS.print("\t Sending Code : "); TS.print(dataToSend,BIN); //Pull pin active, write the command, then pull pin inactive digitalWrite(chipSelectPin, LOW); result = SPI.transfer(dataToSend); delay(10); digitalWrite(chipSelectPin, HIGH); TS.print("\t Reply: "); TS.print(result, BIN); TS.println(" "); return(result); } void stepWiperUP(int Register){ int dataToSend = 0b00000100 | (Register << 4); SPI.transfer(dataToSend); delay(10); digitalWrite(chipSelectPin, HIGH); } void stepWiperDOWN(int Register){ int dataToSend = 0b00001000 | (Register << 4); SPI.transfer(dataToSend); delay(10); digitalWrite(chipSelectPin, HIGH); } void sendData(int Register, byte value) { //4 bit register address needs to be first in 16 bit packet // now combine the register address and the command into one byte: int dataToSend = (Register << 12) | (WRITE << 10) | value; TS.print("Writing Register "); TS.print(Register); TS.print(" with value: "); TS.print(value); TS.print(" Sending Code: "); TS.print(dataToSend,BIN); TS.println(" "); digitalWrite(chipSelectPin, LOW); SPI.transfer(dataToSend); //Send register location delay(10); digitalWrite(chipSelectPin, HIGH); } When I run this, this is the output I get: Reading Register: 0 Sending Code : 110000000000 Reply: 11111111 Sensor Value: 574 Writing Register 0 with value: 218 Sending Code: 11011010 Reading Register: 0 Sending Code : 110000000000 Reply: 11111111 Sensor Value: 574 Writing Register 0 with value: 228 Sending Code: 11100100 From this, I can see that the commands I am writing to the pot follow the format specified in the datasheet, and that I am getting a reply back from the pot. If I pull power to the pot, swap MISO/MOSI lines, kill the clock, etc... this value goes to 0...so I'm fairly certain the connection side of the communication is fine. However, the reply should be changing with each iteration, as each step advances the wiper by 10 steps...which should also show up on the ADC (Sensor Value). I've even tried swapping the endianess with no luck. Has anyone used the SPI library with success? Any hits or tips or things I can check? Quote Link to post Share on other sites
websterling 12 Posted November 23, 2012 Share Posted November 23, 2012 SPI.transfer only transfers 8 bits. This is what I'm using to transfer a 16 bit value to a DAC. void SPIWrite(int value){ // value is 16 bits // take the LE ( SS ) pin high to inhibit data latching: digitalWrite(latchEnablePin, HIGH); MSB = highByte(value); LSB = lowByte(value); SPI.transfer(MSB); SPI.transfer(LSB); // take the LE ( SS ) pin low to latch the data: digitalWrite(latchEnablePin, LOW); } mbeals 1 Quote Link to post Share on other sites
mbeals 74 Posted November 24, 2012 Author Share Posted November 24, 2012 Good to know. Thanks! Is that documented somewhere? Do you know if it only reads the first byte returned as well? Quote Link to post Share on other sites
websterling 12 Posted November 24, 2012 Share Posted November 24, 2012 There should be a sub-directory named 'reference' in your Energia directory. I think it's only 8 bits at a time either way. Quote Link to post Share on other sites
Rickta59 589 Posted November 24, 2012 Share Posted November 24, 2012 I haven't tried it but it seems like there is a library available for this chip: https://github.com/dreamcat4/Mcp4261 .. it is Arduino code but a quick scan ( not tested ) seems like it might just work. If not, at least you can see how someone else has interfaced with your chip. -rick Quote Link to post Share on other sites
mbeals 74 Posted November 24, 2012 Author Share Posted November 24, 2012 oh I see. The ref is in the folder...there just isn't a link to it from the main documentation page. That's why I couldn't find it. That other library looks interesting.....I might look into porting it over to Energia Quote Link to post Share on other sites
mbeals 74 Posted November 25, 2012 Author Share Posted November 25, 2012 There is still something goofy going on. Even once I fixed that, it's still not working. I've even tried the very simple case where I write the one byte "step" command explicitly, and am still seeing no response. I think I'm going to have to wait until I can hook it up to a scope to verify the communication. Quote Link to post Share on other sites
mbeals 74 Posted November 25, 2012 Author Share Posted November 25, 2012 Well I fixed it. I believe the big issue was using signed ints where I should have been using unsigned ints, which was screwing up the bit shifts when I would shift over by a full byte. In case anyone wants to use this device in the future, here is a working set of functions for it. There is more functionality left in the chip, but this covers the main register access and sweep functions. /* CS -> P2.0 SCK -> P1.5 SDI -> P1.7 SDO -> P1.6 */ const unsigned int WIPER0 = 0x00; const unsigned int WIPER0_default = 0x02; const unsigned int WIPER1 = 0x01; const unsigned int WIPER1_default = 0x03; const unsigned int TCON = 0x04; const unsigned int STATUS = 0x05; const unsigned int DATA0 = 0x06; const unsigned int DATA1 = 0x07; const unsigned int DATA2 = 0x08; const unsigned int DATA3 = 0x09; const unsigned int DATA4 = 0x0A; const unsigned int DATA5 = 0x0B; const unsigned int DATA6 = 0x0C; const unsigned int DATA7 = 0x0D; const unsigned int DATA8 = 0x0E; const unsigned int DATA9 = 0x0F; //Command bits const byte READ = 0b11; const byte WRITE = 0b00; const byte UP = 0b01; const byte DOWN = 0b10; const byte chipSelectPin = 8; // the sensor communicates using SPI, so include the library: #include <SPI.h> #include <TimerSerial.h> TimerSerial TS; void setup() { //Setup the software UART TS.begin(9600); //Enable the CS pin as an output pinMode(chipSelectPin, OUTPUT); // start the SPI library: SPI.begin(); delay(100); } void loop() { } /*========= Write to device register and read result ======================*/ unsigned int pushCommand(unsigned int Register, unsigned int command, unsigned int value) { unsigned int COMMAND_MOSI; unsigned int COMMAND_MISO; unsigned int DATA_MOSI; unsigned int DATA_MISO; unsigned int packet; //Format full 2 bit packet into one int to handle overlap from 10 bit data packet = (Register << 12) | (command << 10) | value; //Chop packet into command and data bits COMMAND_MOSI = (packet & 0xFF00) >> 8; DATA_MOSI = (packet & 0xFF); //Enable chip select, write two bits, read two bits, then disable CS digitalWrite(chipSelectPin, LOW); COMMAND_MISO = SPI.transfer(COMMAND_MOSI); //Send command byte DATA_MISO = SPI.transfer(DATA_MOSI); //Send the data byte digitalWrite(chipSelectPin, HIGH); //reassemble and return the returned bytes return (COMMAND_MISO << 8) | DATA_MISO; } /*========= Read value in a register ==========================*/ unsigned int readRegister(unsigned int Register){ return pushCommand(Register,READ,0); } /*======== Write to a register ============================*/ unsigned int writeRegister(unsigned int Register, unsigned int value){ return pushCommand(Register,WRITE,value); } /*======== Step a register n steps up in order ====================*/ //The device supports sequential writing, so we can sweep the wiper up or down //at the clock frequency byte stepUP(unsigned int Register, unsigned int steps){ byte COMMAND_MOSI = (Register << 4) | (UP << 2); //Format command byte byte COMMAND_MISO = 1; //Preset "success" to 1 //Cycle CS, write and read to/from Bus digitalWrite(chipSelectPin, LOW); for(int i=0; i<steps; i++){ COMMAND_MISO &= (SPI.transfer(COMMAND_MOSI) & 2) >> 1; //Issue the step command 'steps' times } digitalWrite(chipSelectPin, HIGH); return COMMAND_MISO; } /*============ Step a register n steps down in order ====================*/ byte stepDOWN(unsigned int Register, int steps){ byte COMMAND_MOSI = (Register << 4) | (DOWN << 2); byte COMMAND_MISO = 1; digitalWrite(chipSelectPin, LOW); for(int i=0; i<steps; i++){ COMMAND_MISO &= (SPI.transfer(COMMAND_MOSI) & 2) >> 1; } digitalWrite(chipSelectPin, HIGH); return COMMAND_MISO; } energia and abecedarian 2 Quote Link to post Share on other sites
abecedarian 330 Posted November 25, 2012 Share Posted November 25, 2012 Nice to know, thanks! FWIW, my motorcycle has a weird ignition system which uses a MAP sensor to advance / retard timing and I've contemplated using a digital potentiometer to emulate the MAP sensor which would allow me some control over the timing. Quote Link to post Share on other sites
mbeals 74 Posted November 25, 2012 Author Share Posted November 25, 2012 No problem. Hopefully my 3 days of fighting will help someone else hitting the same problem in the future. Doesn't your bike's PCM also set mixture off the MAP? It might be better to intercept the input signal to the coil and add a delay/advance circuit there. It would simplify the response quite a bit. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.