Digital potentiometer AD5235 - SPI - Working example

Hi,

In order for this to work proper don't forget to add 2.2kOhm pull up resistors on READY and SDO pins

Connections

CLK - CLK
SDI - MOSI
SDO - MISO
CS - PB1 - pin 9
RDY - A3

Measure the resistance on terminals B1 and B2, A1 and A2 are going in the opposite way
Read the data sheet for timings and settings.

Tested on ATMega 328p @24MHz - to have a clue about the SPI speed
Code

//GROZEA Ion
//www.grozeaion.com
//Open Source, licensed under a Creative Commons Attribution-NonCommercial 3.0 License (http://creativecommons.org/licenses/by-nc/3.0/)
//License Agreement
//
//Copyright (c) 2015 GROZEA Ion (www.grozeaion.com)
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without modification,
//are permitted provided that the following conditions are met:
//
//* Redistributions of source code must retain the above copyright notice, this
//  list of conditions and the following disclaimer.
//
//* Redistributions in binary form must reproduce the above copyright notice, this
//  list of conditions and the following disclaimer in the documentation and/or
//  other materials provided with the distribution.
//
//* The name of the copyright holder may not be used to endorse or promote
//  products derived from this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
//IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
//MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
//SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
//SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
//OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
//IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
//ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//POSSIBILITY OF SUCH DAMAGE.


//Control Registers
#define CMD__NOTHING	B00000000 //0  - Do nothing
#define CMD_MEM2RDAC	B00010000 //1  - Restore EEMEM (A0) contents to RDAC (A0) register. See Table 16.
#define CMD_RDAC2MEM	B00100000 //2  - Store wiper setting. Store RDAC (A0) setting to EEMEM (A0). See Table 15. - Use a delay of 50ms!!!
#define CMD_USER2MEM	B00110000 //3  - Store contents of Serial Register Data Byte 0 and Serial Register Data Bytes 1 (total 16 bits) to EEMEM (ADDR). See Table 18.- Use a delay of 50ms!!!
#define CMD_DECRE6DB	B01000000 //4  - Decrement by 6 dB. Right-shift contents of RDAC (A0) register, stop at all 0s.
#define CMD_DEALL6DB	B01010000 //5  - Decrement all by 6 dB. Right-shift contents of all RDAC registers, stop at all 0s.
#define CMD_DECR1STP	B01100000 //6  - Decrement contents of RDAC (A0) by 1, stop at all 0s.
#define CMD_DECA1STP	B01110000 //7  - Decrement contents of all RDAC registers by 1, stop at all 0s.
#define CMD_ALL2RDAC	B10000000 //8  - Reset. Refresh all RDACs with their corresponding EEMEM previously stored values. - Use a delay of 30us!!!
#define CMD_GETEMEM	B10010000 //9  - Read contents of EEMEM (ADDR) from SDO output in the next frame. See Table 19. - Use a delay of 30us!!!
#define CMD_GET_RDAC	B10100000 //10 - Read RDAC wiper setting from SDO output in the next frame. See Table 20. - Use a delay of 30us!!!
#define CMD_SET_RDAC	B10110000 //11 - Write contents of Serial Register Data Byte 0 and Serial Register Data Byte 1 (total 10 bits) to RDAC (A0). See Table 14.
#define CMD_INCRE6DB	B11000000 //12 - Increment by 6 dB: Left-shift contents of RDAC (A0),stop at all 1s. See Table 17.
#define CMD_INALL6DB	B11010000 //13 - Increment all by 6 dB. Left-shift contents of all RDAC registers, stop at all 1s.
#define CMD_INCR1STP	B11100000 //14 - Increment contents of RDAC (A0) by 1, stop at all 1s. See Table 15.
#define CMD_IALL1STP	B11000000 //15 - Increment contents of all RDAC registers by 1, stop at all 1s.

//EEMEM No.--Address--EEMEM Content for …
//1 		0000 		RDAC1
//2 		0001 		RDAC2
//3 		0010 		USER1
//4		0011 		USER2
//… 		… 			…
//15 		1110 		USER13
//16 		1111 		RAB1 tolerance

// Macros - Toogle this pin to repeat last command
//This is PIN9 on arduino Uno/Duemilanove
#define CS_ON	PORTB |=  (1<<1)
#define CSOFF	PORTB &= ~(1<<1)
uint8_t myVal = 0;

void setup() 
{
  Serial.begin(9600);
  //Setup SPI and start it
  DDRB |= B0101110;//Set digital pin 10SS, 11MOSI and 13SCK as output
  //See https://sites.google.com/site/qeewiki/books/avr-guide/spi for setting the SPI
  SPSR = B00000000;//In my case Clock / 2 = 12Mhz is not working so i use Clock / 4
  SPCR = B01010000;
  DDRC &= ~(1<<0); //A3 Input - Read Status
  PORTC |= B00000111;// Turn on 20K pullup on analog 0, 1, 2
  CS_ON;
  delay(15);
  setWiper(0, 999);
  setWiper(1, 333);
  Serial.print("W1 Value : "); Serial.println(getWiper(0)); 
  Serial.print("W2 Value : "); Serial.println(getWiper(1));
  stepUpOne(0);
  stepUpOne(1);
  Serial.print("Increase W0 with 1 : "); Serial.println(getWiper(0));
  Serial.print("Increase W1 with 1 : "); Serial.println(getWiper(1));
  stepUpOne(0);
  repeatCMD();
  stepUpOne(1);
  repeatCMD();
  Serial.print("W1 after repeat : "); Serial.println(getWiper(0));
  Serial.print("W2 after repeat : "); Serial.println(getWiper(1));
  stepDownSix(0);
  stepDownSix(1);
  Serial.print("Decrease W0 with 6dB : "); Serial.println(getWiper(0));
  Serial.print("Decrease W1 with 6dB : "); Serial.println(getWiper(1));
  stepDownSix(0);
  repeatCMD();
  stepDownSix(1);
  repeatCMD();
  Serial.print("W1 after repeat -6dB: "); Serial.println(getWiper(0));
  Serial.print("W2 after repeat -6dB: "); Serial.println(getWiper(1));  
  SPCR &= ~_BV(SPE);//End SPI
  
}

void loop() 
{
}

void setWiper(uint8_t w, uint16_t value)
{
  CSOFF;
  myVal = transferData(CMD_SET_RDAC + w);
  myVal = transferData(value >> 8);
  myVal = transferData(value & 0xFF);
  CS_ON;
}

uint16_t getWiper(uint8_t w)
{
  uint16_t ret;
  CSOFF; 
  myVal = transferData(CMD_GET_RDAC + w);
  myVal = transferData(CMD__NOTHING);
  myVal = transferData(CMD__NOTHING);
  CS_ON;
//  __asm__("nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"
//          "nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"
//          "nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"
//          "nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t"
//          "nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t""nop\n\t");//only 11 on this row are enought or 3.2us only for new chips at room temp
  delayMicroseconds(30);
  CSOFF;
  myVal = transferData(CMD__NOTHING); //Discard first byte
  ret = (transferData(CMD__NOTHING) << 8);
  ret += transferData(CMD__NOTHING);
  CS_ON;
  return ret;
}

void stepUpOne(uint8_t w)
{
  CSOFF;
  myVal = transferData(CMD_INCR1STP + w);
  myVal = transferData(CMD__NOTHING);
  myVal = transferData(CMD__NOTHING);
  CS_ON;
}

void stepDownSix(uint8_t w)
{
  CSOFF;
  myVal = transferData(CMD_DECRE6DB + w);
  myVal = transferData(CMD__NOTHING);
  myVal = transferData(CMD__NOTHING);
  CS_ON;
}

void repeatCMD()//See page 21 in manual Another subtle feature of the AD5235 is that a subsequent CS strobe, without clock and data, repeats a previous command
{
  CSOFF;
  CS_ON;
}

uint8_t transferData(uint8_t data)
{
    SPDR = data;// send the data
    while(!(SPSR & (1<<SPIF)))  // wait until transmission is complete
    ;
    return SPDR;
}

Didital potentiometer AD5235 - SPI - Working example

It's good to have a device that did it all, instead of just some of it.

Now is digital :slight_smile: