Solved: Need Help getting my SPI-controlled digital pot to write to its EEPROM

EDIT, 25 May 2014: SOLVED - big thanks to SurferTim! - SEE REPLY 3 BELOW FOR FINAL CODE (Solved: Need Help getting my SPI-controlled digital pot to write to its EEPROM - #4 by panther3001 - Networking, Protocols, and Devices - Arduino Forum)


Original post:

Hello, thanks in advance for any assistance you can provide.

I am using a MAX5481 1024-step digital potentiometer, with internal Non-volatile (EEPROM) memory, to store the wiper position. This way, during reboot or reset, the pot will automatically go to the wiper position stored in EEPROM.

Datasheet here: http://datasheets.maximintegrated.com/en/ds/MAX5481-MAX5484.pdf

I have successfully been able to command the device to go to a desired wiper setting, but I have spent many hours now trying to store this command into its EEPROM, and then test that it worked successfully, to no avail. It appears to be NOT storing the command into its EEPROM even when I am commanding it to do so, so that's where I need your help please.

The SPI commands are defined on pgs. 14-16, and I have very carefully studied the datasheet and set up my communication according to its specs.
-I am not 100% sure about SPI mode, however, but it appears to be SPI_MODE0. I am using the default clock speed of 4MHz, and the device supports up to 7MHz.

My code is as follows:
-It uses the Serial Monitor to receive commands. Set your Serial Monitor baud rate to 115200, and set the end of line character to "newline."
-Type an integer from 0-1023 to set the wiper. - This WORKS!
-Type "t" to turn on the EEPROM store mode, so that it also writes the next integer command to the digital pot's EEPROM. - This appears to not be working.
-Type "f" to turn off the EEPROM store mode, so that when you type the next integer command it only moves the wiper, but does not get stored into EEPROM. - this appears to work.
-Tyep "c" to command the value in EEPROM to get written to the wiper. - this appears NOT to work.

Any help is GREATLY appreciated. Thanks!

/*
write_to_MAX5481 
-Code to command this 10-bit digital potentiometer
By Gabriel Staples
http://electricrcaircraftguy.blogspot.com/
23 May 2014

Circuit:
Pin  Name      Connect to what?
1    VDD       +5V (Important: connect a 0.1uF [or larger] ceramic capacitor from VDD to GND, as close to the device as possible) [I'm using a 1uF multi-layer ceramic cap]
2    GND       GND/0V
3    CS (SS)   Any Arduino pin, let's use D9
4    SCK       Arduino pin D13
5    MOSI      Arduino pin D11
6    SPI/UD    +5V to select SPI mode
7    X
8    X
9    X
10   L         GND/0V; this is the LOW side of the voltage divider
11   W         Vout; this is the wiper on the potentiometer
12   H         +5V; this is the HIGH side of the voltage divider
13   X
14   VSS       GND/0V (make sure to tie this to GND)
*/

#include <SPI.h>

//Global Variables
const uint8_t slaveSelectPin = 9; //SPI Slave Select pin
float VDD_measured = 4.4; //measured supply voltage to the device

void setup()
{
  Serial.begin(115200);
  
  //set the Slave Select pin as an output
  pinMode(slaveSelectPin,OUTPUT);
  
  //initialize SPI
  SPI.begin(); 
  SPI.setDataMode(SPI_MODE0); //I think this is the correct mode
//  SPI.setDataMode(SPI_MODE2); //test mode
//  SPI.setBitOrder(MSBFIRST); //this is correct; the LSBFIRST bit order definitely does NOT work--it scrambles the commands (I checked)
  
  Serial.println("Enter your desired command (0-1023) as an int, with end of line char on. \nType just a \"c\" to copy the EEPROM val to the wiper. ");
}

void loop()
{
  static boolean writeEEPROM = false;
  if (Serial.available()>0)
  {
    if (Serial.peek()=='c')
    {
      Serial.println("copying EEPROM value to wiper register");
      Serial.read(); //read in the 'c'
      Serial.read(); //read in the '\n'
      copy_EEPROM_to_wiper;
    }
    else if (Serial.peek()=='t')
    {
      Serial.println("writeEEPROM = true");
      Serial.read(); //read in the 't'
      Serial.read(); //read in the '\n'
      writeEEPROM = true;
    }
    else if (Serial.peek()=='f')
    {
      Serial.println("writeEEPROM = false");
      Serial.read(); //read in the 'f'
      Serial.read(); //read in the '\n'
      writeEEPROM = false;
    }
    else //the first char coming in is NOT a 'c', 't', or 'f', so it must be the wiper command
    {
      unsigned int command = Serial.parseInt();
      if (Serial.read()=='\n') //read in the last char & ensure it is an end-of-line char
      {
        command = constrain(command,0,1023);
        float V_out_calculated = command/1023.0*VDD_measured;
     
        //print data
        Serial.print("command = "); Serial.println(command);
        Serial.print("V_out_calculated = "); Serial.println(V_out_calculated);
        
        digitalPotWrite(command, writeEEPROM);
      }
    }
  } //end of if serial is available
} //end of loop()


void digitalPotWrite(unsigned int command, boolean writeEEPROM)
{
  //local constants
  const byte WRITE_WIPER = 0x00; //command to write to the wiper register only
  const byte WRITE_EEPROM = 0x20; //command to copy the wiper register into the non-volatile memory (EEPROM) of the digital pot
  const byte COPY_EEPROM_TO_WIPER = 0x30; //command to copy the EEPROM value to the wiper register
  
  //command the new wiper setting (requires sending 3 bytes)
  digitalWrite(slaveSelectPin,LOW); //set the SS pin low to select the chip
  SPI.transfer(WRITE_WIPER); //Byte 1: the command byte
  SPI.transfer(highByte(command<<6)); //Byte 2: the upper 8 bits of the 10-bit command: (D9.D8.D7.D6.D5.D4.D3.D2)
  SPI.transfer(lowByte(command<<6)); //Byte 3: the lower 2 bits of the 10-bit command, with 6 zeros to the right of them: (D1.D0.x.x.x.x.x.x)
  digitalWrite(slaveSelectPin,HIGH); //set the SS pin high to "latch the data into the appropriate control register" (see datasheet pg. 14)
  
  //copy the wiper register into the non-volatile memory (EEPROM) of the digital pot, if commanded (requires sending only 1 byte)
  if (writeEEPROM)
  {
    Serial.println("writing to EEPROM");
    delay(10); //wait a short time for the previous command to get properly set
    digitalWrite(slaveSelectPin,LOW); //set the SS pin low to select the chip
    SPI.transfer(WRITE_EEPROM); //Byte 1: the command byte
    digitalWrite(slaveSelectPin,HIGH); //set the SS pin high to "latch the data into the appropriate control register" (see datasheet pg. 14 & 16)
    delay(13); //wait 13ms (see datasheet pg. 16 under the paragraph titled "Copy Wiper Register to NV Register"--they require a 12ms wait time, so I'll wait 13ms to be sure)
    Serial.println("done writing to EEPROM");
  }
}

//copy the value stored in the EEPROM to the wiper register, to command the wiper to go there
void copy_EEPROM_to_wiper()
{
  const byte COPY_EEPROM_TO_WIPER = 0x30; //command to copy the EEPROM value to the wiper register
  digitalWrite(slaveSelectPin,LOW); //set the SS pin low to select the chip
  SPI.transfer(COPY_EEPROM_TO_WIPER); //Byte 1: the command byte
  digitalWrite(slaveSelectPin,HIGH); //set the SS pin high to "latch the data into the appropriate control register" (see datasheet pg. 14 & 16)
}

I don't know about the digital pot, but this function call does not look correct.

    if (Serial.peek()=='c')
    {
      Serial.println("copying EEPROM value to wiper register");
      Serial.read(); //read in the 'c'
      Serial.read(); //read in the '\n'
      // change this
      copy_EEPROM_to_wiper;
      // to this
      copy_EEPROM_to_wiper();

    }

Hey thanks for the catch! I've got to run to church right now but I really hope that is it! Simple mistake. I'm not sure why it didn't throw a compiler error with that.

SurferTim,

THANK YOU SOOOOO MUCH FOR TAKING THE TIME TO LOOK AT MY CODE AND HELP ME! It was a simple mistake, and you caught it. I spent over 3.5 hrs stuck on this stupid thing, after writing all this code, with that being my only error the whole time. Since this is my first time talking to a device via SPI, from scratch, with no library or anything, I was sure I was doing something wrong there, sending the commands and all, or that my device was broken, since I did the TSSOP-14 surface-mount soldering myself and didn't know what else to think. It turns out, however, that you are spot-on, and my only problem was forgetting the two parentheses () at the end of a function call.

Problem solved!
Sincerely,
Gabriel Staples
http://electricrcaircraftguy.blogspot.com/
My final, working code is here:

/*
write_to_MAX5481 
-Code to command this 10-bit digital potentiometer
By Gabriel Staples
http://electricrcaircraftguy.blogspot.com/
23 May 2014

Circuit:
Pin  Name      Connect to what?
1    VDD       +5V (Important: connect a 0.1uF [or larger] ceramic capacitor from VDD to GND, as close to the device as possible) [I'm using a 1uF multi-layer ceramic cap]
2    GND       GND/0V
3    CS (SS)   Any Arduino pin, let's use D9
4    SCK       Arduino pin D13
5    MOSI      Arduino pin D11
6    SPI/UD    +5V to select SPI mode
7    X
8    X
9    X
10   L         GND/0V; this is the LOW side of the voltage divider
11   W         Vout; this is the wiper on the potentiometer
12   H         +5V; this is the HIGH side of the voltage divider
13   X
14   VSS       GND/0V (make sure to tie this to GND)
*/

/*
===================================================================================================
  LICENSE & DISCLAIMER
  Copyright (C) 2014 Gabriel Staples.  All right reserved.
  
  ------------------------------------------------------------------------------------------------
  License: GNU Lesser General Public License Version 3 (LGPLv3) - https://www.gnu.org/licenses/lgpl.html
  ------------------------------------------------------------------------------------------------
  
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with this program.  If not, see http://www.gnu.org/licenses/
===================================================================================================
*/

#include <SPI.h>

//Global Variables
const uint8_t slaveSelectPin = 9; //SPI Slave Select pin
float VDD_measured = 4.4; //measured supply voltage to the device

void setup()
{
  Serial.begin(115200);
  
  //set the Slave Select pin as an output
  pinMode(slaveSelectPin,OUTPUT);
  
  //initialize SPI
  SPI.begin(); 
  
//  SPI.setDataMode(SPI_MODE0); //I think this is the correct mode; I also think this is the default mode, so I'll just comment this out
//  SPI.setDataMode(SPI_MODE2); //test mode
//  SPI.setBitOrder(MSBFIRST); //this is correct; the LSBFIRST bit order definitely does NOT work--it scrambles the commands (I checked); Note: default mode is
                               //"MSBFIRST," so I'll just comment this line out
  
  Serial.println(F("Enter your desired command (0-1023) as an int, with end of line char on.\n"
                 "Type just a \"c\" to copy the EEPROM val to the wiper.\n"
                 "Type a \"t\" to have future commands also get stored into the pot's EEPROM.\n"
                 "Type a \"f\" to NOT have future commands also get stored into the pot's EEPROM"));
}

void loop()
{
  static boolean writeEEPROM = false;
  if (Serial.available()>0)
  {
    if (Serial.peek()=='c')
    {
      Serial.println("copying EEPROM value to wiper register");
      Serial.read(); //read in the 'c'
      Serial.read(); //read in the '\n'
      copy_EEPROM_to_wiper();
    }
    else if (Serial.peek()=='t')
    {
      Serial.println("writeEEPROM = true");
      Serial.read(); //read in the 't'
      Serial.read(); //read in the '\n'
      writeEEPROM = true;
    }
    else if (Serial.peek()=='f')
    {
      Serial.println("writeEEPROM = false");
      Serial.read(); //read in the 'f'
      Serial.read(); //read in the '\n'
      writeEEPROM = false;
    }
    else //the first char coming in is NOT a 'c', 't', or 'f', so it must be the wiper command
    {
      unsigned int command = Serial.parseInt();
      if (Serial.read()=='\n') //read in the last char & ensure it is an end-of-line char
      {
        command = constrain(command,0,1023);
        float V_out_calculated = command/1023.0*VDD_measured;
     
        //print data
        Serial.print("command = "); Serial.println(command);
        Serial.print("V_out_calculated = "); Serial.println(V_out_calculated);
        
        digitalPotWrite(command, writeEEPROM);
      }
    }
  } //end of if serial is available
} //end of loop()


void digitalPotWrite(unsigned int command, boolean writeEEPROM)
{
  //local constants
  const byte WRITE_WIPER = 0x00; //command to write to the wiper register only
  const byte WRITE_EEPROM = 0x20; //command to copy the wiper register into the non-volatile memory (EEPROM) of the digital pot
//  const byte COPY_EEPROM_TO_WIPER = 0x30; //command to copy the EEPROM value to the wiper register
  
  //command the new wiper setting (requires sending 3 bytes)
  digitalWrite(slaveSelectPin,LOW); //set the SS pin low to select the chip
  SPI.transfer(WRITE_WIPER); //Byte 1: the command byte
  SPI.transfer(highByte(command<<6)); //Byte 2: the upper 8 bits of the 10-bit command: (D9.D8.D7.D6.D5.D4.D3.D2)
  SPI.transfer(lowByte(command<<6)); //Byte 3: the lower 2 bits of the 10-bit command, with 6 zeros to the right of them: (D1.D0.x.x.x.x.x.x)
  digitalWrite(slaveSelectPin,HIGH); //set the SS pin high to "latch the data into the appropriate control register" (see datasheet pg. 14)
  
  //copy the wiper register into the non-volatile memory (EEPROM) of the digital pot, if commanded (requires sending only 1 byte)
  if (writeEEPROM)
  {
    Serial.println("writing to EEPROM");
    delay(10); //wait a short time for the previous command to get properly set
    digitalWrite(slaveSelectPin,LOW); //set the SS pin low to select the chip
    SPI.transfer(WRITE_EEPROM); //Byte 1: the command byte
    digitalWrite(slaveSelectPin,HIGH); //set the SS pin high to "latch the data into the appropriate control register" (see datasheet pg. 14 & 16)
    delay(13); //wait 13ms (see datasheet pg. 16 under the paragraph titled "Copy Wiper Register to NV Register"--they require a 12ms wait time, so I'll wait 13ms to be sure)
    Serial.println("done writing to EEPROM");
  }
}

//copy the value stored in the EEPROM to the wiper register, to command the wiper to go there
void copy_EEPROM_to_wiper()
{
  const byte COPY_EEPROM_TO_WIPER = 0x30; //command to copy the EEPROM value to the wiper register
  digitalWrite(slaveSelectPin,LOW); //set the SS pin low to select the chip
  SPI.transfer(COPY_EEPROM_TO_WIPER); //Byte 1: the command byte
  digitalWrite(slaveSelectPin,HIGH); //set the SS pin high to "latch the data into the appropriate control register" (see datasheet pg. 14 & 16)
}

Code now posted to my website, and to Github (link on website). ElectricRCAircraftGuy.com--RC, Arduino, Programming, & Electronics: MAX5481 10-bit Digital Potentiometer Arduino Code