Library for DAC855X Digital to Analogic converters by Texas Instruments

Hello! I recently purchased some Texas Instruments DAC8552 chips, but I do not get the proper libraries for Uno. I have achieved something similar here and here, but I do not know if it will be compatible. Can someone help me?
Thanks!

[EDIT 17.12.18] The project resulted in good results! The library is now available on GitHub!

Thanks for the contributions!
Following in this post, you can follow the development of the project.

After a lot of searching on Github and other pages, I was able to build something ... Look, I'm a beginner, and I need your suggestions to make this code work ...
First, the libraries:
DAC8552.h

/*************************************************************************
**  Device: DAC8552                                                 	**
**  File:   DAC8552.h - Biblioteca para obter valor de voltagem DAC    	**
*************************************************************************/


#ifndef DAC8552_h
#define DAC8552_h

#include "Arduino.h"

class DAC8552
{
  public:
    DAC8552(int DIN, int SCK, int SS, boolean DAC, boolean DAC1);    
    void setValue(int Value);

  private:
    int _DATAOUT;
    int _SPICLOCK;
    int _SLAVESELECT;
    boolean _DAC;
	boolean _DAC1;
                                              
    void sendSPIHeader();
    void mandaSPI(int Value);
    void sendSPIClock();
};

#endif

DAC8552.ccp

/*************************************************************************
**  Device: DAC8552                                                 	**
**  File:   DAC8552.h - Biblioteca para saída analógica              	**
**************************************************************************/
#include "DAC8552.h"
#include "Arduino.h"

DAC8552::DAC8552(int DIN, int SCK, int SS, boolean DAC, boolean DAC1)  //Seleção dos pinos e das saídas
{
 _DATAOUT = DIN;
 _SPICLOCK = SCK;
 _SLAVESELECT = SS;
 _DAC = DAC;
 _DAC1 = DAC1;
  
 pinMode(_DATAOUT,OUTPUT);
 pinMode(_SPICLOCK,OUTPUT);
 pinMode(_SLAVESELECT,OUTPUT);

}

//************************************************************************
void DAC8552::setValue(int Value) {
  sendIntValueSPI(Value);
}
//************************************************************************ 
void DAC8552::sendSPIClock() {
  digitalWrite(_SPICLOCK, HIGH);
  digitalWrite(_SPICLOCK, LOW);
  delay(1);
}

//************************************************************************ 
void DAC8552::sendSPIHeader() {
// bit 23 Adress reservado = 0
 digitalWrite(_DATAOUT,LOW);
 sendSPIClock();
// bit 22 Adress reserved = 0
  digitalWrite(_DATAOUT,LOW);
  sendSPIClock();
// bit 21 Output on Channel B
 digitalWrite(_DATAOUT,_DAC1);
 sendSPIClock();
// bit 20 Output on Channel A
 digitalWrite(_DATAOUT,_DAC);
 sendSPIClock();
// bit 19 Don't care bit
  digitalWrite(_DATAOUT,LOW);
  sendSPIClock();
// bit 18 Buffer selection
  digitalWrite(_DATAOUT,DAC);
  sendSPIClock();
// bit 17 and 16 Power down mode - sets as Normal
  digitalWrite(_DATAOUT,LOW);
  sendSPIClock();
  digitalWrite(_DATAOUT,LOW);
  sendSPIClock();

  }
 
//************************************************************************
void DAC8552::mandaSPI(int value) {
 // initiate data transfer with 8552
 digitalWrite(_SLAVESELECT,LOW);
 
 // send 8 bit header
 sendSPIHeader();
 
  // send data
  for(int i=16;i>=0;i--){
    digitalWrite(_DATAOUT,((Value&(1<<i)))>>i);
    sendSPIClock();
  }
 
  // finish data transfer
  digitalWrite(_SLAVESELECT,HIGH);
}

And the test:

#include <DAC8552.h>

DAC8552 SaidaA(11, 13, 10, HIGH, LOW); //Seta a saída para canal A

int LeituraA;

void setup() {
 Serial.begin(9600);
}

void loop() {
  for(int i=0; i<65535; i++)
  {
    SaidaA.mandaSPI(i);
    LeituraA = analogRead(0);
    Serial.println(LeituraA);
    delay(1000);
  }
}

The debug get a error in "SaidaA.mandaSPI(i);" "within this context"
Whats wrong with my code?

Can you post the exact error?
NOrmally it has a line number where it goes wrong.

mandaSPI() is a private function of the class, so it cannot be called directly.

try to change the testcode to

//
// File: DAC8552demo.ino
// Author: ...
// Date: ...
// Purpose: demo sketch DAC8552
// 
#include <DAC8552.h>

DAC8552 SaidaA(11, 13, 10, HIGH, LOW); //Seta a saída para canal A

int LeituraA;

void setup() 
{
  Serial.begin(9600);
  Serial.println(__FILE__); // shows the name of the sketch
}

void loop() 
{
  for (int i = 0; i < 65535; i++)
  {
    SaidaA.setValue(i);
    delay(100); // give it some time to settle

    // do the measurement
    LeituraA = analogRead(0);

    // print results
    Serial.print(i);
    Serial.print("\t ==> \t");
    Serial.println(LeituraA);
    delay(1000);
  }
}

Thank you, robtillaart! You're right! The only fuction I can use is "setValue (int)". I'll test here!

The error:

Arduino: 1.8.5 (Windows 10), Placa:"Arduino/Genuino Uno"

Opções de compilação alteradas, recompilando tudo
C:\Users\RENATO~1\AppData\Local\Temp\ccZO68FI.ltrans0.ltrans.o: In function `__static_initialization_and_destruction_0':

C:\Users\Renato Ianhez\Documents\Arduino\DAC_Teste\DAC8552-Teste2/DAC8552-Teste2.ino:3: undefined reference to `DAC8552::DAC8552(int, int, int, bool, bool)'

C:\Users\RENATO~1\AppData\Local\Temp\ccZO68FI.ltrans0.ltrans.o: In function `loop':

C:\Users\Renato Ianhez\Documents\Arduino\DAC_Teste\DAC8552-Teste2/DAC8552-Teste2.ino:17: undefined reference to `DAC8552::setValue(int)'

collect2.exe: error: ld returned 1 exit status

exit status 1
Erro compilando para a placa Arduino/Genuino Uno

There may be an error in the .cpp file, in the bit counting part. I did according to the DAC8552 datasheet (page 14), but since I have no experience with it, I may have been wrong there ...

Sorry to say but that library is incomplete at best for Arduino. I do not know the DAC in question and I do not see a quick fix for the code. It could be that the lib was written for another processor than Arduino. Just don't know.

The datasheet is available - http://www.ti.com/lit/ds/symlink/dac8552.pdf

A quick google did not show any library for this DAC...

Where did you find the library?

The codes were adapted from the DAC8554, which I found here. However, the two DACs are different, since 8552 has 2 channels and 8554 has 4. So the encoding of the initial bits is distinct, and I did not know what to do with what the datasheet calls "buffer" and the "update" of each channel (my English is via google translator).

Where are you from?

Have you tried to use the 8554 library code as it is?
If these 2 DACs only differ in the number of channels it might just work.
Please give it a try,

At least a quick view showed that the 8554 library has more code than the 8552 code above.
A part of the missing might explain the failing of compilation.

A quick check showed that the DAC8554 library does compile.
I cannot test if it works as I do not have such DAC.

There is only one warning, which is not critical (but should be solved sometime)

I'm from Brasil..
In fact, I compared the datasheets of both DACs and noticed that there are significant differences in the 8 control bits. In the 8552, the first 2 bits (23 and 22) are reserved, whereas in the 8554 they have address and control functions. The next two bits (21 and 20) are upgrading channels A and B to 8552 and have a similar function in 8554. Bit 19 is useless ("don't care bit") in both. Bit 18 indicates DAC (buffer select) addressing, whether for channel A or for B on 8552, while on 8554 bits 18 and 17 are used for this function (since they are 4 channels). In 8552, bits 17 and 16 control the power down for each channel, while in 8554 the same function is done by bit 16, but for all channels together.

My main question is about the meaning of bits 21 and 20 ("control the updating of each analog output with the specified 16 bit data") and bit 18 ("controls the destination of the data"). They seem redundant to me. The goal is, in the Arduino sketch, to write only "sendtoDACout(channelX, ValueDAC)". Where X is channel A or B, and ValueDAC is a value between 0 and 65535.

In conclusion, I came across the DAC8552 because it was the only device of this type (16 bit DAC) that I found in the market of my country. I'm assembling a low-cost potentiostat to be used in my instrumental chemical analysis classes. I've made good progress with the popular MCP4725, 12-bit I2C. But as I found this better one...
In Brasil, cheap devices of the same type cost about 1 year of work by an ordinary worker (R$ 12,000.00 / U$ 4,000.00). My project wants to make one for 100 times less (R$ 120,00).

I know Brasil is expensive (import taxes) so I understand your need.

What I would do is make a copy of that class as a starting point.

With respect to the differences, to me they seem not blocking for the experiment

bits 23 and 22 - not used so don't care
bits 21 and 20 - same function so OK
bit 18 - address is same so OK

In 8552, bits 17 and 16 control the power down for each channel,
while in 8554 the same function is done by bit 16, but for all channels together.

Power down is important but not critical, so just not use it for a first experiment.
(unless you need them to power up!!)
Rewrite should not be too difficult.

In short the 8554 library should have a big chance of working. Give it a try.
If it not works I will try to help rewrite the lib to match the specs.

Do you have links to the datasheets?

Had a quick look at the lib and stripped it to something minimal to have a start.
See attachment.

you might need to tweak the SPIHeadder function to set some bits right.
please let me know is if works or not then I can do some rework this evening.

DAC8552.zip (2.23 KB)

Thank you, Rob! (can I call it that?) Your tips are being very useful and I'm learning a lot about "modus operandi"!
Let's take the steps used in libraries:

DAC8552.h
(The comments of headings will do later)

  1. Initial statements. They seem OK.
#ifndef DAC8552_h
#define DAC8552_h

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
  1. Class declaration and public functions. Here I would like to be able to pass the channel selection to the setValue function. But for now, let's find out which bit is responsible for this.
class DAC8552
{
  public:
    DAC8552(int DIN, int SCK, int SS, bool DAC, bool DAC1);    
    void setValue(int value);
  1. Here we declare the internal variables and functions. Seems OK... Notice that there was a separation of the control bits (SPIHeader) from the data bits. I have doubts about the type of data sent to SPI. Is it really an integer?
private:
    int _DATAOUT;
    int _SPICLOCK;
    int _SLAVESELECT;
    boolean _DAC;
    boolean _DAC1;
                                              
    void sendSPIHeader();
    void sendtoSPI(int value);
    void sendSPIClock();
};

Let's move on to DAC8552.cpp.
4. Header, with all the typical information, I'll do later. Initial part looks ok too.

#include "DAC8552.h"

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
  1. Here I changed the name of the pins according to their names on the chip: DIN = MISO (Uno pin 12), SCLK = SCK (pin 13) and SYNC = SS (pin 10). I include one more Boolean variable (DAC2) for bit 18. In the sketch, we will change the values in DAC, DAC1 and DAC2 to see how the output we will take. (I changed this in the .h file)
DAC8552::DAC8552(int DIN, int SCK, int SS, bool DAC, bool DAC1, bool DAC2) 
{
 _DATAOUT = DIN;
 _SPICLOCK = SCK;
 _SLAVESELECT = SS;
 _DAC = DAC;
 _DAC1 = DAC1;
 _DAC2 = DAC2;
  
 pinMode(_DATAOUT,OUTPUT);
 pinMode(_SPICLOCK,OUTPUT);
 pinMode(_SLAVESELECT,OUTPUT);

}
  1. That part was where I adapted the code for the 8552. The first two functions (setValue and sendSPIClock) are OK like this?
    Now the sendSPIHeader(): The first two bits (23 and 22) are LOW (reserved). Bits 21 and 20 we can change in command. Bit 19 don't care. Bit 18 we can change the value in the command as well. Bits 17 and 16 will leave as LOW, without "power down" (we can activate this feature later).
void DAC8552::setValue(int value) {
  sendIntValueSPI(value);
}

void DAC8552::sendSPIClock() {
  digitalWrite(_SPICLOCK, HIGH);
  digitalWrite(_SPICLOCK, LOW);
  delay(1);
}

void DAC8552::sendSPIHeader() {
// bit 23 (reserved) = 0
 digitalWrite(_DATAOUT,LOW);
 sendSPIClock();
// bit 22 (reserved) = 0
  digitalWrite(_DATAOUT,LOW);
  sendSPIClock();
// bit 21 Output on Channel B
 digitalWrite(_DATAOUT,_DAC1);
 sendSPIClock();
// bit 20 Output on Channel A
 digitalWrite(_DATAOUT,_DAC);
 sendSPIClock();
// bit 19 Don't care bit
  digitalWrite(_DATAOUT,LOW);
  sendSPIClock();
// bit 18 Buffer selection
  digitalWrite(_DATAOUT,DAC2);
  sendSPIClock();
// bit 17 and 16 Power down mode - sets as Normal
  digitalWrite(_DATAOUT,LOW);
  sendSPIClock();
  digitalWrite(_DATAOUT,LOW);
  sendSPIClock();
  1. Here, I do not know if it's correct. Notice that in the loop, i starts with 16 and goes to zero. Would not it be starting with 15?
void DAC8552::sendtoSPI(int value) {
 // initiate data transfer with 8552
 digitalWrite(_SLAVESELECT,LOW);
 // send 8 bit header
 sendSPIHeader();
  // send data
  for(int i=16;i>=0;i--){
    digitalWrite(_DATAOUT,((value&(1<<i)))>>i);
    sendSPIClock();
  }
  // finish data transfer
  digitalWrite(_SLAVESELECT,HIGH);
}

Finally (wow!), The test program:
8. I set the Arduino pins, and the Boolean bits 21, 20 and 18. The presence of spaces in the part "DAC8552 OutputA (11, 13, 10, HIGH, LOW, LOW);" is it critical?

#include <DAC8552.h>

DAC8552 SaidaA(11, 13, 10, HIGH, LOW, LOW);

int LeituraA;

void setup()
{
  Serial.begin(9600);
  Serial.println(DAC8552Test);
}
  1. This loop, I suppose, is OK. I put increments of 64 to give 1024 values.
void loop()
{
  for (int i = 0; i < 65535; i+=64)
  {
    SaidaA.setValue(i);
    delay(100); // give it some time to settle

    // do the measurement
    LeituraA = analogRead(0);

    // print results
    Serial.print(i);
    Serial.print("\t ==> \t");
    Serial.println(LeituraA);
    delay(1000);
  }
}

Verifying, result the error:

Arduino: 1.8.5 (Windows 10), Placa:"Arduino/Genuino Uno"

C:\Users\RENATO~1\AppData\Local\Temp\ccdpP4zQ.ltrans0.ltrans.o: In function `__static_initialization_and_destruction_0':

C:\Users\Renato Ianhez\Documents\Arduino\DAC_Teste\DAC8552Test3/DAC8552Test3.ino:3: undefined reference to `DAC8552::DAC8552(int, int, int, bool, bool, bool)'

C:\Users\RENATO~1\AppData\Local\Temp\ccdpP4zQ.ltrans0.ltrans.o: In function `loop':

C:\Users\Renato Ianhez\Documents\Arduino\DAC_Teste\DAC8552Test3/DAC8552Test3.ino:16: undefined reference to `DAC8552::setValue(int)'

collect2.exe: error: ld returned 1 exit status

exit status 1
Erro compilando para a placa Arduino/Genuino Uno

Este relatório teria mais informações com
"Mostrar a saida detalhada durante a compilação"
opção pode ser ativada em "Arquivo -> Preferências"

Would the error be in the "constructor" of the functions?

You must take care that the constructor in the .h file matches the constructor in the .cpp file.

Check DAC8552.h
DAC8552(int DIN, int SCK, int SS, bool DAC, bool DAC1 ?MISSING? );


These lines can be removed from the DA8552.cpp file
as these are already in DAC8552.h

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

I came back to the beginning and discovered that I can program the DAC8552 via SPI in an easier way: via library SPI.h. So I adapted this program that worked perfectly!

/*
 Circuit:
 DAC8552 pins:
                SPI    Uno pins   MEGA pins
 SYNC:      SS     10             38
 DIN:	        MOSI 11             51
 SCLK:      SCK   13             52
  */

// DAC communicate by SPI
#include <SPI.h>

// pins declaration
const int P_SYNC = 10;
const int P_DIN = 11;
const int P_CLK = 13;
int Value = 535;
int CHA = 0x10;//Channel A
int CHB = 0x20;//Channel B
int CAB = 0x30;//Channel AB


void setup() {
  Serial.begin(9600);

  // initialize pins:
  pinMode(P_SYNC, OUTPUT);
  pinMode(P_DIN, OUTPUT);
  pinMode(P_CLK, OUTPUT);

  digitalWrite(P_SYNC, HIGH); 
  delay(1);

  SPI.begin();// initialize SPI
  delay(1);
  SPI.beginTransaction(SPISettings(16000000, MSBFIRST, SPI_MODE1));
  delay(1000);
  ProgChannel();
}

void ProgChannel() {
  digitalWrite(P_SYNC, LOW); 
  byte hiByte = highByte(Value);
  byte loByte = lowByte(Value);

  SPI.transfer(CHA); //configuration byte
  
  //data bytes
  SPI.transfer(hiByte); 
  SPI.transfer(loByte);

  digitalWrite(P_SYNC, HIGH); 
}

void loop() {
  ProgChannel();  
  delay(500);
  int Result = analogRead(0);
  Serial.print(Result);
}

Now, the next step is to convert this into a library to do the command "doOutDAC(Channel, DACValue);"

That is very useful as it shows how it can work.

The public interface of the library could look like this

class DAC855X
{
public:
  DAC855X(uint8_t SDI, uint8_t SCK, uint8_t CS, int type); // type = 8554 or 8552

  void setValue(uint8_t DAC, uint16_t value);
  void setVoltage(uint8_t DAC, float voltage);

  uint16_t  getValue(uint8_t DAC);
  float getVoltage(uint8_t DAC);

  void enable(uint8_t DAC);
  void disable(uint8_t DAC);
  bool isEnabled(uint8_t DAC);

A constructor to define the pins used, and the type (a 8552 only lib would not need the type)

Two functions to set the output values, as a voltage or as integer.
The voltage is a wrapper that brings the interface on a "physics" level.
The getters are to read back the value

Finally three functions to enable/diable a certain channel.

The above interface hides the differences between the 8552 and the 8554. I have some time coming weekend to help to create such library.


note updated interface description to merge 8552 & 8554 lib into one which seems logical.

Great! A library that covers the two DACs in one! It is still possible to serve the DAC8551, which has only one channel.
In this case, the .ccp file can contain an array with all the possibilities for the control byte. I did this for 8552:
Ch A ==> 0x10
Ch B ==> 0x20
Ch AB ==> 0x30
Ch A + bit18 ==> 0x14 (no power down, I still do not know what this [buffer] does)
Ch B + bit18 ==> 0x24
Ch AB + bit18 ==> 0x34 (I have not yet tested this possibility, nor do the following)
Ch A + PD1 ==> 0x11 (power down 1kOhm w/buffer [?] Ch A)
Ch B + PD1 ==> 0x21 (power down 1kOhm w/buffer [?] Ch A)
Ch A + PD1 ==> 0x15 (power down 1kOhm w/buffer [?] Ch B)
Ch B + PD1 ==> 0x25 (power down 1kOhm w/buffer [?] Ch B)
Ch A + PD2 ==> 0x12 (power down 100kOhm w/buffer [?] Ch A)
Ch B + PD2 ==> 0x22 (power down 100kOhm w/buffer [?] Ch A)
Ch A + PD2 ==> 0x16 (power down 100kOhm w/buffer [?] Ch B)
Ch B + PD2 ==> 0x26 (power down 100kOhm w/buffer [?] Ch B)
Ch A + PD3 ==> 0x13 (power down hi-impedance w/buffer [?] Ch A)
Ch B + PD3 ==> 0x23 (power down hi-impedance w/buffer [?] Ch A)
Ch A + PD3 ==> 0x17 (power down hi-impedance w/buffer [?] Ch B)
Ch B + PD3 ==> 0x27 (power down hi-impedance w/buffer [?] Ch B)

For 8554, I belive, will much different...

trick is to make a base class, so they have essentially the same interface, and derive specific classes from the base. But lets start with one 8552...

datasheets - family of four ....