Pages: [1]   Go Down
Author Topic: ADS1252: Tutorial on High precision (24-bit) ADC  (Read 11814 times)
0 Members and 1 Guest are viewing this topic.
Portland, OR
Offline Offline
Sr. Member
****
Karma: 5
Posts: 260
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
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 (19.15 KB, 698x153 - viewed 516 times.)

* pinout.JPG (51.57 KB, 659x427 - viewed 559 times.)
« Last Edit: April 07, 2011, 12:49:27 pm by giantsfan3 » Logged

Left Coast, CA (USA)
Online Online
Brattain Member
*****
Karma: 331
Posts: 16517
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Portland, OR
Offline Offline
Sr. Member
****
Karma: 5
Posts: 260
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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!
Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 121
Posts: 8443
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Any more news on using this chip?

______
Rob
Logged

Rob Gray aka the GRAYnomad www.robgray.com

SF Bay Area
Offline Offline
Full Member
***
Karma: 1
Posts: 182
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Portugal
Offline Offline
God Member
*****
Karma: 5
Posts: 962
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

SF Bay Area
Offline Offline
Full Member
***
Karma: 1
Posts: 182
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Portugal
Offline Offline
God Member
*****
Karma: 5
Posts: 962
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 121
Posts: 8443
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Rob Gray aka the GRAYnomad www.robgray.com

Portugal
Offline Offline
God Member
*****
Karma: 5
Posts: 962
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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!
Logged

SF Bay Area
Offline Offline
Full Member
***
Karma: 1
Posts: 182
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

India
Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Pages: [1]   Go Up
Jump to: