SPI Transfer issue with AD5535

Hey there, I am trying to send 19-bit word data from Arduino Uno to the AD5535 DAC. Sorry for the repeated-ish post since it is essentially a follow-up question from this topic: transfer 19 bit word, SPI, AD5535 - #33 by dougp.

However, I am trying to understand what is wrong with the SPI transferring as I am not receiving any voltage value even with hard-coded word data.

#include <SPI.h>

const int columnDACSelectPin = 6;
const int rowDACSelectPin = 7;
const int resetPin = 0;

boolean dataReady = false;
boolean recvInProgress = false;


void channel_write() {
  /*
   * A - Address Bit
   * d - Data Bit
   * x - Don't Care Bit
   * 
  byte 3 | xxxx xxxx
  byte 2 | AAAA Addd
  byte 1 | dddd dddd
  byte 0 | dddx xxxx
  */

  uint8_t bytesToSend[3];
  uint8_t addressValue = 0;
  uint16_t dataValue = 0;
  uint16_t dataValueBuffer = 0;
  bool colWrite = false;
  
  bytesToSend[0] = B00001111;   
  bytesToSend[1] = B10001011;      
  bytesToSend[2] = B00100000;   

       
  
  addressValue = (bytesToSend[0] >> 3) & 0x1F;          // Mask (0001 1111)
  dataValue = (bytesToSend[0]) & 0x7;                   // Mask (0000 0111)
  dataValue = (dataValue << 11) & 0x3800;               // Mask (0011 1000 0000 0000)
  
  dataValueBuffer = (bytesToSend[1]) & 0xFF;            // Mask (1111 1111)
  dataValueBuffer = (dataValueBuffer << 3) & 0x7F8;     // Mask (0000 0111 1111 1000)
  dataValue |= dataValueBuffer;    
  dataValueBuffer = 0;
  
  dataValueBuffer = (bytesToSend[2] >> 5) & 0x7;        // Mask (0000 0111)
  dataValue |= dataValueBuffer & 0x7;  


    
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
  
    Serial.print("Row Address (DEC): "); Serial.print(addressValue, DEC); Serial.print(" -> Converted Voltage (DEC): "); Serial.print(dataValue, BIN);
    Serial.print("\t(from Decimal: "); Serial.print(dataValue, DEC); Serial.println(")"); 
    for (int i = 0; i < 3; i++)
    {
        if(i==0)
        {
          Serial.print("Row Btyes to Send: "); Serial.print("("); Serial.print(i); Serial.print(") "); Serial.print(bytesToSend[i], BIN); Serial.print("|"); 
        }
        else
        {
          Serial.print("("); Serial.print(i); Serial.print(") "); Serial.print(bytesToSend[i], BIN); Serial.print("|");         // Leading Zeros are not printed
        }                               //Delay 0.1s, Minimal of 0.2ms Delay between Sucessive Write
    }
    
    digitalWrite(rowDACSelectPin, LOW);           // Selects Row Chip
    delay(100);    
    SPI.transfer(bytesToSend[0]);
    SPI.transfer(bytesToSend[1]);
    SPI.transfer(bytesToSend[2]);
    delay(100);    
    digitalWrite(rowDACSelectPin, HIGH);             // De-Selects Row Chip
  
  SPI.endTransaction();
  Serial.println();
  dataReady = false;
  recvInProgress = false;
}


void setup() {
  Serial.begin(115200);
  pinMode(rowDACSelectPin, OUTPUT);
  SPI.begin();
}


void loop() {
  long currentColChannelValue = 20;
  int channelLoop = 0;
  uint16_t v_out_col_bin = currentColChannelValue*pow(2,14)/(50*1);  
  const char startMarker = '<';      
  
  while(Serial.available() > 0 && (dataReady == false))
  {
    char rc = Serial.read();
    if (rc == startMarker) {
      recvInProgress = true;
    } else if (recvInProgress) {
      dataReady = true;
    }  
  }
  if (recvInProgress)
  {
    channel_write(); 
  }
}

The hardware side is fine (as checked by my colleague) but I just can't figure what's wrong with the SPI transfer

Your example is too complicated and you are not even sure about the SPI communication. Make the simplest example possible just to test sending commands. I don't know if I'm right, but something caught my eye.

/ *
* A - Address Bit
* d - Data Bit
* x - Don't Care Bit
*
byte 3 | xxxx xxxx
byte 2 | AAAA Addd
byte 1 | dddd dddd
byte 0 | dddx xxxx
* /

Why do you set 4 bytes and byte 3 is completely redundant !? You do some 'mambo-jumbo' (maybe is raight, i dont know) with the bits and send 3 bytes.

SPI.transfer (bytesToSend [0]);
SPI.transfer (bytesToSend [1]);
SPI.transfer (bytesToSend [2]);

It seems to me that you are sending them in reverse order. I looked at the DAC datasheet and I think you should send bytes 2, 1 and 0. Your program is very large, scattered and has a strange logic for me.

Hey there, oops ye you are right, the Btye[3] annotation is redundant, but essentially the whole block on dataValue bit shifting can be ignored because I'm splitting the hard-coded 19-bit word data to the 5 Channel bit and 14 Voltage Bit, i.e., this bit:

  addressValue = (bytesToSend[0] >> 3) & 0x1F;          // Mask (0001 1111)
  dataValue = (bytesToSend[0]) & 0x7;                   // Mask (0000 0111)
  dataValue = (dataValue << 11) & 0x3800;               // Mask (0011 1000 0000 0000)
  dataValueBuffer = (bytesToSend[1]) & 0xFF;            // Mask (1111 1111)
  dataValueBuffer = (dataValueBuffer << 3) & 0x7F8;     // Mask (0000 0111 1111 1000)
  dataValue |= dataValueBuffer;    
  dataValueBuffer = 0;
  dataValueBuffer = (bytesToSend[2] >> 5) & 0x7;        // Mask (0000 0111)
  dataValue |= dataValueBuffer & 0x7;

What I hard-coded was basically:

  • A - Address Bit
  • d - Data Bit
  • x - Don't Care Bit

bytesToSend[0] | AAAA Addd
bytesToSend[1] | dddd dddd
bytesToSend[2] | dddx xxxx

Sorry for the confusion, it was annotated incorrectly. But in this order, is it correct to set SPI to MSBFIRST and send from array 0 to 2 to get AAAAAdddddddddddddd for the DAC register?

Here's the cleaned-up code (which does not work still)

#include <SPI.h>

const int rowDACSelectPin = 7;        // CS connected to D7
boolean sendSPIOnce = true;

void setup() {
  Serial.begin(115200);
  pinMode(rowDACSelectPin, OUTPUT);
  SPI.begin();
}

void loop() {
  
  if(sendSPIOnce)
  {
      /*
       * A - Address Bit
       * d - Data Bit
       * x - Don't Care Bit
       
       bytesToSend 0 | AAAA Addd
       bytesToSend 1 | dddd dddd
       bytesToSend 2 | dddx xxxx
      */
    
      uint8_t bytesToSend[3];
      uint8_t addressValue = 0;
      uint16_t dataValue = 0;
      uint16_t dataValueBuffer = 0;
      
      bytesToSend[0] = B00001111;                           // Channel Bits are the 5 MSB of the byte; 00001 | First 3 MSB of Data Bits are the 3 LSB of the btye
      bytesToSend[1] = B10001011;                           // Middle 8 MSB of Data Bits
      bytesToSend[2] = B00100000;                           // Last 3 LSB of Data Bits are the 3 MSB of the btye; 11110001011001 (15449 DEC -> ~47v output)

      addressValue = (bytesToSend[0] >> 3) & 0x1F;          // Mask (0001 1111)
      dataValue = (bytesToSend[0]) & 0x7;                   // Mask (0000 0111)
      dataValue = (dataValue << 11) & 0x3800;               // Mask (0011 1000 0000 0000)
      
      dataValueBuffer = (bytesToSend[1]) & 0xFF;            // Mask (1111 1111)
      dataValueBuffer = (dataValueBuffer << 3) & 0x7F8;     // Mask (0000 0111 1111 1000)
      dataValue |= dataValueBuffer;    
      dataValueBuffer = 0;
      
      dataValueBuffer = (bytesToSend[2] >> 5) & 0x7;        // Mask (0000 0111)
      dataValue |= dataValueBuffer & 0x7;  
              
      Serial.print("Row Address (DEC): "); Serial.print(addressValue, DEC); Serial.print(" -> Converted Voltage (DEC): "); Serial.print(dataValue, BIN);
      Serial.print("\t(from Decimal: "); Serial.print(dataValue, DEC); Serial.println(")"); 
      for (int i = 0; i < 3; i++)
      {
          if(i==0)
          {
            Serial.print("Row Btyes to Send: "); Serial.print("("); Serial.print(i); Serial.print(") "); Serial.print(bytesToSend[i], BIN); Serial.print("|"); 
          }
          else
          {
            Serial.print("("); Serial.print(i); Serial.print(") "); Serial.print(bytesToSend[i], BIN); Serial.print("|");         // Leading Zeros are not printed
          }                               //Delay 0.1s, Minimal of 0.2ms Delay between Sucessive Write
      }
      
      digitalWrite(rowDACSelectPin, LOW);              // Selects Row Chip
      delay(100);    
      SPI.transfer(bytesToSend[0]);
      SPI.transfer(bytesToSend[1]);
      SPI.transfer(bytesToSend[2]);
      delay(100);    
      digitalWrite(rowDACSelectPin, HIGH);             // De-Selects Row Chip
      
      SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));
      SPI.endTransaction();
      Serial.println();
      sendSPIOnce = false;
  }
}

Further, I have tested it with the Analogue Discover 2 logic tester, which shows something like this.

This pretty much confirms the SPI issue but may I check what could cause such mis-transfer

Are you sure about the test signals? It seems to me that the DATA and CLOCK signals have been exchanged. At the top it says 4000 samples at 400 Hz but your SPI setting is 1MHz. I have never used SPI commands directly and have no experience with them. Why not try to transmit data bit by bit directly? So the transmission speed will be slower but at least you will be sure what you are doing. I would write a very simple program on this principle by periodically sending two different values ​​to one of the DAC channels for example every second. If it works you will know that the problem is in the SPI usage.

Hey there, thanks for the troubleshooting methodology. Yes, the problem was mainly due to the need to set the SCLK, too, so that the data can be read on the Falling Edge of the clock.

So the Suspected Main Issue is the DAC not receiving the word data properly in its register.

The current SPI setting is set to SCLK : 1MHz, Bit Order : MSBFIRST, dataMode : Mode 1 (Polarity 0, Phase 1).

The delay between each channel writing (i.e., each 19-bit word data write) is 0.5ms, the min. is 12Mhz rate (~0.83us), so that should be ok.

The data was cross-checked with an Oscilloscope which provided the correct result.

Additionally, the Serial Interface Timing indicates that it reads from MSB -> LSB, so sending Channel byte (MSB to LSB) before sending Data bytes (MSB to LSB) should be correct?

Could there be any other issue or steps I should take into account for the SPI transaction or is it likely the DAC's hardware issue?

void setup() {
  Serial.begin(115200);
  pinMode(rowDACSelectPin, OUTPUT);
  pinMode(colDACSelectPin, OUTPUT);
  pinMode(SCK, OUTPUT);
  pinMode(MOSI, OUTPUT); 
  digitalWrite(colDACSelectPin,  HIGH);
  digitalWrite(rowDACSelectPin,  HIGH);
  SPI.begin();
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE1));  
}

      digitalWrite(colDACSelectPin, HIGH);               // Ensure Column Chip is not interfering
      digitalWrite(rowDACSelectPin, LOW);              // Selects Row Chip    
      SPI.transfer(bytesToSend[0]);
      SPI.transfer(bytesToSend[1]);
      SPI.transfer(bytesToSend[2]);
      digitalWrite(rowDACSelectPin, HIGH);             // De-Selects Row Chip
      delay(1);                                                                //1ms delay for 12MHz Channel update