NOTE: This circuit is designed for extremely low level voltages in the nano and microvolt range.  If your sensor outputs a voltage in the high millivolt range or volt range you will need to change the gain of the first stage op amp (LTC1052 with a gain set at 1000 as shown in this schematic) and adjust the code gain to low numbers such as "GAIN = 1;"  to start. Since the second op amp has a biased output of 2.5 volts with a zero volt input on pin 2, The code variable OFFSET will need to be adjusted to calibrate your ADC reading to agree with your sensor reading. In this schematic, the ADC sees 2.5 volts as the zero point (half way between zero and + 5 volts) and will add your signal to 2.5 volts. So if your signal is + 0.3 volts, the ADC output will be 2.5 + 0.3 or 2.8 volts.
By setting the offset in the code to make the 2.5 volt ADC output read zero, you would then read your 0.3 volt input as  0.3 output from the ADC.  The same is true of negative voltages up to -2.5 volts. Any signal or sensor voltage greater than ± 2.5 volts  will simply read 5 volts or zero volts out of the ADC. The second op amp is single ended and will go to its rails with input voltages greater than   ± 2.5 volts. The good news it that you will probably not damage the ADC with a reasonable over voltage.

Working Arduino code is below the schematic.


24 BIT VOLT METER

This schematic is shown as the design that is wired for the Schäfer code (below) where the ADC SDI (Pin 7) is controlled by the Arduino board. If you prefer to use the Beale code, (goes to code on the main page) either hard wire Pin 7 to ground where the sample rate is 880 sample per second or wire Pin 7 high (to +5 VDC) for a sample rate of 6.7 samples per second. 

Diode D1 is clamping diode placed to limit the negative voltage to the ADC. D1 will turn on at its forward bias voltage and clip any voltage more negative than - 0.3 to -0.4 volts. The forward bias of the diode used should be between +0.3 and +0.4 volts. In the schematic configuration the diode will clip a negative voltage from the op amp at - 0.3 to -0.4 volts until the voltage is above -0.3 volts. I would like to place a red LED in the schematic to note the voltage has being clipped but the design involves an added transistor and becomes to complex for this startup schematic.

I have found it necessary to use a linear power supply because the linear design has far less high frequency noise and my noise measurements show that the output noise using a switcher driven supply is about twice as noisy as a good, well filtered, linear supply.

If you expect the op amp to be exposed to wide variations in temperature (more than 30 degrees F variation), I would suggest using the chopper stabilized LTC1052.  The LTC1052 is almost pin compatible with the LT1007.  The difference is with Pins 1 and 8 which do not allow output zero. Pins 1 and 8 must be wired with 0.1 caps to ground or the negative supply depending on the power supplied to the amp. The "Amp zero set" wiring on pin 1 and pin 8 must be removed.

The pin 1 and pin 8 caps are critical and leaving pin 1 and pin 7 open can destroy the chip.

The LTC1052 data sheet is here: http://www.linear.com/product/LTC1052     

Examples of op amp gain calculations

There are two devices to shift the measurement of voltage in the circuit above. The first device is the op amp gain, the second device is the ADC zero shift. As a place to start an op amp gain, it is best to assume a zero shift to be used by the ADC. If we start with a 3 place zero shift in the ADC, we can calculate the additional gain needed in the op amp.

setup assumptions... 

If you plan to use this circuit with various inputs you need to set the gain of the op amp to supply the ADC with zero to +/- 0.250 volts input volts - MAX.

Lets assume you want to measure a one microvolt signal. That would be a source voltage of 0.000001 volts. Or SOURCE VOLTS = 1.

Lets also assume you want a reading in microvolts so you want an output from the ADC to be "1.000". Or ADC OUTPUT READING.

We will also assume you want a zero shift in the ADC to be 3 places. Or ADC GAIN = 1000.

 With these limits in mind we can calculate the gain of the op amp. Or OP AMP GAIN.

Since we will get a decimal shift of 3 places from the ADC, our output from the op amp is 0.000001 x (3 decimal place shift) or x 1000.

This gives us the final value to the ADC to be .000001 x 1000 or .0001 volts.

If the original voltage into the op amp is .000001 and the output (or ADC input) is .0001 the gain of the op amp needs to be .0001/.000001 or 100.


Another way to think of this is to call the ADC shift a "gain" and then multiply your OP AMP GAIN times your ADC GAIN. In this case OP AMP GAIN times the ADC GAIN is 1000  and the final reading from the ADC is 1.000.

As a word equation this would be (SOURCE VOLTS) * (ADC GAIN) * (OP AMP GAIN) = ADC OUTPUT READING.

In this case OP AMP GAIN is:  1/(ADC GAIN) * (SOURCE VOLTS(  or  1/(1000 * .000001) = 1/.001 = 100.  Or OP AMP GAIN = 100


General "word" equations for this circuit

A general statement would be:

ADC OUTPUT READING  =  (SOURCE VOLTS) * (ADC GAIN) * (OP AMP GAIN)

from this statement we can say:

OP AMP GAIN = (ADC OUTPUT READING) / [(ADC GAIN) * (SOURCE VOLTS)]

and the critical ADC input is:

ADC input = (SOURCE VOLTS) * (OP AMP GAIN)       Which needs to be no more negative than -  0.250  volts

By the way - It is possible to measure microvolts directly with a 24 bit ADC and use a 6 place shift of the source voltage. This would change the ADC reading from 0.000001 with a 6 decimal shift to 1 as an output of the ADC. This is one of the great benefits of 24 bits. The problem here is that a 6 place shift is approaching the accuracy limit of the ADC. The 1 value is all you would get. Where amplifying the source voltage by 100 with an op amp would give at least a 3 place accuracy or 1.00. Amplifying by 1000 would give an accuracy to 4 places or 1.000. And the op amp output in volts is still safe for the ADC input at 0.001 volts.

When I say ADC zero shift I am talking about a kind of gain in the ADC program. There is NO amplification in an ADC but the code below acts like a gain setting. So setting the GAIN variable in the code below to 1,000,000 will shift the source voltage by six places.

CODE TO USE WITH THIS SCHEMATIC - Schäfer code

Copy the code between the page lines (below this line to the next line) and enter the code into a new Arduino program  blank page. It is critical that the libraries are added to the program just as they appear in this sample code. This code does not supply the libraries, the "Linduino" libraries must be downloaded as the text below describes.
 

start copy from the

"/*"


through and including the line:
 


 } //end ADC read and output loop


Begin Code below this line



/* begin code

Schäfer Code
Version 1.1
13 Sep 2014

This code is written by Stefan Schäfer of Heidelberg University,
Institute of Environmental Physics. This is an open source code and available
for any NON-COMMERCIAL application. Any proposed commercial use of this code is
allowed only with written permission of Stefan Schafer. Stefan's amateur radio call sign is DK7FC

The Original full code, from which this code below was developed, access to the
Linduino libraries, the use of the LTC Linduino libraries, and LTC license statements can
be found on this LTC page:

http://www.linear.com/solutions/linduino

Download the LTC Linduino libraries here:

Download the Linduino Library (Library only - please follow Quick Start Guide for instructions on install)

This will download a file called LTSketchbook.zip - This file holds all the necessary Arduino
libraries to run this code, below.

We are all extremely grateful for the cooperative work to create this library and code
between Arduino and Linear Technologies. This contribution by Linear Technologies will
enhance the Arduino community and independent developers for many years to come.

This code below was adapted by Stefan Schafer for use on this LTC2440 applications page.

>>> CRITICAL NOTE: To control SDI pin 7, connect the LTC2440 ADC SDI pin 7 to Arduino pin 11 <<<

*/



#include <Wire.h>                                      //had to add separately
#include <QuikEval_EEPROM.h>                 //from Linduino library
#include <LTC2440.h>                              //from Linduino library
#include <UserInterface.h>                       //from Linduino library
#include <LTC24XX_general.h>                //from Linduino library
#include <LT_SPI.h>                                  //from Linduino library
#include <Linduino.h>                              //from Linduino library
#include <LT_I2C.h>                                 //from Linduino library
#include <arduino.h>
#include <stdint.h>                                 //from Linduino library?
#include <SPI.h>

static int16_t OSR_mode = LTC2440_OSR_128; // here type values from 2^6 to 2^15, i.e.: 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768
                                                            // In this particular code line the 128 in LTC2440_OSR_128
                                                            // will yield a sample rate of 18 SPS

static float LTC2440_vref = 5.0;                         // Vref is the "reference" voltage supplied to the LTC2440 ADC
                                                                             // Vref/2 seems to give a wider neg dynamic range under 2.5 volts at Pin 5
const int nsamples = 50;                                   // number of data point averages - was 100
                                                                         // which gives lower noise levels
float GAIN=1;                                                     // if you are not sure make this number "1.0" to start
                                                                            // with seismic readings in microvolts, this Gain can be as high as 10000
                                                                            //(or shift decimal FIVE places to the left)
float OFFSET=0;                                                 // if you are not sure make this number "0.0" to start

float Volts_Out=0;                                             // ADC - Arduino board output in volts

// IMPORTANT NOTE: To control pin 7, connect the LTC2440ADC SDI (pin 7) to Arduino pin 11

void setup()
    {
        quikeval_SPI_init(); // Configure the spi port for 4MHz SCK
        quikeval_SPI_connect(); // Connect SPI to main data port
        Serial.begin(115200); // Initialize and set baud rate for the serial port to the PC
    }

void loop()
    { //begin ADC read and output loop

       uint8_t adc_command; // The LTC2440 command word
       int32_t adc_code = 0; // The LTC2440 code
       float adc_voltage = 0; // The LTC2440 voltage
       float adc_summe = 0; // sum of voltages in for-loop
       float adc_average = 0; // averaged voltage after for-loop
       uint16_t miso_timeout = 1000; // Used to wait for EOC
       int i;
       adc_command = OSR_mode; // Build the OSR command code

       for (i=0; i<nsamples; i++)

           { //Begin "for"

                   if(!LTC2440_EOC_timeout(LTC2440_CS, miso_timeout)) // Check for EOC
                       {LTC2440_read(LTC2440_CS, adc_command, &adc_code); // Throws out reading
                       adc_voltage = LTC2440_code_to_voltage(adc_code, LTC2440_vref);
                   } //End "if"

               adc_summe = adc_summe + adc_voltage;

           } //End "for"

       adc_average = adc_summe / nsamples;          // Get average value over nsamples sum
       Volts_Out = adc_average*GAIN + OFFSET;      // Allow for linear ADC voltage output calibration - the linear line graph of "a*x + b " is the model for GAIN and OFFSET where GAIN is "a" and OFFSET is "b"
      
       Serial.println(Volts_Out, 6);                            // Send Arduino Board Output from to serial USB/com port
                                                                             // to be read by PC/MAC/LINUX serial read program, input

    } //end ADC read and output loop

// end code here


END CODE above this line