Post Go back to editing

AD7175 register read glitch

I've been experimenting with an AD7175-8 and I've encountered a problem regarding register writting/reading. 
I'm setting up a register with a certain HEX value, yet when reading it back (to verify that the writing was successful) the ADC responds with that HEX value + 1. Why so?

Console output:

Succesfully connected to ADC
ID=3CDE

ADCMODE Register READ ERROR!
ADCMODE = 8001
Expected 8000

SETUPCON0 Register READ ERROR!
SETUPCON0 = 1321
Expected 1320

FILTCON0 Register READ ERROR!
FILTCON0 = 505
Expected 504

SETUP complete


Waveform capture from a Saleae logic level analyzer:
You can find it attached bellow.

The code that's running on the main uC:

#include <SPI.h>
#include <AD8555.h>

#define ENABLE_OP_AMP_SETUP 0
#define ENABLE_VERBOSE_OUTPUT 1
//op-amp related
int outputpin = 23;    //op-amp data in pin
int inputpin = 15;  //op-amp data out pin
byte opAmpData;     //stores op-amp register values

//intialize op-amp handle
AD8555 opamp(outputpin, inputpin);

//ADC hook-up lines
//ARDUINO
/*
  #define SCK   13
  #define MOSI  11
  #define MISO  12
  #define CS    10
*/


//ESP32

#define SCK   26
#define MOSI  14
#define MISO  27
#define CS    25


//SPI bus settings
#define SPISPEED 4000000

byte received[3];
int i = 0;

//ADC commands
#define READ_REG        0B01000000
#define WRITE_REG       0B00000000

//ADC Register Adressess
#define IDREG           0B00000111
#define ADCMODE         0B00000001
#define FILTCON0        0B00101000
#define IFMODE          0B00000010
#define SETUPCON0       0B00100000

//sample rates
#define SPS5            0B00010100
#define SPS10           0B00010011
#define SPS100          0B00001110
#define SPS1000         0B00001010
#define SPS5000         0B00001000
#define SPS10000        0B00000111
#define SPS31250        0B00000100
#define SPS50000        0B00000011
#define SPS250000       0B00000000

uint16_t id;
uint16_t register16 = 0;
uint16_t dataRegister;

unsigned long raw;
byte adcData[3];
bool ok = false;

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

  if (ENABLE_OP_AMP_SETUP) {
    pinMode(outputpin, OUTPUT);
    pinMode(inputpin, INPUT);
    opAmpSetup();
  }

  //SPI pins
  pinMode(MISO, INPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(SCK, OUTPUT);
  pinMode(CS, OUTPUT);

  digitalWrite(CS, HIGH);

  SPI.begin(SCK, MISO, MOSI, CS); //start the spi-bus
  SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE3));

  //terminate CONTINUOUS READ MODE
  exitContinuous();

  //read device ID
  id = readID();
  checkID(id);

  dataRegister = 0B1000000000000000;
  writeRegister16(ADCMODE, dataRegister);
  register16 = readRegister16(ADCMODE);
  checkRegisterData(register16, dataRegister, "ADCMODE");


  //configure SETUPCON0 register
  //set unipolar mode
  dataRegister = 0B0001001100100000;
  writeRegister16(SETUPCON0, dataRegister);
  register16 = readRegister16(SETUPCON0);
  checkRegisterData(register16, dataRegister, "SETUPCON0");

  //configure FILTCON0 register
  /*
     FILTCON SYNC5 + SYNC 1 FILTER
  */


  dataRegister = 0B0000010100000000;
  //chose SPS
  dataRegister |= SPS31250;
  writeRegister16(FILTCON0, dataRegister);
  register16 = readRegister16(FILTCON0);
  checkRegisterData(register16, dataRegister, "FILTCON0");


  //configure IFMODE register
  //enable continious read by setting CONTREAD to 1
  //ONCE CONTREAD is set any other command is ignored!!!
  dataRegister = 0B0000100010000000;
  writeRegister16(IFMODE, dataRegister);

  //setup complete
  if (ENABLE_VERBOSE_OUTPUT)
    Serial.println("SETUP complete");

  digitalWrite(CS, LOW);
  digitalWrite(MOSI, LOW);
  //attachInterrupt(digitalPinToInterrupt(12), readADC, FALLING);
}
void loop () {


  digitalWrite(CS, LOW);
  digitalWrite(MOSI, LOW);

  //read analog input
  if (Serial.available() > 0) {
    byte pcFlag = Serial.read();

    if (pcFlag == 115) {
      ok = true;
      delay(2000);
    }
  }

  while (1 && ok) {
    while (digitalRead(MISO)) {}


    raw = SPI.transfer(0);
    raw <<= 8;
    raw |= SPI.transfer(0);
    raw <<= 8;
    raw |= SPI.transfer(0);
    Serial.println(raw);


    //adcData[0] = SPI.transfer(0);
    //adcData[1] = SPI.transfer(0);
    //adcData[2] = SPI.transfer(0);


    //Serial.print(adcData[0], BIN);
    //Serial.print(" ");
    //Serial.print(adcData[1], BIN);
    //Serial.print(" ");
    //Serial.println(adcData[2], BIN);


    //Serial.write(adcData, sizeof(adcData));

    while (!digitalRead(MISO)) {}
  }

}

uint16_t readID() {
  uint16_t id;

  //read JEDEC ID
  digitalWrite(CS, LOW);
  SPI.transfer(READ_REG | IDREG);
  id = SPI.transfer(0);
  id <<= 8;
  id |= SPI.transfer(0);
  digitalWrite(CS, HIGH);

  return id;
}

void checkID(unsigned long id) {
  if (ENABLE_VERBOSE_OUTPUT) {
    if (id == 0x3CDE )  {
      Serial.print("\nSuccesfully connected to ADC");
      Serial.print("\nID=");
      Serial.println(id, HEX);
    }
    else
      Serial.println("ADC not responding");
  }
}

void checkRegisterData(uint16_t received, uint16_t expected, char* registerName) {
  if (ENABLE_VERBOSE_OUTPUT) {
    if (received == expected) {
      Serial.print("\n");
      Serial.print(registerName);
      Serial.print(" succesfully set to: ");
      Serial.println(received, HEX);
    }
    else {
      Serial.print("\n");
      Serial.print(registerName);
      Serial.println(" Register READ ERROR!");
      Serial.print(registerName);
      Serial.print(" = ");
      Serial.println(received, HEX);
      Serial.print("Expected ");
      Serial.println(expected);
    }
  }
}

uint16_t readRegister16(byte regAddress) {
  uint16_t register_value = 0;

  digitalWrite(CS, LOW);
  SPI.transfer(READ_REG | regAddress);
  register_value = SPI.transfer16(0);
  digitalWrite(CS, HIGH);

  return register_value;
}
void writeRegister16(byte regAddress, uint16_t userData) {
  digitalWrite(CS, LOW);
  SPI.transfer(WRITE_REG | regAddress);
  SPI.transfer16(userData);
  digitalWrite(CS, HIGH);
}
void exitContinuous() {
  while (digitalRead(MISO)) {};
  digitalWrite(CS, LOW);
  SPI.transfer16(0x44);
  digitalWrite(CS, HIGH);
}

void opAmpSetup() {
  //op-amp setup sequence
  //Start----------------Op-Amp setup--------------------

  // get Second Stage Gain code
  Serial.println("Input Second Stage Gain code (0..7)");
  // wait for user to enter value
  while (!Serial.available());
  // set Second Stage Gain
  if (!opamp.setSecondStageGain(Serial.parseInt())) {
    // opamp will return false if code is out of range
    Serial.println("Invalid Second Stage Gain code. Valid range is 0..7");
    return;
  }



  //get First Stage Gain code
  Serial.println("Input First Stage Gain code (0..127)");
  while (!Serial.available());
  //set First Stage Gain
  if (!opamp.setFirstStageGain(Serial.parseInt())) {
    Serial.println("Invalid First Stage Gain code. Valid range is 0..127");
    return;
  }


  // get offset code
  Serial.println("Input Offset code (0..255)");
  while (!Serial.available());
  //set op-amp offset
  if (!opamp.setOffset(Serial.parseInt())) {
    Serial.println("Invalid Offset code. Valid range is 0..255");
    return;
  }


  // Chose between programming/simulation mode
  // !!! Once the op-amp gets programmed (programming has been chosen) you cannot revrse the settings! It's permanent!
  Serial.println("Choose programming mode: Enter \"0\" for simulation, \"1\" for permanent programming");
  while (!Serial.available());
  int mode = Serial.parseInt();
  if (mode == 0) {
    // simulation mode
    opamp.simulate();
    Serial.println("Done!");
  } else if (mode == 1) {
    // permanent programming mode
    Serial.println("Make sure to meet programming requirements described in AD8555 datasheet:");
    Serial.println("- A 5.5 V supply is required");
    Serial.println("- The power supply must be able to deliver 250 mA of current");
    Serial.println("- At least 0.1 uF of decoupling capacitance is needed across the power pins of the device");
    Serial.println("\nWARNING: This operation can not be undone, all programming values are permanent");
    Serial.println("Continue? [y/N]");
    while (!Serial.available());
    if (Serial.read() == 'y') {
      opamp.program();
      Serial.println("Programming... done");
    } else {
      Serial.println("Operation canceled");
    }
  }
  //opAmpData = opamp.readData(PAR_SSG_CODE);
  //Serial.println("Op-Amp offset code= ");
  //Serial.println(opAmpData, BIN);
  //Finish----------------Op-Amp setup--------------------

}


AD7175.logicdata.zip
  • Hi,

    Apologies, this got overlooked because it was in the wrong section. I've moved your question to the Precision ADC section. Somebody here will be able to help you.

    - Lars 

  • Hi,

    May I know what is your SCLK frequency? The DOUT and RDY functions share a pin on the ADC. So, the DRDY pin functions as a ready pin when /CS is low. Every time a conversion is completed, the pin goes low, indicating to the microprocessor that a valid conversion is available. When the user requests a read of the data register, the DRDY pin functions are a DOUT pin. When pulses are applied to the SCLK pin, the data is placed on the DOUT pin. The change from the DOUT to the RDY function occurs on the last SCLK rising edge. The microprocessor is latching the bits on the SCLK rising edge. So, if the microprocessor is slow, then the DRDY pin is functioning as a RDY pin when the LSB is latched into the microprocessor. So, the microprocessor reads the value of the RDY pin rather than the LSB, causing the LSB to be a 1. To prevent this, a faster microprocessor must be used. Alternatively, general-purpose input/output pins of the microprocessor can be used to represent a serial interface. By bit-banging, the user has more control over the read instant. By reading the values on the DOUT pin when SCLK is low rather than latching in the data on the SCLK rising edge, all bits of the data read will be valid. For AD717x, by default,  the DRDY pin changes functionality after a period of time following the last SCLK rising edge, the SCLK edge on which the LSB is read by the processor. This time is the T7 which is 10 ns minimum as shown on the timing diagram of the datasheet. However, using the DOUT_RESET bit, the instant at which the DRDY pin changes from being a DOUT pin to RDY pin is programmable.

    Thanks,

    Jellenie

  • Hi,

    I cannot open the file, do you have a screen shot copy of your digital signals? Yes, 4MHz SCLK is too slow for your configuration, it must be between 6MHz to 20MHz. But I would prefer to try using the DOUT_RESET bit in the IFMODE register and see if can solve the issue.

    Thanks,

    Jellenie

  • The SCLK frequency may be found in the code of the first post, line 32.

    //SPI bus settings
    #define SPISPEED 4000000

    So, it is 4MHz. I've also tried with 8MHz. The results are the same, even with 8MHz.

     So, if the microprocessor is slow, then the DRDY pin is functioning as a RDY pin when the LSB is latched into the microprocessor

    The ipotesis stated above may explain the problem, but I'm pretty sure it's no the case. The uC that I'm using is an ESP32, which is capable of delivering up to 45MHz SCLK speed; way more that the ADC can handle. 

    Did you take a look on the logic analyzer file attached to the first post? 

    Thanks!

  • Setting out the DOUT_RESET bit in the IFMODE register solved my issue. 
    I had no problem downloading the logic data capture and opening it. Have you tried downloading the following software?
    Logic analyzer software from Saleae 

    Thank you for your support!