ADS1252: Tutorial on High precision (24-bit) ADC

Hello all,

Introduction
Here is a tutorial/recommendation for those who need a high-precision [24-bit] analog to digital converter (ADC) that is easy to use (SPI but just 2 wires, and ADC itself has only 8 pins) and is fast (40,000 samples per second). Learning to use this ADC will also be a good beginner exercise in using Serial Peripheral Interface (SPI).

I’m no expert yet, but since I’ve played around with this for some time, feel free to ask if you have any questions.

The ADC I use is ADS1252 from Texas Instruments. It has a maximum of 24 bit digital readout precision, which is significantly better than Arduino’s inbuilt 10-bit ADC, so if you need to make some precise readings, this is useful.

I’ve spent the last couple of weeks working on reading the datasheet and gaining some very useful knowledge while figuring out how to code this thing, along with some help from forum members here, so now, I decided to make a small tutorial with the basic pseudocode as well as actual code.

You can see the datasheet here: http://focus.ti.com/lit/ds/symlink/ads1252.pdf

What to do
If you want to learn more details, for future use, about how exactly the interfacing (SPI) is working between the Arduino and the ADC, then read the “FULL DETAILS” section below (and consult the datasheet as well; this one’s pretty simple).
By the way, a very clear tutorial on SPI by Nick Gammon is here: http://www.gammon.com.au/forum/?id=10892

But if you want to just quickly use the ADC, I suggest you simply read the ADC clock source section below, then view the Pinout diagram below and plug the wires in as shown (be sure to add capacitors as appropriate too), and finally just use the code I pasted below… And you can start reading data at 24 bit precision (kinda) from the serial terminal.

ADC Clock Source
For the clock pin of this ADC, I used 2MHz; if you use a different frequency, you have to change some timing variables in the code, as outlined in the “Full details” section. As far as how to actually supply the clock (not a typical 2-pin crystal oscillator input), you have three options (thanks BenF for suggestions):

  1. Use CKOUT of Arduino/Atmega chip. Not very hard but you have to set some fuse bits; You can search Google or this forum for help on this. I recommend using the option of Prescaler of 8, so the ADC gets 2MHz instead of 16MHz.
  2. Set up a timer for your chosen frequency on one of Arduino/Atmega’s pins.
  3. Or easiest, buy a clock oscillator (has 4 pins, one of which goes to the CLK pin of the ADC). Buy one of low jitter, such as Crystek’s C33xx or S33xx (DIP)… http://www.crystekcrystals.com/crystal/spec-sheets/clock/S33xx.pdf

Pinout diagram (from TI datasheet)

Present code

#include <SPI.h>

#define MISOPIN 12
#define SCLKPIN 13

byte byte1; byte byte2; byte byte3;
// declare 3 bytes = 24 bits

void setup()
{
  Serial.begin(9600);
  
  pinMode(SCLKPIN, OUTPUT); pinMode(MISOPIN, INPUT);
  // corresponding to SCK pin and DRDY/DOUT pin on ADC
  
  reset_adc();
  // put ADC on reset at the outset
  
  SPI.begin();
  // initialize SPI (with default settings, including...
  // CPOL = 0: so that SCLK is normally LOW
  // CPHA = 0: data sampled on rising edge (LOW to HIGH)
  // perhaps try changing CPHA ??
  
  digitalWrite(SCLKPIN, LOW);
  // release ADC from reset; now we're at a known point
  // in the timing diagram, and just have to wait for
  // the beginning of a conversion cycle
}

void loop()
{
  if (digitalRead(MISOPIN) == HIGH) read_adc();
  // "sort of" an interrupt to go to read_adc routine;
  // can use hardware interrupt in future but now just poll
}

void reset_adc()
// to reset ADC, we need SCLK HIGH for min of 4 CONVCYCLES
// so here, hold SCLK HIGH for 5 CONVCYCLEs = 1440 usec
{
  digitalWrite(SCLKPIN, HIGH);
  delayMicroseconds(1440);
}

void read_adc()
{
  drdy_wait();
  // go to drdy_wait routine, where we wait for
  // DRDY phase to pass, and thus for DOUT phase to begin

  byte1 = SPI.transfer(0x00);
  byte2 = SPI.transfer(0x00);
  byte3 = SPI.transfer(0x00);
  // read in adc data (sending out don't care bytes)
  // and store read data into three bytes */

  Serial.println(byte1, DEC);
  Serial.println(byte2, DEC);
  Serial.println(byte3, DEC);
  Serial.println();
  // print out data;
  // will these instructions eat into time significantly?
  // possible improvement: store all data from multiple cycles
  // into array, and print out only later at end.
}

void drdy_wait()
// wait for DRDY to pass and to reach start-point of DOUT
{
  delayMicroseconds(30);
  // to be safe, 30 usec, instead of 27 usec, which is
  // the expected period of DRDY phase
}

FULL DETAILS
My approach is partly based on TI Application Note for same ADC: http://focus.ti.com/lit/an/slaa242/slaa242.pdf
This ADC only uses two pins to interface with the Arduino/microcontroller:
-SCLK pin (for commands: Synchronization, Power-down, or RESET)
-DRDY/DOUT pin (yes, only one pin)

The ADS1252 has DRDY (converted data ready indication) and DOUT (actual data output) both on the same pin, one followed by the other in a chronologically structured way, so I can use proper timing and interrupts to check when data is ready and when to get data.

So my method is to initially have the microcontroller reset the ADC, so that we have awareness/control of the timing from then on (also see Timing diagram below)…
-At the start of a new conversion cycle, the DRDY phase lasts for 36 MCLKs (modulator cycle periods), so each cyle, we can wait/delay for the DRDY phase to simply pass.
-Then the DOUT phase starts and lasts for 348 MCLKs (typical numbers), during which we can clock out the data.
-At the end of each conversion cycle (36 + 348 = 384 MCLKs total), there is a low-to-high (RISING edge) in the transition from the end of DOUT to the beginning of DRDY, which we can detect using a RISING-edge interrupt/poll on the DRDY/DOUT line.
-And then the next conversion cycle begins, and so on.

Timing diagram (from TI datasheet)

The math
I’m having the ADC clocked using CKOUT of 16MHz Atmega chip, with prescaler of 8, so it’s clocked with f_ADSCLK = 2 MHz, implying…

See datasheet for details on math below: http://focus.ti.com/lit/ds/symlink/ads1252.pdf

calculation formulas:
f_MCLK = f_ADSCLK / 6
DRATE = f_MCLK / 64
1 p_MCLK = 1 / f_MCLK
1 DRDY partition = 36 * p_MCLK
1 DOUT partition = 348 * p_MCLK
1 CONVCYCLE = 1 DRDY partition + 1 DOUT partition = 384 * p_MCLK

calculation with actual values:
f_ADSCLK = 2 MHz
f_MCLK = 0.333 MHz
DRATE = 5.21 KHz
p_MCLK = 0.75 usec

1 CONVCYCLE = 384 * p_MCLK = 288 usec
1 DRDY partition = 36 * p_MCLK = 27 usec
1 DOUT partition = 348 * p_MCLK = 261 usec

t_RESET_5 = 5 * CONVCYCLE = 1440 usec

PSEUDOCODE

MY INITIAL SEQUENCE
(1)
ACTION: reset by holding SCLK high for at least 4 conversion cycles
With 1 conversion cycle = 1 DRDY + 1 DOUT = 36 MCLK + 348 MCLK = 384 MCLK
RESULT: ADC is held into reset
(2)
ACTION: write SCLK LOW after holding it HIGH for 4 conversion cycles
RESULT: ADC is released from reset and begins operation

Then, for each of the conversion cycles, I do the following in a loop…

LOOP begins here
(1)
time_position: 0th MCLK; Beginning of conversion cycle, i.e., beginning of DRDY partition
ACTION: delay for an amount of time equivalent to 36 MCLK cycles
result: remaining time (36 MCLK cycles) of DRDY partition simply passes
(2)
time_position: 37th MCLK; End of DRDY; Beginning of DOUT partition
ACTION: read 24 bits of data
result: data read into microcontroller memory
(note: due to filter, data only starting from the 6th conversion cycle after reset is valid; see datasheet for details regarding the digital filter)
(3)
time_position: nth MCLK (post data-read-time); Somewhere in the middle of DOUT partition
ACTION: wait until interrupt triggered on DOUT/DRDY-line going from LOW to HIGH (i.e., RISING)
result: remaining time (post data-read-time) of DOUT partition simply passes
LOOP ends here

timing2.JPG

pinout.JPG

Thanks for that, it is interesting. 24 bit A/D is sure a tremendous dynamic range. What has been your findings on quality of the conversions done, noise, actual useful # of bits to use, etc. What are you using for the Vref for the chip? Any problems with circuit layout, etc.

Go Giants, 2010 World Champions!

Lefty

Hey Lefty,

Very interesting findings with the ongoing quest to improve noise in my data output. Reading various application notes (Analog Devices at analog.com has some great ones) regarding good supporting circuitry for ADCs.

After trying various configs, will report in a few days hopefully on what I find works best.

I'm using a load cell as my "sensor". Right now, according to my calculations, I think the precision is hovering around 17-18 bits. But I don't know if the values after a certain amount of digits are correct or just random. I'm in the process of getting my Arduino to also read an arbitrary force gauge by a standard manufacturer (which has RS-232 output) and then hope to answer the question: To how many digits is my ADC actually precise?

Right on, go Giants!

Any more news on using this chip?


Rob

I would love to hear if this is working for anyone too?

I'm not sure what's involved for the CKOUT fuse bits, and if there are any drawbacks to doing that. It looks pretty low-level though, so I'm not sure it's such a good idea for me.

It would seem to me, that I'd rather have one clock source for everything tho.

This is te CKOUT of the SPI hardware, aka as the SCK or PB5 or digital pin 13 or the led pin in the arduino duemilanove/uno.

Pin 13 is the LED pin, right?

Is this just the same thing that is done to get the MCP3208 working at 2MHz or higher? Which I just copied the other day and it works for that chip.

http://arduino.cc/forum/index.php?topic=53082.0

Yes, SPI can work at up to 8Mhz in a regular Arduino and at 10Mhz if you clock your atmega at 20Mhz its a really fast interface.

What I would like to know is the accuracy achieved with no obvious ground planes etc.

From everything I've read getting the full 24 bits is a real black art, OTOH all the data sheets just show the chips hooked up and they state an ENOB of 23.5 bits or whatever with no mention of PCB design or anything.

I would like to get some more definitive info about this.


Rob

That would need a very nice pcb, and a super voltage reference, and using instrumentation op-amps and lots of low noise resistors and lots of filtering, I have used some 12 bits adc's from Microchip and they are nice chips, but 24 bits is really another world, each bit is aprox 2.98x10^-7 V, that is really tiny!

It looks like to me that it really has only an effect 19-bit resolution, but you get a 24-bit number.

Thanks for nice information. I am using AD7781 for Weighing Machine application. I have tried above code with new delay time, seems delay not correct. Please help me for in chissing correct delay or do I need to change code? Regards, Harikesh

can anyone please give me connection diagram to interface ADS1252 and Arduino uno

Thank you.

hello dear giantsfan3 thank you so much for the tutorial i have some question for you in the part when you explain how you calculated the values for the time that you give to the sclk pin you do this p_MCLK = 0.75 usec i want to know how did you get that value y used this formula : 1 p_MCLK = 1 / f_MCLK but i get 3 usec i dont know where is my error please help me to understand how did you get that value of 0.75 usec

also if you could help me to calculate the values for a clk of 1.2MHz that would be great!

thank you so much for help me!

sorry if i write wrong in any part i dont have an excelent english

is theair any detaild information regarding this?

I don't see a chip select here, does that mean that you can't have other SPI components running simultaneously? i.e. SD card?

Hi can u provide schematic details ..interface between between ads1252 and ardino controller,(how to give different analog signal for vin shall i connect variable POT )this is for learning aspects iam doing ..i dont have sensor to connect vin

I may have logged in as cossoft, but I'm actually Mr. Skeptic in a cunning disguise. I've recently been using the Arduino ADC, and I wonder how much practical use a 24bit converter is to the people reading this forum. Respectfully, they're not thick. Far from it, but I feel that only expert professional electronics engineers would be able to build a real world device that can return a meaning full 24 bit reading of a signal.

Getting a reading from the converter of 1 unit from a 24 bit range is equivalent to a range of 144dBV. That's a lot of range. Surely a good few (8?) bits would simply be picking up the mains hum, Lady Gaga on the radio and my girlfriend's microwave? I would be interested in seeing the construction you used for your experiments.

The feasibility of using the full range on the ADS1252 can easily be demonstrated so all can be sold on it:-

  • Connect the input to 0.5 * Vcc using a potential divider
  • Sample the input a million times
  • Incrementally transfer data to a host PC
  • Post the readings here (or a graph)

I really hope that I'm wrong and overly sceptical, because I'd love 24 bits for my projects :money_mouth_face:

Well, the data sheet says the best you can expect is 19 bits of resolution, so don't hold your breath. And no doubt that's with a pcb configuration that has a very well-regulated power supply, proper pcb layout, and decoupling in accordance with the datasheet recommendations.

Simple tests that I did with the HX711 (a considerably slower 24 bit ADC, but available on breakout boards) showed it is capable of at most about 14 to 15 bits noise-free resolution.

That's very interesting. Lets take your 14 bits as a conservative case. The classic Arduino reads 10 bits. That's 4 bits less, I think. However lets also consider the case of a slow signal. If you over sample, I believe that you'll get back two additional bits and get good 12 bit readings from a standard board. That's probably something to consider in deciding to add an external ADC.