AD7124 (24-bit ADC) and epsilonRT library : how fast will it go?

JBeale

Well-known member
I'm just curious if anyone has used this AD7124 24-bit ADC at higher data rates, for example 1 kHz or above. The Analog Devices AD7124 datasheet claims it is capable of sampling rates up to 19200 sps.

Using the "continuous conversion" mode and whether or not I wait for a sample to complete, I get at most 320 samples per second, no matter what sampling rate I select, down to "1" which is maximum speed. I also tried only printing out only every 100th result, or printing nothing at all, in case there was a problem with USB overhead. I'm using a Teensy 4.0.

Looking at the /CS and Clock lines with a scope, I see the chip is selected for 41 usec every 1.033 msec. Some reads are 3 bytes, some 5 bytes, some two sets of 5 bytes. Apparently, only every third or fourth transaction ends up reading out a value.
My testing code is below.

Code:
// use epsilonRT library for AD7124
// https://github.com/epsilonrt/ad7124

#include <ad7124.h>

Ad7124Chip adc;

// Physical Pins
const int ssPin = 10;
const int outFlag = 9;

long dataWord;
byte ch;

// Sample Speed Setting (Full Power Sinc3 Filter Mode)
//  FW  |  SPS  |  SPS/Ch
//  600 |    6  |     1
//  100 |   60  |    10
//   40 |  156  |    26
//   20 |  300  |    50
int filterWord = 5;

void setup() {
  pinMode(outFlag, OUTPUT);
  digitalWrite(outFlag, 0);
  Serial.begin (115200);

  int state = adc.begin(ssPin);
  while (state < 0) {
    delay(100);
    state = adc.begin(ssPin);
  }

  // Configuring ADC in Full Power Mode & Continuous Mode (Prevents sleep mode)
  adc.setAdcControl(Ad7124::ContinuousMode, Ad7124::FullPower, true);
  adc.setMode(Ad7124::ContinuousMode);

  // Configure Channel 0
  adc.setConfig(0, Ad7124::RefInternal, Ad7124::Pga1, true);
  adc.setConfigFilter(0, Ad7124::Sinc3Filter, filterWord);
  adc.setChannel(0, 0, Ad7124::AIN3Input, Ad7124::AIN2Input, true);
  
  adc.enableChannel(0,true);
  delay(100);
}


void loop() {

const int setSize = 100;

  for (int i=0;i<setSize; i++) {
    adc.waitEndOfConversion(500);
    dataWord = adc.getData();
  }

  Serial.println(dataWord);
}
 
Looks like this library isn't designed for performance. It configures the SPI clock to only 1 MHz.

https://github.com/epsilonrt/ad7124...df375d24981cd49cd/src/ad7124-private.cpp#L430

With a quick search I found a couple datasheets. Hopefully this one is for the chip you're using?

https://www.analog.com/media/en/technical-documentation/data-sheets/ad7124-8.pdf

On page 11 it shows 3 different maximum SPI clock speeds, all of them relative to MCLK which I don't know. Maybe try to check what you're using for MCLK and which of those 3 modes you're using, then edit the library to use a faster SPI clock.
 
Thanks for having a look Paul. Still can't believe how much of your life must be debugging random other people's problems.

Yes, that's the one I'm using, AD7124-8. Good point about the SPI clock, I think the chip can take SCLK up to 5 MHz (minimum t3 = t4 = 100 ns).
Also, the chip has a "continuous conversion" mode, and also separately a "continuous readout" mode.
Continuous readout means you don't have to first write the data output register address, or anything else after setup, you simply clock out the 24 data bits each time DOUT/RDY* goes low to indicate conversion complete.
This library doesn't seem to support that mode, so I'll need to get busy and implement it. I was just being lazy to start with, and checking if anyone else already had.
Thanks again!

-john
 
detailed timings on AD7124 SPI bus

In case of interest, my Saleae logic analyzer says the reading loop in my test code goes like this, on the SPI bus:

1) wait a millisecond
2) read the 1-byte status register 0x00 (3 bytes, 25.4 usec)
3) wait a millisecond
4) read the 3-byte error register 0x06 (5 bytes, 42 usec)
5) waits a millisecond
6) finally read the 3-byte data register 0x02 for the selected channel (5 bytes, 42 usec),
7) wait only 2.375 usec, surprisingly enough...
8) again read the 3-byte error register 0x06 (5 bytes, 42 usec)
9) repeat the cycle at (1)

So the fastest this code can ever go, regardless of serial clock, is not faster than about 330 Hz due to three fixed 1-msec delays.
I think it was written for original Arduino and it uses delay() but not delayMicroseconds().
It also turned on the CRC so every transaction has the extra CRC byte, though that's hardly the major factor.

I suppose most people use this particular chip and this library in few-readings-per-second rates, where milliseconds don't really matter.

AD7124-SPI-1.png
 
experiment with removing 1 msec delay

I was able to get 4.4 kHz readout at a filterWord of 2, by simply removing the 1 msec delay in ad7124-private.cpp Ad7124Private::waitForSpiReady and waitForConvReady
Using the simple continuous read mode would get you faster still, but this is actually already faster than I need for now.

Code:
/***************************************************************************//**
* @brief Waits until a new conversion result is available.
*
* @param timeout - Count representing the number of polls to be done until the
*                  function returns if no new data is available.
*
* @return Returns true for success, false if not ready, AD7124_TIMEOUT for timeout
*******************************************************************************/
int
Ad7124Private::waitForConvReady (uint32_t timeout) {
  int ret;
  bool ready = false;
  bool timeout_en;

  timeout_en = (timeout > 0);

  do {

    /* Read the value of the Status Register */
    ret = readRegister (&reg[Status]);
    if (ret < 0) {

      return ret;
    }

    /* Check the RDY bit in the Status Register */
    ready = (reg[Status].value &
             AD7124_STATUS_REG_RDY) == 0;

    if (timeout) {
      // drv.delay (1);  // DEBUG: just removed this J.Beale 9/2/2022
      timeout--;
    }
  }
  while (!ready && timeout);

  return timeout_en && (timeout == 0) ? AD7124_TIMEOUT : ready;
}

AD7124-noDelay.jpg
 
Just a comment on your speed-up fix. With that line commented out, the function will just spin through "timeout" tests of the ready condition. The default timeout is 1000 ms, so it will spin through the loop 1000 times, which may take a very, very short time. Perhaps there is almost no delay required, but you should check the return value to make sure the function is returning "ready". If you find it's not ready, another way to speed it up and still have a real timeout would be to replace the call drv.delay(1) with a loop that makes 100 calls of delayMicroseconds(10), breaking out of the loop as soon as the ready flag is true.
 
I know It's been a few months, but I just stumbled into this while searching for something else related to the Ad7124. I have a library for the AD7124-4 here: https://github.com/NHBSystems/NHB_AD7124 if it helps anyone out. I originally started out hacking on the EpsilonRT library, but then it just mutated into it's own thing.

I have tested it to 9600sps while outputting to serial on a Teensy 3.2. It doesn't support the *-8 variety, but the main difference is just extra registers to support the additional channels, so it could still be a useful place to start.
 
Back
Top