Author Topic: Digital Potentiometer  (Read 3270 times)

bazinjohn

  • RFduino Newbie
  • *
  • Posts: 4
  • Karma: +1/-0
    • View Profile
Digital Potentiometer
« on: June 25, 2014, 11:24:25 AM »
I'm having trouble programing this digital potentiometer over i2c.  Here is a link to the data sheet.  I am using pins 5 and 6 for SDA and SCL on the RFduino.  I can't even get it to vary the resistance.  Here is the code I have.

http://www.analog.com/static/imported-files/data_sheets/AD5272_5274.pdf


Code: [Select]
#include <Wire.h>

void setup()
{
  Wire.beginOnPins(5,6);
  Wire.begin(); // join i2c bus (address optional for master)
  //int reset_command = 4096;
  //Wire.write(reset_command);
  //int enable = 7168;
  //Wire.write(enable);
 
}

byte val = 0;

void loop()
{
  Wire.beginTransmission(46); // transmit to device #44 (0x2c)
                                // device address is specified in datasheet
  //int output = val+1024; 
  Wire.write(byte(0x00));            // sends instruction byte 
  Wire.write(val);                 // sends instruction byte 
  //Wire.write(output);             // sends potentiometer value byte 
  Wire.endTransmission();     // stop transmitting

  val++;        // increment value
  if(val == 1023) // if reached 64th position (max)
  {
    val = 0;    // start over from lowest value
  }
  delay(1000);
}

any advice will help

tolson

  • Global Moderator
  • *****
  • Posts: 866
  • Karma: +20/-0
    • View Profile
    • Thomas Olson Consulting
Re: Digital Potentiometer
« Reply #1 on: June 25, 2014, 02:56:02 PM »
1. Have you verified that the chip recognizes your communications at the address you've chosen. You can use the i2c_scanner.ino to see what address are being recognized by your devices...
http://forum.rfduino.com/index.php?topic=316.msg2092#msg2092
In the i2c_scanner.ino, be sure you change the SDA/SCL pins to meet your circuit connections.

2. You are starting the Wire interface on pins 5 and 6 with Wire.beginOnPins(5,6). Then you are starting it again with Wire.begin(). The first way already does this. I don't know if the latter one resets the pins back to 6 and 5. But remove it anyways.

3. You are defining val as a byte (8 bits) and then using it to count to 1024 which is bigger than a byte. I don't know how arduino code deals with that.

4. In your loop you are alway sending 0x00 followed by some data. OpCode 0x00 is a NoOP for the AD527x,  which means the 2nd byte you send is ignored. So you will never see any wiper changes on the POT.

Sending data to the RDAC wiper setting register is more complex than shown. The command for the AD527x is the middle 4 bits of the 1st byte you are sending as the command byte. The data is made up of the bottom 2 bits of the 1st byte you send plus the 8 bits from the second byte.

I'll leave the details of merging that info into the proper 2 bytes up to you. But, here is simple demo how we can send just 256 levels....

Code: [Select]
#include <Wire.h>

void setup(){
  Wire.beginOnPins(5,6);
}

uint8_t val = 0;  // for this demo 8 bits is good enough.

void loop(){

  Wire.beginTransmission(46);   // Need to verify that chip address is 0x2e. I.E. AD527x pin10 open circuit and not bi-polar power supply
  Wire.write(0x04);  // 0b00 0001 00 ... write to RDAC
  Wire.write(val);
  Wire.endTransmission();

  if(val++ >= 0xFF){
    val = 0;
  }

  delay(1000);
}












« Last Edit: June 25, 2014, 03:39:18 PM by tolson »

bazinjohn

  • RFduino Newbie
  • *
  • Posts: 4
  • Karma: +1/-0
    • View Profile
Re: Digital Potentiometer
« Reply #2 on: June 26, 2014, 11:22:54 AM »
Thank you that was very helpful.  I used the I2C scanner and verified that the address is indeed 46.  However, there needs to be a reset command sent to the POT first before it can be programmed.  I'm still a little confused on how to actually send commands to the POT specifically the format that it wants.  This is what I have for the reset.

Code: [Select]
  int reset_command = 4096;
  Wire.write(reset_command);
  int enable = 7168;
  Wire.write(enable);

Those values I got from table 12 in the data sheet to my interpretation of it.  I tried using the demo code but the resistance didn't move. 

tolson

  • Global Moderator
  • *****
  • Posts: 866
  • Karma: +20/-0
    • View Profile
    • Thomas Olson Consulting
Re: Digital Potentiometer
« Reply #3 on: June 26, 2014, 12:34:25 PM »
The reset value in decimal is correct at 4096 or 0x1000 in hex.

You are trying to send a 16 bit value in one simple Wire.write command which expects just a byte. I see this alot in arduino sketches which can confuse new users to the C language. I guess the compiler doesn't force the use of a cast to make the integer result a byte value.  It seems to just drop the high byte bits and pass the low byte bits. For correctness in portablility I think the use of a cast is more correct.

I think coding for low level driver stuff is easier to understand if you don't convert to decimal values to enter into your programs. But that is just me.

Here is the send reset command using typical arduino style coding. I tend to do the same thing as it seems to work without compiler complaints.. so I get lazy as well.
Code: [Select]
int reset_command  = 0x1000;  // same as 4096 decimal.

Wire.write(reset_command >> 8);   // The high byte needs to be sent first.
Wire.write(reset_command & 0xFF);   // Then the low byte.

Here is example of casting a 16 bit integer to 8 bits.
Code: [Select]
Wire.write( (byte)((reset_command >> 8) & 0x00FF)));
Wire.write( (byte)(reset_command & 0x00FF));



tolson

  • Global Moderator
  • *****
  • Posts: 866
  • Karma: +20/-0
    • View Profile
    • Thomas Olson Consulting
Re: Digital Potentiometer
« Reply #4 on: June 26, 2014, 12:50:05 PM »
However, there needs to be a reset command sent to the POT first before it can be programmed.
 . . .
Code: [Select]
  int reset_command = 4096;
  Wire.write(reset_command);  Wire.write(reset_command);
  int enable = 7168;
  Wire.write(enable);

By the way, I am not sure you really have to do the reset. I think the reset just loads the RDAC from the locked in value from 50TP if you want that.

Also, I would not be messing with the enable until you got everything else working. The enable command enables you to write your current
wiper position to what they call the 50TP. It is like the old OTP(One Time Programmable) register except it lets you write to it 50 times. After that you
can not change the permanent value. So you don't want to accidentally use up your 50 chances and be stuck with some wrong value.

If you don't initially program the 50TP the POT should just come up with the wiper at mid-value. I think.

Have fun!


bazinjohn

  • RFduino Newbie
  • *
  • Posts: 4
  • Karma: +1/-0
    • View Profile
Re: Digital Potentiometer
« Reply #5 on: June 26, 2014, 01:11:20 PM »
Measuring the resistance between W and A it just sits at 10k when I give it power.  The SCL and SDA pins are wired with 10k pull up resistors.

The reset value in decimal is correct at 4096 or 0x1000 in hex.

You are trying to send a 16 bit value in one simple Wire.write command which expects just a byte. I see this alot in arduino sketches which can confuse new users to the C language. I guess the compiler doesn't force the use of a cast to make the integer result a byte value.  It seems to just drop the high byte bits and pass the low byte bits. For correctness in portablility I think the use of a cast is more correct.

I think coding for low level driver stuff is easier to understand if you don't convert to decimal values to enter into your programs. But that is just me.

Here is the send reset command using typical arduino style coding. I tend to do the same thing as it seems to work without compiler complaints.. so I get lazy as well.
Code: [Select]
int reset_command  = 0x1000;  // same as 4096 decimal.

Wire.write(reset_command >> 8);   // The high byte needs to be sent first.
Wire.write(reset_command & 0xFF);   // Then the low byte.

Here is example of casting a 16 bit integer to 8 bits.
Code: [Select]
Wire.write( (byte)((reset_command >> 8) & 0x00FF)));
Wire.write( (byte)(reset_command & 0x00FF));




Even if I don't need the reset, I will still need to send the enable command in order to get the resistance to change from 10k right? If so will it be the same format as above for the reset? (casting a 16 bit integer to 8 bits as well)

tolson

  • Global Moderator
  • *****
  • Posts: 866
  • Karma: +20/-0
    • View Profile
    • Thomas Olson Consulting
Re: Digital Potentiometer
« Reply #6 on: June 26, 2014, 05:31:14 PM »
Quote
Even if I don't need the reset, I will still need to send the enable command in order to get the resistance to change from 10k right? If so will it be the same format as above for the reset? (casting a 16 bit integer to 8 bits as well)

Yes, you are getting it. But...
The decimal number you are calling enable, 7168, is 0x1C00. Please let's start using hex or binary since we are dealing with registers at the bit levels.
bit0  0(default) means 50TP disabled. 1 means enable 50TP.
bit1  0(default) means wiper position frozen to the value in 50TP. 1 means allow wiper position from the digital interface.
bit2  0(default) RDAC calibration enabled. 1 is disabled
bit3  is read only

So by default it is already set the way you are trying to set it. And bit1 is telling it to use the burned value so you are always going to get a fixed wiper value.  What you want for controlling the wiper in real time is to set bit1 to 1.

So send 0x1C02. The high byte first (0x1C) the low byte second(0x02).
Your sketch now looks like this...

Quote

#include <Wire.h>

void setup(){
  Wire.beginOnPins(5,6);

// set control register to use wiper control from serial port instead of burned value.
  Wire.beginTransmission(46);
  Wire.write(0x1C);  // 0b00 0111 00 ... write high byte to control register
  Wire.write(0x02);  // 0b0000 0020 ... write low byte to control register
  Wire.endTransmission();
}

uint8_t val = 0;  // for this demo 8 bits is good enough.

void loop(){

  Wire.beginTransmission(46);   // Need to verify that chip address is 0x2e. I.E. AD527x pin10 open circuit and not bi-polar power supply
  Wire.write(0x04);  // 0b00 0001 00 ... write to RDAC
  Wire.write(val);
  Wire.endTransmission();

  if(val++ >= 0xFF){
    val = 0;
  }

  delay(1000);
}


« Last Edit: June 27, 2014, 10:22:06 AM by tolson »

bazinjohn

  • RFduino Newbie
  • *
  • Posts: 4
  • Karma: +1/-0
    • View Profile
Re: Digital Potentiometer
« Reply #7 on: June 27, 2014, 08:30:58 AM »
Thanks! I got it to work with this code

Code: [Select]

#include <Wire.h>

void setup()
{
  Wire.beginOnPins(5,6);

}

void loop(){

    while (1)
    {
      write_value(1022);
      delay(500);
    }
//}
}
void write_value(int resistance) {
  int value = 1024 + resistance;
  int enable_rdac = 0x1C03;
 
  Wire.beginTransmission(46);
 
  Wire.write(enable_rdac >> 8);
  Wire.write(enable_rdac & 0xFF);
 
  Wire.write(value >> 8);
  Wire.write(value & 0xFF);
  Wire.endTransmission();
}


 

anything