This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

ADS1255: ADS1255 Continuous Read Glitch

Part Number: ADS1255
Other Parts Discussed in Thread: ADS1256

Hi!

Regarding ADS1255: I've been trying to make it work with an ESP32 (the platform is not important though) at it's full potential of 30kSPS.
Unfortunatelly, reading data in continuous mode (RDATAC) yields some strange behaviour: the raw ADC values that I'm getting are not stable at all varying by a lot (more than 5000000 in magnitude), compared to using the normal reading mode (RDATA) where the values vary by no more than a few thousands (which corresponds to the specified noise performance). 
I'm using Arduino IDE and an ESP32 connected via SPI to talk with the ADC. 
Some other components include an AD8555 op-amp and a +2.5V low noise voltage source, the ADR431BRZ. (see the folowing schematic).

The code that I use for setting up the ADC can be found below and is an adaption of the one posted over here by darioslavi.

Apparently, the registers don't seem to be set up properly. For example after writing a value of 0B00000001 in the Status Register (0x00h), I'm reading back 0 instead of 1. Why so?
Also, in the loop function, am I continuously reading the ADC output in the right way? Am I missing something?

Thanks for your help! Here's the code that I use.

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

#define SCK   14
#define MOSI  12
#define MISO  27
#define DRDY  26
#define CS 5


#define SPISPEED 1200000

#define WREG    0x50
#define RDATA   0x01
#define RDATAC  0x03
#define RREG    0x10

#define ENABLE_OP_AMP_SETUP 0

unsigned long adc_val = 0; // store reading
int outpin = 19; //op-amp data in pin


AD8555 opamp(outpin);

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

  if (ENABLE_OP_AMP_SETUP) {
    //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 code
    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;
    }


    // First Stage Gain code
    Serial.println("Input First Stage Gain code (0..127)");
    while (!Serial.available());

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


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

    // Programming mode
    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");
      }
    }

    //Finish----------------Op-Amp setup--------------------
  }

  pinMode(MISO, INPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(SCK, OUTPUT);
  pinMode(CS, OUTPUT);
  pinMode(DRDY, INPUT);
  delay(1);

  delay(500);
  SPI.begin(SCK, MISO, MOSI, CS); //start the spi-bus
  delay(500);

  //init

  while (digitalRead(DRDY)) {}  // wait for ready_line to go low
  SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); // start SPI
  delayMicroseconds(10);

  //Reset to Power-Up Values (FEh)
  SPI.transfer(0xFE);
  delayMicroseconds(100);

  SPI.endTransaction();

  while (digitalRead(DRDY)) {}  // wait for ready_line to go low
  SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); // start SPI

  byte status_reg = 0x00;  // address of status reg (datasheet p. 30)
  byte status_data = 0B0000001;
  byte status_read;
  //000 0 0 0 1
  //000 - Factory programmed Identification Bits
  //0 - set MSB transfer
  //0 - Auto-Calibration Disabled
  //0 - Buffer Disabled
  //1 - duplicates state of DRDY pin
  SPI.transfer(WREG | status_reg);
  SPI.transfer(0x00);   // 2nd command byte, write one register only
  SPI.transfer(status_data);   // write the databyte to the register
  delayMicroseconds(10);



  Serial.println("Status register values: ");
  SPI.transfer(RREG | status_reg);
  SPI.transfer(0x00);   // 2nd command byte, read one register only
  SPI.transfer(status_read);
  delayMicroseconds(10);
  Serial.print("Expected: ");
  Serial.println(status_data, BIN);
  Serial.print("Received: ");
  Serial.println(status_read);


  //PGA SETTING
  //1 ±5V
  //2 ±2.5V
  //4 ±1.25V
  //8 ±0.625V
  //16 ±312.5mV
  //32 ±156.25mV
  //64 ±78.125mV

  byte adcon_reg = 0x02; //A/D Control Register address: 0x02
  byte adcon_data = 0B00000000;
  // 0 00 00 000
  // 0 reserved always 0
  // 00 - Clock Out off
  // 00 - Sensor Detec off
  // 000 - PGA gain = 1
  SPI.transfer(WREG | adcon_reg);
  SPI.transfer(0x00);   // 2nd command byte, write one register only
  SPI.transfer(adcon_data);   // write the databyte to the register
  delayMicroseconds(10);



  byte drate_reg = 3; //data rate register address: 0x03
  byte drate_data = 0B11110000;
  // 11110000
  //set data rate to max. (30000 SPS)
  SPI.transfer(WREG | drate_reg);
  SPI.transfer(0x00);   // 2nd command byte, write one register only
  SPI.transfer(drate_data);   // write the databyte to the register
  delayMicroseconds(10);



  byte mux_reg = 0x01; //Mux Register (Address 01h)
  byte mux_data = 0B00001111;
  // 0000 1111
  // 0000 - AIN0 (default)
  // 1111 - AINCOM
  SPI.transfer(WREG | mux_reg); // MUX register
  SPI.transfer(0x00);   // 2nd command byte, write one register only
  SPI.transfer(mux_data);   // write the databyte to the register
  delayMicroseconds(10);

  byte channel = 0;
  byte data = 0B00001000; //AIN-channel and AINCOM
  //configured for single ended input measurement on AIN0
  //AINCON = VREFP = +2.5V
  
  SPI.transfer(0x50 | mux_reg); // MUX register
  SPI.transfer(0x00);   // 2nd command byte, write one register only
  SPI.transfer(data);   // write the databyte to the register
  delayMicroseconds(10);

  //SYNC command 1111 1100
  SPI.transfer(0xFC);
  delayMicroseconds(10);

  //WAKEUP 0000 0000
  SPI.transfer(0x00);
  delayMicroseconds(10);

  SPI.endTransaction();

  while (digitalRead(DRDY)) {}  // wait for ready_line to go low

  SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); // start SPI
  SPI.transfer(RDATAC); // Read Data Continuous command 0000  0011 (03h)
  delayMicroseconds(10);
  SPI.endTransaction();

  Serial.println("ADC configured, starting sampling");
}


void loop()
{
  while (digitalRead(DRDY)) {} ;

  SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); // start SPI
  adc_val = SPI.transfer(0);
  adc_val <<= 8; //shift to left
  adc_val |= SPI.transfer(0);
  adc_val <<= 8;
  adc_val |= SPI.transfer(0);
  SPI.endTransaction();

  //The ADS1255/6 output 24 bits of data in Binary Two's
  //Complement format. The LSB has a weight of
  //2VREF/(PGA(223 − 1)). A positive full-scale input produces
  //an output code of 7FFFFFh and the negative full-scale
  //input produces an output code of 800000h.
  if (adc_val > 0x7fffff) { //if MSB == 1
    adc_val = (16777215ul - adc_val) + 1; //do 2's complement
  }
  Serial.println(adc_val);
}

  • Hi Alin,

    Welcome to the TI E2E Forums!

    One thing to be aware of when using the RDATAC mode is that it is easy for data to get corrupted if it is not read quickly by the MCU. What happens is the output shift register that clocks out the data will start to shift out the conversion data, but in the middle of this process a new ADC conversion completes and is then loaded into this shift register. The result of this is that you read a corrupted result that is a combination of old and new data.

    ...When using the "RDATA" command the data in the output shift register is first buffered and then clocked out to prevent data from being corrupted. Therefore, if you're MCU is not able to quickly respond to the /DRDY interrupt and read the data before the next /DRDY falling edge, you likely should be using the RDATA command to retrieve data.

    To test if this is indeed the case you might try decreasing the output data rate and seeing if the output data integrity improves for lower data rates..

    Alin Paun said:
    Apparently, the registers don't seem to be set up properly. For example after writing a value of 0B00000001 in the Status Register (0x00h), I'm reading back 0 instead of 1. Why so?

    Regarding this behavior, bit 0 of the STATUS register mirrors the state of the /DRDY pin and it is read-only. Therefore, you may read back a different result than what you program for this register.

    Alin Paun said:
    Also, in the loop function, am I continuously reading the ADC output in the right way? Am I missing something?

    Polling for /DRDY is okay, but if possible I would try to implement a /DRDY falling edge interrupt since this is usually a bit faster than polling.

    Regarding the data format, I would recommend making "adc_val" a 32-bit signed integer and simply sign-extending the signed 24-bit conversion result into the signed 32-bit datatype. You would simply look at the most significant bit and add an additional "0x00" before the data if the MSB is 0, or "0xFF" before the data if the MSB is 1.

    Best regards,
    Chris

  • Apparently, I was missing something after all, regarding the process of reading a register. According to timing diagram for RREG (datasheet pg.31 top section) a delay equal to t6 must occur between RREG command and the request of data. I was missing that delay in the code posted in the 1st post. Furthermore, I was requesting the data wrong by doing SPI.transfer(register_values), instead of register_values = SPI.transfer(0); . Now, everything works ok, and I get the following output:

    Status register values: 
    Received: 110000
    
    Control register values: 
    Expected: 0
    Received: 0
    
    Data rate register values: 
    Expected: 11110000
    Received: 11110000
    
    Mux register values: 
    Expected: 1111
    Received: 1111
    
    ADC configured, attatching interrupt
    

    As you can see from above, the data rate is set to max. output rate, which is 30kSPS.
    Unfortunatelly, I'm still getting the same glitches in Serial Monitor as before when running at 30kSPS.

    13451
    11234
    3667930 <-- glitch
    3684890 <-- glitch
    13540
    8817
    8539
    10400
    11970
    12013
    10797
    7612
    7774
    8519
    9311
    12377
    11000
    12283
    10557
    7618
    7561
    13645
    12240
    7590
    10393
    9836
    9599
    9158
    9582
    9497
    9564
    13705
    12662
    15112
    11116
    7191
    6132
    7784
    10734
    9640
    15089
    15656
    16948
    16632
    15191
    18962
    15995
    14121
    10297
    10718
    13494
    13087
    9359
    10893
    16876
    16560
    13794
    9830
    10799
    12213
    11915
    11972
    11491
    12462
    14596
    12919
    8567
    11307
    11115
    10402
    6334
    3503
    5102
    8721
    13790
    11200
    9905
    12333
    13817
    13270
    14175
    17417
    19000
    16631
    10726
    10038
    12735
    12860
    12359
    10500
    13251
    3799336 <-- glitch
    3549206 <-- glitch
    6365
    8527
    11246
    9708
    8475
    

    However, if i switch to a lower rate, as Christopher has suggested I'm getting no glitch at all :)
    I guess the SPI driver of ESP32 for Arduino IDE has something to do with that, being a little to slow for the max. output data rate of the ADC. 

    The code that I'm using can be found bellow. I have:
    1. Switched to uint32_t for var 
    2. Handled the DRDY event by implementing a /DRDY falling edge interrupt
    3. Commented out the var conversion structure, in order to reduce delay, and get the raw output in the Serial Console. 

    Still, the setup seems unable to handle 30kSPS. Is there any more room for improvement in order to get the full 30kSPS?

    Code:

    #include <AD8555.h>
    #include <SPI.h>
    
    #define SCK   14
    #define MOSI  12
    #define MISO  27
    #define DRDY  26
    #define CS 5
    
    
    #define SPISPEED 1800000
    #define CLKIN 7680000 //7.68MHz (external crystal frequency)
    
    #define WREG    0x50
    #define RDATA   0x01
    #define RDATAC  0x03
    #define RREG    0x10
    #define SDATAC  0x0F
    
    #define ENABLE_OP_AMP_SETUP 0
    
    uint32_t var; // store reading
    int outpin = 19;    //op-amp data in pin
    int inputpin = 25;  //op-amp data out pin
    byte opAmpData;
    byte register_values;
    byte buf[24];
    
    //according to datasheet pg.6 t6 should be at leas 50 time CLKIN (master clock) period
    int t6 = round((float)1 / CLKIN * 50 * 1000000);
    
    int counter = 0;
    unsigned long start_time;
    
    AD8555 opamp(outpin, inputpin);
    
    void setup()
    {
      Serial.begin(2000000);
    
      if (ENABLE_OP_AMP_SETUP) {
        //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 code
        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;
        }
    
    
        // First Stage Gain code
        Serial.println("Input First Stage Gain code (0..127)");
        while (!Serial.available());
    
        if (!opamp.setFirstStageGain(Serial.parseInt())) {
          Serial.println("Invalid First Stage Gain code. Valid range is 0..127");
          return;
        }
    
    
        // Offset code
        Serial.println("Input Offset code (0..255)");
        while (!Serial.available());
        if (!opamp.setOffset(Serial.parseInt())) {
          Serial.println("Invalid Offset code. Valid range is 0..255");
          return;
        }
    
        // Programming mode
        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--------------------
      }
    
    
    
      pinMode(MISO, INPUT_PULLUP);
      pinMode(MOSI, OUTPUT);
      pinMode(SCK, OUTPUT);
      pinMode(CS, OUTPUT);
      pinMode(DRDY, INPUT);
    
       //init SPI bus
      delay(500);
      SPI.begin(SCK, MISO, MOSI, CS); //start the spi-bus
      delay(500);
    
     
    
      while (digitalRead(DRDY)) {}  // wait for ready_line to go low
      SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); // start SPI
    
      //send SDATAC command
      SPI.transfer(SDATAC);
      delayMicroseconds(100);
    
      //Reset to Power-Up Values (FEh)
      SPI.transfer(0xFE);
      delayMicroseconds(100);
    
      SPI.endTransaction();
    
      while (digitalRead(DRDY)) {}  // wait for ready_line to go low
    
      SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); // start SPI
    
      byte status_reg = 0x00;  // address of status reg (datasheet p. 30)
      byte status_data = 0B0000001;
      //000 0 0 0 1
      //000 - Factory programmed Identification Bits
      //0 - set MSB transfer
      //0 - Auto-Calibration Disabled
      //0 - Buffer Disabled
      //1 - duplicates state of DRDY pin
      SPI.transfer(WREG | status_reg);
      SPI.transfer(0x00);   // 2nd command byte, write one register only
      SPI.transfer(status_data);   // write the databyte to the register
      delayMicroseconds(10);
    
    
      register_values = 0;
      Serial.println("Status register values: ");
      SPI.transfer(RREG | status_reg);
      SPI.transfer(0x00);   // 2nd command byte, read one register only
      delayMicroseconds(t6);
      register_values = SPI.transfer(0);
    
      Serial.print("Received: ");
      Serial.println(register_values, BIN);
    
    
      //PGA SETTING
      //1 ±5V
      //2 ±2.5V
      //4 ±1.25V
      //8 ±0.625V
      //16 ±312.5mV
      //32 ±156.25mV
      //64 ±78.125mV
    
      byte adcon_reg = 0x02; //A/D Control Register address: 0x02
      byte adcon_data = 0B00000000;
      // 0 00 00 000
      // 0 reserved always 0
      // 00 - Clock Out off
      // 00 - Sensor Detec off
      // 000 - PGA gain = 1
      SPI.transfer(WREG | adcon_reg);
      SPI.transfer(0x00);   // 2nd command byte, write one register only
      SPI.transfer(adcon_data);   // write the databyte to the register
      delayMicroseconds(10);
    
      register_values = 0;
      Serial.println("Control register values: ");
      SPI.transfer(RREG | adcon_reg);
      SPI.transfer(0x00);   // 2nd command byte, read one register only
      delayMicroseconds(t6);
      register_values = SPI.transfer(0);
    
      Serial.print("Expected: ");
      Serial.println(adcon_data, BIN);
      Serial.print("Received: ");
      Serial.println(register_values, BIN);
    
    
      byte drate_reg = 0x03; //data rate register address: 0x03
      byte maxSampleRate = 0B11110000;
      // 11110000
      //set data rate to max. (30000 SPS)
      byte minSampleRate = 0B00000011;
      // 00000011
      //set data rate to min. (2.5SPS SPS)
      SPI.transfer(WREG | drate_reg);
      SPI.transfer(0x00);   // 2nd command byte, write one register only
      SPI.transfer(maxSampleRate);   // write the databyte to the register
      delayMicroseconds(10);
    
      register_values = 0;
      Serial.println("Data rate register values: ");
      SPI.transfer(RREG | drate_reg);
      SPI.transfer(0x00);   // 2nd command byte, read one register only
      delayMicroseconds(t6);
      register_values = SPI.transfer(0);
    
      Serial.print("Expected: ");
      Serial.println(maxSampleRate, BIN);
      Serial.print("Received: ");
      Serial.println(register_values, BIN);
    
    
      byte mux_reg = 0x01; //Mux Register (Address 01h)
      byte mux_data = 0B00001111;
      // 0000 1111
      // 0000 - AIN0 (default)
      // 1111 - AINCOM
      SPI.transfer(WREG | mux_reg); // MUX register
      SPI.transfer(0x00);   // 2nd command byte, write one register only
      SPI.transfer(mux_data);   // write the databyte to the register
      delayMicroseconds(10);
    
      register_values = 0;
      Serial.println("Mux register values: ");
      SPI.transfer(RREG | mux_reg);
      SPI.transfer(0x00);   // 2nd command byte, read one register only
      delayMicroseconds(t6);
      register_values = SPI.transfer(0);
    
      Serial.print("Expected: ");
      Serial.println(mux_data, BIN);
      Serial.print("Received: ");
      Serial.println(register_values, BIN);
    
      //SYNC command 1111 1100
      SPI.transfer(0xFC);
      delayMicroseconds(10);
    
      //WAKEUP 0000 0000
      SPI.transfer(0x00);
      delayMicroseconds(10);
    
      SPI.endTransaction();
    
      while (digitalRead(DRDY)) {}  // wait for ready_line to go low
    
      SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); // start SPI
      SPI.transfer(RDATAC); // Read Data Continuous command 0000  0011 (03h)
      delayMicroseconds(t6);
      SPI.endTransaction();
    
      Serial.println("ADC configured, attatching interrupt");
     
      attachInterrupt(digitalPinToInterrupt(DRDY), sampleADC, FALLING);
    
    }
    
    
    void loop()
    {
    
    }
    
    void sampleADC() {
    
      SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1)); // start SPI
      var = SPI.transfer(0);
      var <<= 8; //shift to left
      var |= SPI.transfer(0);
      var <<= 8;
      var |= SPI.transfer(0);
      SPI.endTransaction();
    
      //  if (var > 0x7fffff) { //if MSB == 1
      //    var = (16777215ul - var) + 1; //do 2's complement
      //  }
      Serial.println(var, DEC);
    }
    

  • Hi Alin,

    Good catch with the "t6" delay time!

    I'm not sure how much faster you'll be able to make your code but here are a few suggestions that are worth trying...

    1.  Try to avoid, or at least reduce how often you print data to the console.

      The serial print function can be slow so if you can avoid calling it I would. However, if you need to get data out to your console then what I would recommend is to store data into an array (of size 10 or 20, for example) and only print data to the console in batches when this array is full. In this way, you'll minimize the overhead time required to perform the serial print.

    2. If you're not already using the fastest SCLK frequency for the ADS1256, then I would increase your SCLK frequency to read the data faster. SCLK can be upto 1/4 the ADC's clock frequency (so 1.92 MHz when using a 7.68 MHz ADC clock).


    3. This may or may not work, but you can try using the "RDATA" command to read data in your collection loop.

      The advantage of using "RDATA" is it helps to prevent data corruption when reading data during a /DRDY falling edge. However, this also requires that you send an additional SPI byte, which further slows down your read operation. Therefore, over time you might start to lose data if your read operation cannot keep up with the 30 kSPS data rate.

    Best regards,
    Chris