SPI protocol with external DAC

Good morning!
I have a problem with the interfacing between Arduino DUE board and an external high resolution DAC, the AD5570http://www.analog.com/media/en/technical-documentation/data-sheets/AD5570.pdf
I would like to obtain two different output:

1- Constant voltage output: the user will select the desired output voltage (1V, 100mV, -100mV…) and the board will sent to the DAC the proper binary code in order to obtain thet voltage;

2-Voltage ramp out;

I’ve written something regarding the first problem but i can’t obtain a good result (I have no idea how to solve the second problem). The code is the following:

#include <SPI.h>

int DACR;
long Din;                          //Input signal 
const int V_ref = 5;              //reference voltage
long tot_level = 65536;           //2^16 of the output (16 bit DAC)

void setup() {
 Serial.begin(115200);
 SPI.begin(pinSYNC_DAC);
 SPI.setBitOrder(MSBFIRST);
 SPI.setClockDivider(21);       
 SPI.setDataMode(pinSYNC_DAC,SPI_MODE1);
}

void loop() {
  if (Serial.available()!=0){
    DACR = Serial.parseInt() ;
    Serial.print("Voltage selected: ");
    Serial.println(DACR,DEC);
    Din = ((DACR + 2* V_ref)*tot_level)/(4*V_ref);         
    Serial.println(Din);
    delay(1000);
    if(Din>=tot_level){
        Din=tot_level;}
    byte msg1 = (Din >> 8);
    byte msg2 = (Din & 0xFF);
    Serial.println(msg1,BIN);
    Serial.println(msg2,BIN);
    }
}

And this is what i obtain on the serial monitor when i run the program:

Voltage selected: 1
36044
10001100
11001100
Voltage selected: 2
39321
10011001
10011001
Voltage selected: 3
42598
10100110
1100110
Voltage selected: 5
49152
11000000
0
Voltage selected: -1
29491
1110011
110011
Voltage selected: -2
26214
1100110
1100110

I don’t know what is wrong but i don’t know how to solve the problem. Unfortunately i’m stuck on these two problem and i can’t go ahead. How can i send the properly binary code to the DAC through Arduino DUE?
Thank you for your attention!

Arduino: 1.6.2 (Windows 8.1), TD: 1.22-beta1, Board: "Arduino Uno"

sketch_sep03a.ino: In function 'void setup()':

sketch_sep03a.ino:11:12: error: 'pinSYNC_DAC' was not declared in this scope

Error compiling.

Please give us your real sketch that you are actually running.

What did you try to enter when you got those outputs? What did you expect to see in the output?

Sorry for that error. The code is that one, but at this moment i’ve just neglected the SPI command because i would like to understand how to send the properly code to the DAC.
The input voltage is insterted from the keyboard and represents the desired output voltage; for example if i press “1” i would like to obtain 1 volt as output.
I don’t understand how to obtain the properly binary code that gives me 1V output (i would like also to obtain negative outputs or in mV).

(first problem. second problem i haven’t understood how to solve it.)

#include <SPI.h>

int DACR;
long Din;                          //Input signal
const int V_ref = 5;              //reference voltage
long tot_level = 65536;           //2^16 of the output (16 bit DAC)

void setup() {
 Serial.begin(115200);
 //SPI.begin(pinSYNC_DAC);
 //SPI.setBitOrder(MSBFIRST);
 //SPI.setClockDivider(21);       
 //SPI.setDataMode(pinSYNC_DAC,SPI_MODE1);
}

void loop() {
  if (Serial.available()!=0){
    DACR = Serial.parseInt() ;
    Serial.print("Voltage selected: ");
    Serial.println(DACR,DEC);
    Din = ((DACR + 2* V_ref)*tot_level)/(4*V_ref);         
    Serial.println(Din);
    delay(1000);
    if(Din>=tot_level){
        Din=tot_level;}
    byte msg1 = (Din >> 8);
    byte msg2 = (Din & 0xFF);
    Serial.println(msg1,BIN);
    Serial.println(msg2,BIN);
    }
}

OK, datasheets are tough. They include a lot of strange information like a picture of the box that the components come in. This particular one, I have to scan down to page 16 before I get any useful information from it.

On power-up, the input shift register and DAC register are
loaded with midscale (0x8000).

OK, that's useful. Zero volts output comes from (decimal) 32768 input.

The way that works, it's using the 16-bit input as an unsigned integer. 32768 is outside the range of a signed int but it's perfectly OK for unsigned int. Change your main DACR variable to unsigned.

But the calculations on those values will use intermediate values up to 20 times bigger (4 times 5 is used as a divider at the end of the formula.) 20 times a number that just barely fits into an unsigned int won't fit into an unsigned int. So the intermediate calculation should be upgraded to long except, you probably want to have voltages in between integers, like maybe 1.5v? So that indicates we need to have some way of representing decimals.

You've got two choices: use floating-point numbers or fixed-point representation. You can use Serial.parseFloat() to accept user input or you may decide that you want to enter everything in millivolts. eg. "-123" will represent -0.123V and "1234" will represent 1.234V. I suggest you start with floating-point and use parseFloat().

Try to use the formula in the datasheet to hand-calculate a few examples, like 1.234V and then see if your Arduino can output the same number. Try the end points and zero, like the ones shown in the example table just above the formula on page 17.

Ok i’m trying to follow your instructions but i can’t undestand them properly, sorry but i’m new to this world!
According to your corrections the code should be like this one:

#include <SPI.h>

unsigned int DACR;
long Din;                          //Input signal 
const int V_ref = 5;              //reference voltage
long tot_level = 65536;           //2^16 of the output (16 bit DAC)

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

void loop() {
  if (Serial.available()!=0){
    DACR = Serial.parseFloat() ;
    Serial.print("Voltage selected: ");
    Serial.println(DACR,DEC);
    Din = ((DACR + 2* V_ref)*tot_level)/(4*V_ref);         
    Serial.println(Din);
    delay(1000);
    if(Din>=tot_level){
        Din=tot_level;}
    byte msg1 = (Din >> 8);
    byte msg2 = (Din & 0xFF);
    Serial.println(msg1,BIN);
    Serial.println(msg2,BIN);    
    }
}

The problem is that even using the Serial.parseFloat(), when i tape for example “1.234”, the serial monitor doesn’t show me the decimal value but only “1”.
i think that the preavious problem is still present and this is what the serial monitor returns to me:

Voltage selected: 1
36044
10001100
11001100
Voltage selected: 0
32768
10000000
0
Voltage selected: 2
39321
10011001
10011001
Voltage selected: 3
42598
10100110
1100110

Thank you so much for your help

long Din;                          //Input signal

No, this should be unsigned int because this is the 16-bit value to be sent to the chip.

When you parse a float, store it in a float. If you try to store it in an integer, then it will strip off the ".234" and just store the "1". (And there's no such thing as an unsigned float, which means that it's perfect for storing physical values like 1.234 or -4.567.)

When you re-post your examples, it will help us if you add a comment next to the incorrect values like "I typed 1.234". Where your code shows "Voltage selected 1" then we don't know if that's what you typed or if that was what you were expecting to see.

So the code should be like this one:

#include <SPI.h>

float DACR;
unsigned int Din;                 //Input signal 
const int V_ref = 5;              //reference voltage
long tot_level = 65536;           //2^16 of the output (16 bit DAC)

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

void loop() {
  if (Serial.available()!=0){
    DACR = Serial.parseFloat() ;
    Serial.print("Voltage selected: ");
    Serial.println(DACR,DEC);
    Din = ((DACR + 2* V_ref)*tot_level)/(4*V_ref);         
    Serial.println(Din);
    delay(1000);
    if(Din>=tot_level){
        Din=tot_level;}
    byte msg1 = (Din >> 8);
    byte msg2 = (Din & 0xFF);
    Serial.println(msg1,BIN);
    Serial.println(msg2,BIN);   
    }
}

This is what the serial monitor shows me after uploading the code on the board:

Voltage selected: 1.2339999676     //typed 1.234
36811
10001111
11001011
Voltage selected: -1.2339999676   //typed -1.234
28724
1110000
110100
Voltage selected: 3.0000000000
42598
10100110
1100110
Voltage selected: 5.0000000000
49152
11000000
0
Voltage selected: -10.0000000000
0
0
0
Voltage selected: 9.9899997711
65503
11111111
11011111
Voltage selected: 7.0000000000
55705
11011001
10011001
Voltage selected: 4.0000000000
45875
10110011
110011

I’ve tried the minimum and maximum voltage, but something is still wrong because sometimes the result is different from 16-bit as you can see, for example when i type a negative number or (i don’t know why) the number “3”, or “4”. I’m just a bit confused about these problem, considering that the second problem is still a “mistery” for me because unfortunately i’m new to this world.Have i missed something else according to your corrections?
Really thank you!

The BIN output option doesn't know how many digits you want to print. The same as normal decimal output doesn't print 0001.234, the BIN numbers don't print the leading zeros. Add the missing leading zeros to pad the numbers out to 8 bits and that will be more like what the Arduino is actually sending.

The values printed for -10 and +9.99 look correct to me. I haven't examined the others closely. Try entering zero and see if you get the value suggested in the datasheet on page 16. 0x8000, which is 10000000 00000000 (which will print as 10000000 0)

Ok, i tried to type also the “0” which is at the midscale a this is the result:

Voltage selected: 0.0000000000
32768
10000000
0

If the other zeroes aren’t displayed i think that the result is correct. I think that there is something wrong with negative number. For example if i type “-1”, i would expect a binary code like : “111001100110011” which is a 15-bit instead of 16-bit.
This is what comes from serial monitor:

Voltage selected: -1.0000000000
29491
1110011
110011

In your opinion is there something wrong? What should i have to modify in order to solve the problem?

Thank you!

No, that looks like the correct result. There's two leading zeroes dropped from the second byte.

That sounds great! Really thank you!

Regarding the second problem do you have any suggestion?
I would like to obtain a voltage ramp output or (let's say) a triangular wave, and the user will choose the slope and the amplitude of the output.
Is possible to achieve this result? How can i do that?

Thank you again for the attention

jack_plant:
That sounds great! Really thank you!

Regarding the second problem do you have any suggestion?
I would like to obtain a voltage ramp output or (let’s say) a triangular wave, and the user will choose the slope and the amplitude of the output.
Is possible to achieve this result? How can i do that?

Thank you again for the attention

You would have to repeatedly send values to the DAC, each value calculated to be a specific voltage at a specific time.

If you wanted a signal to be a triangular wave with a max voltage of X, and a period of 1 second.

uint16_t calcTriVoltage(uint16_t msTime, uint16_t msPeriod, uint16_t maxVoltage){ // time in mi
// voltage is positive DAC value 0..32767

msTime = msTime % msPeriod; // cycle repeats 

uint16_t quad= msTime/(msPeriod/4); // which quadrant signal is in 0,1,2,3
uint16_t x = msTime%(msPeriod/4); // position in quadrant;
int32_t y = maxVoltage*x/(msPeriod/4);
switch(quad){
  case 0:  ;
    break;
  case 1:  y = maxVoltage - y;
    break;
  case 2:  y = -y;
    break;
  case 3: y = -maxVoltage + y;
    break;
  default : y = 0; // should never get here
  } 
  
y = y + 0x8000; // DAC 0v = 32768
return (uint16_t)y;
}


void tri(uint16_t maxVoltage, uint16_t period,uint16_t cycles){

uint16_t volt=0;
uint16_t time=0;
uint32_t st=millis();
while(cycles>0){
   volt=calcTriVoltage(time,period,maxVoltage);
   writeDAC(volt);
   st += 1;
   while(millis-st<1); // wait for 1 ms to elapse
   time += 1;
   if(time>=period){
    time=0;
    cycles--;
    }
  }
}

void loop(){
  tri(0x4000,1000,5); // create a 1/2 scale triangular wave with a 1 second period, 5 cycles
}

you will have to provide to code to actually talk to the DAC

chuck.

Thank you for the answer and for the code, but if possibile can you explain it briefly? I would like to understand what does the code do. Does the user can choose the voltage and the periode of the triangular wave?

I suggest you check what 65536 appears as, in a long

if (Serial.available()!=0){
DACR = Serial.parseInt() ;

this is no good either

Sorry but what di you mean with these two post? Why se Serial.parseFloat() isn't good? What is the problem with the long type of the variabile tot_level? I'm really sorry but it's my first project and i'm learning how to implement a correct code

Thank you for your attention!

jack_plant:
Thank you for the answer and for the code, but if possibile can you explain it briefly? I would like to understand what does the code do. Does the user can choose the voltage and the periode of the triangular wave?

This code just creates a series of values, from 32768 up to 49152, down to 32768 down to 16384, up to 32768, which produces one cycle of a triangular wave and repeats this sequence five time. The code sends these values to the DAC spaced one decimal value apart, and one millisecond apart in time.

You will have to build the user interface to create the values for maxVoltage, period, cycles.

Chuck.

So i need (for example) to implement the acquisition from keyboard of these 3 values?
Should i use the Serial.parseFloat() also in this situation in order to perform a complete acquisition?

jack_plant:
So i need (for example) to implement the acquisition from keyboard of these 3 values?
Should i use the Serial.parseFloat() also in this situation in order to perform a complete acquisition?

You will have to do a few things:
0. build a user interface (prompts, input validity testing)

  1. accept input from the user interface
  2. convert the human readable input values to values the program can use.
  3. pass these converted values to the ‘function’ generator procedures (a.k.a. my tri())
  4. create a function that actually communicates with your DAC (that accepts a uint16_t as input)

Chuck.

p.s. I like helping people to learn, I don’t like doing your homework.