New library for the AD5668 DAC

I've created a new library for use with the Analog Devices AD5668, an 8-channel, SPI interface Analog to Digital Converter chip, located at GitHub - bobhart/AD5668-Library: An Arduino library for the Analog Devices AD5668 Digital-to-Analog Converter. This library implements all 9 commands for the chip, and has constructors for both hardware and software SPI. I have tested all functions, and everything seems to work well. This is my first library, so of course suggestions and helpful criticisms would be appreciated. (Needless to say, I'm extremely excited about this!)

I created this library because after an exhaustive search, I could only find three projects that used this chip, including a 32 step sequencer by Christer Janson that I've been inspired by, but none of them provided methods for all the commands. Also, after reviewing posts on both the Analog Devices and TI web sites (TI has a compatible part DAC8568), I realized the initialization sequence didn't need to be as complicated as what was previously done. In fact, using the functions this library provides, the user could bypass my init() function and write their own sequence in startup() to set up the chip to some other initial condition.

I hope others will find this useful.

Bob Hart

Thanks for sharing!

A quick look

int _mosiPin, _sclkPin, _ssPin, _ldacPin, _clrPin, _vRegBit, _channel, _channelsN, _clearCode;

Think a number of these internal variables can be uint8_t (byte), thereby reducing memory footprint
(memory is a valuable resource)

you can reduce footprint e.g. in the init is quite some double code.

void AD5668::init(int vRegBit, int channelsN) 
{
  if (_hwSPI) 
  {
    SPI.begin();
    SPI.setBitOrder(MSBFIRST);
  }
  else
  {
    digitalWrite(_mosiPin, LOW);
    digitalWrite(_sclkPin, LOW);
  }
  _vRegBit = vRegBit;
  digitalWrite(_ssPin, LOW);
  if (_ldacPin > 0) 
  {
     digitalWrite(_ldacPin, HIGH);
  }
  _channelsN = channelsN;
  digitalWrite(_clrPin, LOW);
  delayMicroseconds(1);
  digitalWrite(_clrPin, HIGH);
  delayMicroseconds(1);
  writeDAC(SETUP_INTERNAL_REF, 0, 0, _vRegBit);
  delayMicroseconds(1);
  powerDAC_Normal(_channelsN);
}

slightly faster as shifts take one clock cycle per position.

  byte b1 = B00000000|command; //padding at beginning of byte 1
  byte b2 = address << 4 | data >> 12; //4 address bits and 4 MSBs of data
  byte b3 = (data << 4) >> 8; // middle 8 bits of data
  byte b4 = (data << 12) >> 8 | function;

could be

  byte b1 = command;
  byte b2 = address << 4 | data >> 12;  // 4 address bits and 4 MSBs of data
  byte b3 = data >> 4;    // will be clipped automatically            // middle 8 bits of data
  byte b4 = 0xF0 & (data << 4)  | function;

give it a try

Thanks Rob, I'm trying out your suggestions now. I've also discovered some minor mistakes in the code that I'm working on correcting, so for those who may have downloaded the library, I'll be updating it with changes in the next couple of days.

Bob Hart

Rob,

Making the changes to the data types, changing how variables b2 and b4 in the writeDAC function are built and consolidating the code in the init function save a total of 40 bytes of program memory and 8 bytes of dynamic memory in my AD5668_SoftLDAC example! Thanks for the suggestions!

Unfortunately, your suggestion for optimizing the line for building variable b3 in the writeDAC function didn't give correct results when writing an output value, so I kept the line as is. I've also cleaned up a few of the other functions where possible and corrected some errors in variables in some of the functions (I forgot to change to the private ones in the call to writeDAC). I've also made corrections to the keywords.txt file, as well as spelling errors in the README.

It's getting rather late for me, so I'll upload the changes hopefully tomorrow.

Bob Hart

Good to hear it saved quite some bytes.

wrt b3, you might need to add a mask byte b3 = 0xFF & (data >> 4);

assume data = 0xABCD
org code
data << 4 ==> 0xBCD0
(data << 4) >> 8 ==> 0x00BC

proposed code
(data >> 4) ==> 0x0ABC
0xFF & (data >> 4) ==> 0x00BC

should be the same.

Rob,

Actually, it turns out your first suggestion for b3 was correct, I think what I did last night was accidentally reverse the shift operator so it was shifting left instead of right (I was getting pretty tired). I tried it again tonight and every value I threw at it came out correct.

I also found a huge error in the command functions that set individual channel bits (powerDAC_x, setSoftLDAC). In the line that builds the value for chA_D, I was doing 0x0F | _channelsN, when it should have been 0x0F & _channelsN. I think the way I had written the test sketches had masked the error.

I've also had a major re-think on the init function. Due to the fact that the chip can be configured to such a large number of different start-up conditions, I've decided to limit the init function to only initializing the communication pins and clearing the chip. This will both simplify using the function and allow the user to decide which configuration commands suits their needs in their application by placing them in the sketch's setup function. I'm re-writing the examples to work with this change, and hopefully I can think of a few different configurations to show in more examples.

I will try to finish the edits tonight, hopefully I can get them uploaded tomorrow or Friday. Thanks again for all your help.

Bob Hart

Welcome,
I'll see the new version pop up and although I do not have that DAC I might review it again :wink:

I have committed the changes to github. This version was bumped to 1.1 due to the major change to the init function. it also implements Rob's suggestions with significant memory savings, and fixes a major bug in all functions that set register bits for individual channels. I have re-written the examples to reflect the changes.

If you previously downloaded the library, you should replace it with the update due to the above-mentioned bugs as those functions in the old code DO NOT WORK. I have verified that values are now correctly written.

Bob Hart

Rob Tillaart,

I was wondering if you've had a chance to see the changes I had made to the library. I really appreciated your help and would love to know your thoughts.

Bob Hart

Not yet :frowning:

some consistency (multiple places)

void writeChannel(uint8_t channel, unsigned int value);
==>
void writeChannel(uint8_t channel, uint16_t value);

** if (_ldacPin > 0) {**
** pinMode(_ldacPin, OUTPUT);**
** }**
you allow to interfere with hardware serial, but should it not be ldacPin >= 0

void AD5668::writeDAC

you could place b1..b4 in an array b[4], and use for loops, might just decrease the footprint a bit (not tested)

void AD5668::writeDAC(uint8_t command, uint8_t address, uint16_t data, uint8_t function) 
{
  byte b[4];
  b[0] = command;
  b[1] = address << 4 | data >> 12; //4 address bits and 4 MSBs of data
  b[2] = data >> 4;                 // middle 8 bits of data
  b[3] = 0xF0 & (data << 4) >> 8 | function;
  
  digitalWrite(_ssPin, LOW);
  delayMicroseconds(1);
  
  for (uint8_t i = 0; i < 4; i++)  
  if (_hwSPI) SPI.transfer(b[i]);
  else shiftOut(_mosiPin, _sclkPin, MSBFIRST, b[i]);
  
  delayMicroseconds(1);
  digitalWrite(_ssPin, HIGH);
}

Why do you use local variables like _channelN in many functions?
they are never read back or did I miss that ?

Or is this for future functionality, like returning the last value set or so?

You might add some state information calls like bool AD5668::isHWSPI();
or returning pins used for SWSPI.

What certainly would be interesting is to think how to support multiple devices.
For SPI that means all lines are equal except the ssPin?

good job by the way

Hello Bob,

Thanks for making this library. I've ordered some samples from Analog.
I have the AD5668-1 and AD5668-3.

But for some reason I can't get any output from the DAC. I've tried all
three code samples. I've tested both the hardware and software SPI options.
Also tested with an external Vref and internal ref. With the use of
the internal Vref I don't see any voltage appear on the Vref pin ( 8 ).

Signals on the SLK and Din are nice 0 and 5V levels.
But for some reason it seems both DAC's doesn't initialize.
At the first test I used the -1 version, now the -3 is on my board.

Your code compiles fine without any errors. Is the code which is now
listed 100% working? Anyone else tested this from scratch?

Regards, Roland

@Bob
please add a link to this thread in your GH-repo so people can discuss the lib here.

To test my hardware I used this DAC8568 sample code yesterday.

With an external Vref this code works immediately on the AD5668.
So it isn't an hardware problem here.

Regards, Roland

Just wondering if anyone knew if this library works on the arduino due?

Hi,

I'm trying to understand what you have here and I have a question. What is the difference between WRITE_INPUT_REGISTER_UPDATE_N and UPDATE_OUTPUT_REGISTER as far as the hardware goes? The datasheet is a little ambiguous with these command values. Thank you for your help

Austin