[Home] [Downloads] [Search] [Help/forum]

Gammon Forum

See www.mushclient.com/spam for dealing with forum spam. Please read the MUSHclient FAQ!

[Folder]  Entire forum
-> [Folder]  Electronics
. -> [Folder]  Microprocessors
. . -> [Subject]  ADC conversion on the Arduino (analogRead)
Home  |  Users  |  Search  |  FAQ
Username:
Register forum user name
Password:
Forgotten password?

ADC conversion on the Arduino (analogRead)

Postings by administrators only.

[Refresh] Refresh page


Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Fri 27 Feb 2015 08:24 AM (UTC)

Amended on Fri 05 Feb 2016 04:24 AM (UTC) by Nick Gammon

Message
This page can be quickly reached from the link: http://www.gammon.com.au/adc


Introduction


The Arduino has an ADC (Analog to Digital Converter) which is connected to various input pins on the board. In the case of the Uno they are labelled A0 to A5.

The basic usage is very simple:


int value = analogRead (A0);  // read A0


The results are a number in the range 0 to 1023.

Using default settings, a return value of 0 would represent approximately 0V, and a return value of 1023 (the maximum) would represent approximately 5V. However more about the exact conversion details later.

This thread explores various things you can do with the ADC, including:


  • Speeding it up
  • Using different reference voltages
  • Making it non-blocking
  • Using an interrupt
  • Reading other things such as the internal chip temperature
  • Measuring resistances
  • Measuring voltages higher than 5V
  • Interpreting the results accurately


Timing


The ADC takes 13 ADC clock cycles to perform a conversion, except the first one after the ADC is enabled, at which time it takes 25 ADC cycles, while the circuitry is initialized.

You can choose various prescalers, from 2 to 128. This divides down the processor clock speed to give an ADC clock speed. You can do that by changing the ADCSRA register, like this:



ADCSRA &= ~(bit (ADPS0) | bit (ADPS1) | bit (ADPS2)); // clear prescaler bits

ADCSRA |= bit (ADPS0);                               //   2  
ADCSRA |= bit (ADPS1);                               //   4  
ADCSRA |= bit (ADPS0) | bit (ADPS1);                 //   8  
ADCSRA |= bit (ADPS2);                               //  16 
ADCSRA |= bit (ADPS0) | bit (ADPS2);                 //  32 
ADCSRA |= bit (ADPS1) | bit (ADPS2);                 //  64 
ADCSRA |= bit (ADPS0) | bit (ADPS1) | bit (ADPS2);   // 128


The conversion time will be 13 times the clock period (or 25 for the first conversion) thus each one would be (assuming a 16 MHz clock):


Prescaler
    2    * 13 * 1/16E6  = 0.000001625 (  1.625 µs)
    4    * 13 * 1/16E6  = 0.00000325  (  3.25  µs)
    8    * 13 * 1/16E6  = 0.0000065   (  6.5   µs)
   16    * 13 * 1/16E6  = 0.000013    ( 13     µs)
   32    * 13 * 1/16E6  = 0.000026    ( 26     µs)
   64    * 13 * 1/16E6  = 0.000052    ( 52     µs)
  128    * 13 * 1/16E6  = 0.000104    (104     µs)


Taking the inverse of the period we can work out the maximum theoretical conversion rate per second:


Prescaler  Conversions/sec
  2           615,385
  4           307,692
  8           153,846
 16            76,923
 32            38,462
 64            19,231
128             9,615


Of course, it won't ever get that good because there would be code needed to start the conversion, get the result, etc., but this is a ballpark figure.


Tip:

There is an extra time before a conversion starts. The conversion starts on the leading edge of the ADC clock, not the moment the code asks for it. In the case of a scaler of 128, there could be 127 extra (processor) clock cycles added, because the hardware has to wait for the next ADC clock cycle. This could add 7.938 µs to the conversion time.

Using a lower prescaler will not only make the conversion faster, but will also reduce this wait time.

A more accurate average conversion time would be 13.5 times the ADC clock time, as that 0.5 clock cycles allows for you having to wait, on average, half a clock cycle.



The datasheet mentions optimal ADC clock frequencies so let's see what the ADC clock will be for each prescaler:



Prescaler ADC Clock
          Frequency
          (kHz)
          
  2       8000
  4       4000
  8       2000
 16       1000
 32        500
 64        250
128        125


From the datasheet, section 23.4:

Quote:

By default, the successive approximation circuitry requires an input clock frequency between 50 kHz and 200 kHz to get maximum resolution. If a lower resolution than 10 bits is needed, the input clock frequency to the ADC can be higher than 200 kHz to get a higher sample rate.


Thus the choice by the analogRead library of a prescaler of 128 is the only one that gives the maximum resolution. The next prescaler (64) would give a frequency (250 kHz) slightly higher than the recommended one, however that could well be acceptable for many applications.

Note that the datasheet (Table 28-7. ADC Characteristics) mentions that the ADC clock frequency range is 50 kHz to 1000 kHz. Thus (at this CPU clock speed of 16 MHz) the smallest prescaler you are allowed to use is 16, which is born out by testing below.

Accuracy with various prescalers


The test below gets an average of 1000 readings of 4 voltages on A0 to A3, namely:


  • 5V (should return 1023)
  • 3.3V (should return 674)
  • 2.5V (should return 511)
  • 0V (should return 0)




// Prescaler accuracy test

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  
  ADCSRA &= ~(bit (ADPS0) | bit (ADPS1) | bit (ADPS2)); // clear prescaler bits
  
  // uncomment as required
  
//  ADCSRA |= bit (ADPS0);                               //   2  
//  ADCSRA |= bit (ADPS1);                               //   4  
//  ADCSRA |= bit (ADPS0) | bit (ADPS1);                 //   8  
//  ADCSRA |= bit (ADPS2);                               //  16 
//  ADCSRA |= bit (ADPS0) | bit (ADPS2);                 //  32 
//  ADCSRA |= bit (ADPS1) | bit (ADPS2);                 //  64 
  ADCSRA |= bit (ADPS0) | bit (ADPS1) | bit (ADPS2);   // 128

  }  // end of setup

const int ITERATIONS = 1000;
unsigned long totals [6];
const byte lowPort = 0;
const byte highPort = 3;

void loop ()
  {
  for (int whichPort = lowPort; whichPort <= highPort; whichPort++)
    totals [whichPort - lowPort] = 0;
  
  unsigned long startTime = micros ();
  for (int i = 0; i < ITERATIONS; i++)
    {
    for (int whichPort = lowPort; whichPort <= highPort; whichPort++)
       {
       int result = analogRead (whichPort);
       totals [whichPort - lowPort] += result;
       } 
    }
  unsigned long endTime = micros ();

  for (int whichPort = lowPort; whichPort <= highPort; whichPort++)
     {
     Serial.print ("Analog port = ");
     Serial.print (whichPort);
     Serial.print (", average result = ");
     Serial.println (totals [whichPort - lowPort] / ITERATIONS);
     } 
   Serial.print ("Time taken = ");
   Serial.print (endTime - startTime);
    
  Serial.println ();
  Serial.flush ();
  exit (0);
  }  // end of loop


Results were:


Prescaler 2

Analog port = 0, average result = 1023
Analog port = 1, average result = 1023
Analog port = 2, average result = 1023
Analog port = 3, average result = 1022
Time taken = 26220

Prescaler 4

Analog port = 0, average result = 673
Analog port = 1, average result = 718
Analog port = 2, average result = 512
Analog port = 3, average result = 193
Time taken = 32780

Prescaler 8

Analog port = 0, average result = 842
Analog port = 1, average result = 677
Analog port = 2, average result = 509
Analog port = 3, average result = 34
Time taken = 46040

Prescaler 16

Analog port = 0, average result = 1022
Analog port = 1, average result = 672
Analog port = 2, average result = 509
Analog port = 3, average result = 0
Time taken = 73164

Prescaler 32

Analog port = 0, average result = 1022
Analog port = 1, average result = 672
Analog port = 2, average result = 508
Analog port = 3, average result = 0
Time taken = 128040

Prescaler 64

Analog port = 0, average result = 1022
Analog port = 1, average result = 672
Analog port = 2, average result = 508
Analog port = 3, average result = 0
Time taken = 240972

Prescaler 128

Analog port = 0, average result = 1022
Analog port = 1, average result = 672
Analog port = 2, average result = 508
Analog port = 3, average result = 0
Time taken = 448108


(Times in microseconds).

The time taken is a bit longer than the ADC conversion time (multiplied by 4000 (1000 tests of 4 ports)) but that would be the overhead of storing the results in an array, plus the timer interrupt would have fired a few times. A test with analogRead commented out gives a loop overhead of 9052 µs, so to get the true figure we subtract 9052 from the above results, and then divide by 4000 to find the time per reading.

For example, for a prescaler of 128:


 (448108 - 9052) / 4000 = 109.764 µs


Also taking into account that the average conversion time will be 13.5 ADC clock cycles (because of the time taken waiting for the rising edge of the ADC clock), our expected figure is:


 128 * 13.5 * 1/16E6 = 0.000108 (108 µs)


The slightly longer time there (1.764 µs) would be the overhead of the analogRead function starting the conversion, and saving the result).

The interesting thing about the above is that the returned figures are quite wrong (especially for the higher voltages) until we reach a prescaler of 16, after which it seems pretty stable. So a prescaler of 16 would appear to give very acceptable results, in a somewhat faster time (73 mS compared to 448 mS for 4000 conversions).

Reference voltage


You can choose three different reference voltages:


  • AVCC: analogReference (DEFAULT);
  • Internal 1.1V reference: analogReference (INTERNAL);
  • External provided at AREF pin: analogReference (EXTERNAL);


In all cases Atmel advises putting a capacitor between AREF and ground to smooth out the voltage on that pin. The datasheet doesn't seem to say, but probably 0.1 µF would do the job.

Note that once you do an analogRead if you use either of the internal voltages references (5V or 1.1V) it is connected to the AREF pin internally. You should not connect any other voltage sources to that pin or it will be shorted to the internal reference.

For example, to use the internal 1.1V reference:


void setup ()
{
  analogReference (INTERNAL);
  analogRead (A0);  // force voltage reference to be turned on
}
void loop () { }


The reference voltage is not connected until the first analogRead.

Alternatively you can turn it on instantly:


void setup ()
{
  ADMUX = bit (REFS0) | bit (REFS1);  // Internal 1.1V reference
}
void loop () { }


After running either of those sketches you should find around 1.1V present on the AREF pin (I measured 1.096V).

The external voltage reference option can be used to connect a known, accurate voltage reference. This can improve measurement because the internal reference voltage (AVCC) might change due to loading (such as turning LEDs on and so on). Once again, if you connect an external reference voltage, make sure that you execute:


 analogReference (EXTERNAL);


... before doing an analogRead().

External reference voltage ranges

The datasheet (Section 28.8: ADC characteristics) states that the external reference voltage must be in the range 1V to VCC. Thus if you have a supply voltage of 5V (as on a Uno) then AREF must not exceed 5V.

An example of a voltage reference you could use is this one ( Precision LM4040 Voltage Reference Breakout - 2.048V and 4.096V ) from Adafruit.



If you connect that to AREF then you free yourself from the worry of wondering exactly what the reference voltage is. Is it 4.9V? 5V? 5.1V?

Also see below for a discussion about the TL431 voltage reference IC.

Read without blocking


The library function analogRead "blocks", that is, it waits until the conversion is done. However you don't have to do that. This simple code below starts a conversion, and then checks in the main loop if it is finished. When it is finished it displays the result:


const byte adcPin = 0;  // A0
  
bool working;
  
void setup ()
  {
  Serial.begin (115200);
  Serial.println ();

  ADCSRA =  bit (ADEN);   // turn ADC on
  ADCSRA |= bit (ADPS0) |  bit (ADPS1) | bit (ADPS2);  // Prescaler of 128
  ADMUX =   bit (REFS0) | (adcPin & 0x07);  // AVcc   
  }  // end of setup

void loop () 
  { 
  if (!working)
    {
    bitSet (ADCSRA, ADSC);  // start a conversion
    working = true;
    }
    
  // the ADC clears the bit when done
  if (bit_is_clear(ADCSRA, ADSC))
    {
    int value = ADC;  // read result
    working = false;
    Serial.println (value);
    delay (500);  
    }
    
  }  // end of loop  


That lets you do other things (perhaps send a previous value to a PC) while taking another reading.

Read with an interrupt when done


This example code (from http://www.gammon.com.au/interrupts) shows reading asynchronously (non-blocking) and using the ADC complete interrupt to fetch the results.


const byte adcPin = 0;
volatile int adcReading;
volatile boolean adcDone;
boolean adcStarted;

void setup ()
{
  Serial.begin (115200);
  ADCSRA =  bit (ADEN);                      // turn ADC on
  ADCSRA |= bit (ADPS0) |  bit (ADPS1) | bit (ADPS2);  // Prescaler of 128
  ADMUX  =  bit (REFS0) | (adcPin & 0x07);    // AVcc and select input port

}  // end of setup

// ADC complete ISR
ISR (ADC_vect)
  {
  adcReading = ADC;
  adcDone = true;  
  }  // end of ADC_vect
  
void loop ()
{
  // if last reading finished, process it
  if (adcDone)
    {
    adcStarted = false;

    // do something with the reading, for example, print it
    Serial.println (adcReading);
    delay (500);

    adcDone = false;
    }
    
  // if we aren't taking a reading, start a new one
  if (!adcStarted)
    {
    adcStarted = true;
    // start the conversion
    ADCSRA |= bit (ADSC) | bit (ADIE);
    }    
  
  // do other stuff here

}  // end of loop


ADC multiplexer inputs


The ADC can only do one conversion at time but has a multiplexer that lets it choose from different inputs. On a Uno those inputs are A0 to A5, and on some boards (such as the Arduino Mini) you also can choose A6 and A7.

These are just presented as the low-order bits to the ADMUX register (A0 is 0, A1 is 1 and so on) like this:


  ADMUX = bit (REFS0) | (adcPin & 7);   // Use AVcc for conversion



Tip:

The Arduino IDE defines A0 as 14 on the Uno, so that you can do a digitalWrite to A0 and not have it be confused with digital pin 0. When using analogRead the code subtracts 14 so that A0 becomes 0.

If you are using the suggested code here write "0" for analog input zero, not "A0".


Temperature sensor

Multiplexer input 0x08 is connected as an internal temperature sensor. The datasheet advises you to use that with the 1.1V reference voltage, like this:


  ADMUX = bit (REFS0) | bit (REFS1)  | 0x08;    // temperature sensor


The datasheet suggest that a voltage thus obtained would be around 314 mV for a temperature of 25°C, however I personally found that the reading was somewhat higher than that. I got a reading of 337 which is equivalent to 362 mV.


 337.5 * 1.1 / 1024 * 1000 = 362.5  mV


Still, they do suggest that each chip is different and that you should calibrate your own chip by measuring empirically and storing a suitable subtraction factor in EEPROM.

Example code that gave a reasonable result:


void setup ()
{
  Serial.begin (115200);
  ADCSRA =  bit (ADEN);   // turn ADC on
  ADCSRA |= bit (ADPS0) |  bit (ADPS1) | bit (ADPS2);  // Prescaler of 128
  ADMUX = bit (REFS0) | bit (REFS1)  | 0x08;    // temperature sensor
  
  delay (20);  // let it stabilize
  
  bitSet (ADCSRA, ADSC);  // start a conversion  
  while (bit_is_set(ADCSRA, ADSC))
    { }
  
  int value = ADC;
  Serial.print ("Temperature = ");
  Serial.println (value - 320);  
}  // end of setup

void loop () { }


Internal voltage sensor

Another multiplexer input is the internal bandgap voltage.

This means that we are using the 1.1V reference as an input to the ADC. Since this will have a known value (1.1V) then if we compare that to AVCC then the lower that AVCC is, the higher a reading will be returned.

For example:


1.1 / 5 * 1024 = 225
1.1 / 3.3 * 1024 = 341

In general:

1.1 / voltage * 1024.0 = reading

thus:

voltage = 1.1 / reading * 1024.0


Example code:


const float InternalReferenceVoltage = 1.096; // as measured

void setup ()
{
  Serial.begin (115200);
  ADCSRA =  bit (ADEN);   // turn ADC on
  ADCSRA |= bit (ADPS0) |  bit (ADPS1) | bit (ADPS2);  // Prescaler of 128
  ADMUX = bit (REFS0) | bit (MUX3) | bit (MUX2) | bit (MUX1);
  
  delay (10);  // let it stabilize
  
  bitSet (ADCSRA, ADSC);  // start a conversion  
  while (bit_is_set(ADCSRA, ADSC))
    { }
  
  float results = InternalReferenceVoltage / float (ADC + 0.5) * 1024.0; 
  Serial.print ("Voltage = ");
  Serial.println (results);  
}  // end of setup

void loop () { }


For greater accuracy measure the actual 1.1V reference voltage (there is a sketch further up this page that will do that) and use that instead of hard-coding 1.1V.

The usefulness of this is that code can self-detect its own supply voltage. In particular, if you are connected to a battery, you could work out when the battery is reaching the end of its life, by using the above code.


Tip:

According to the datasheet:

Quote:

When the bandgap reference voltage is used as input to the ADC, it will take a certain time for the voltage to stabilize. If not stabilized, the first value read after the first conversion may be wrong.


Thus, if you are attempting to find AVCC using this technique, take two readings, ignoring the first one while the voltage stabilizes.



Measuring resistance


Quite a few sensors output a resistance, for example some temperature sensors, light-dependent resistors (LDRs), pressure-sensitive mats, and so on. To convert a resistance to a voltage we need a voltage-divider, like this:



The LDR resistance decreases as light increases, so the voltage divider there will have a higher voltage, the brighter the light is.

Measuring higher voltages


Sometimes you might want to check the voltage on a 12V or similar battery. You can't just connect that into the analog input or you will damage your Arduino. A voltage divider like this will help drop the voltage down:



In that case the analog pin would read 4V if the battery was 8V.


10k / (10k + 10k) * 8 = 4V


That circuit is OK for an 8V battery, but for a 12V battery the top 10k resistor would need to be bigger (say, 22k).

In that case we would get 3.75 on the analog pin if the battery was 12V.


10k / (10k + 22k) * 12 = 3.75V

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Reply #1 on Fri 27 Feb 2015 08:25 AM (UTC)

Amended on Sat 28 Feb 2015 02:56 AM (UTC) by Nick Gammon

Message
How to interpret the results


So how do we convert the result from analogRead to a voltage?

Let's suppose:


int value = analogRead (A0);  


And imagine we get the value of 1000.

Now (ignoring the fact that integers don't have decimal places) is the voltage (assuming we have a 5V reference):


 1000.0 / 1024 * 5.0 = 4.8828125

or:

 1000.0 / 1023 * 5.0 = 4.8875855


In other words, divide by 1023 or 1024? I should point out that dividing by 1024 immediately gives a "wrong" answer for measuring 5V:


 1023.0 / 1024 * 5.0 = 4.9951171


This is because the maximum reading is 1023.

Browsing the web:




http://arduino.cc/en/Tutorial/ReadAnalogVoltage

Quote:

To scale the numbers between 0.0 and 5.0, divide 5.0 by 1023.0 and multiply that by sensorValue


One vote for 1023.




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

Multiple schools of thought, including:

Quote:

You divide by 1024 if you are a mathematician or if you truly understand ADC successive approximation.





http://blog.codeblack.nl/post/NetDuino-Getting-Started-with-ADC.aspx

Hedging his bets:

Quote:

The number 0 represents 0V and 1023 represents Aref. The voltage level at any ADC port can be determined as follows:

float analogValue = (float)digitalValue * (3.3f/ 1023f)


In theory, you should divide by 1024, because there are that many steps.





http://forums.netduino.com/index.php?/topic/2216-adc-value-divide-by-1024-or-1023/

Different opinions:

Quote:

The ADC returns a value from 0 to 1023 not 0 to 1024. 0 to 1023 is 1024 different values because zero is a value too. ... That is why you divide by 1023 and not 1024.


Quote:

Not exactly.
Consider the nominal "width" of the ADC reading: it is 1024. That is strictly related to Vref (typically 3.3V).
Now we must slice that segment in many smaller parts: in total are 1024 "tiles" (if you like numbered from 0 to 1023).





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

Quote:

The divisor (1023.0) in that tutorial is wrong. The correct value is 1024.0...

float voltage= sensorValue * (5.0 / 1024.0);


The maximum value returned by the analog-to-digital converter (1023) is correct.





http://www.electro-tech-online.com/threads/why-adc-1024-is-correct-and-adc-1023-is-just-plain-wrong.132570/

Quote:

The correct scaling math; *x/n or *x/1024 correctly scales all the output data in size, but gives an average rounding error of 0.5 ADC counts (always rounding down).





So, how come we get a "wrong" result for a value of 1023?

It seems that the ADC can return 1024 "slots" of values (from 0 to 1023) where each slot represents 1/1024 of the reference voltage.

So for a 5V reference voltage the slot width is:


 5 / 1024 = 0.0048828125V


Thus a result of 0 could be: 0V to 0.00488V
A result of 1 could be: 0.00488V to 0.009766V
...
A result of 1023 could be: 4.99511V to 5V

So really the "wrong" result for 5V is more that the voltage is not necessarily 5V, it could be 4.995.

In addition to that, the hardware rounds down the result, so you could compensate by adding in an average error of 0.5.


 1023.5 / 1024 * 5.0 = 4.99756V


So this is reasonably close to 5V. Similarly a reading of 0 should probably be treated as possibly halfway between 0 and 1, thus it could on average be:


 0.5 / 1024 * 5.0 = 0.002441V





Oh yes, and the Atmega328 datasheet mentions in the section "23.7 ADC Conversion Result" that the figure is 1024.

So how do we interpret the results, then?


First, let's go back to the datasheet:

Quote:

0x000 represents analog ground, and 0x3FF represents the selected reference voltage minus one LSB.


So (contrary to what you might expect) 0x3FF (ie. 1023) does not claim to represent Vref, but rather the reference voltage minus one bit (which in the case of 5V is 5/1024 = 4.88 mV).

Thus, the fact that dividing 1023/1024 and multiplying by 5 does not give 5 is entirely consistent with the fact that it does not claim to represent Vref.

The datasheet goes on (on the previous page):

Quote:

Quantization Error: Due to the quantization of the input voltage into a finite number of codes, a range of input voltages (1 LSB wide) will code to the same value. Always ±0.5 LSB.


So there is an expected error of +/- 2.44 mV.


Therefore I suggest the formula:


float voltage = ((float) rawADC  + 0.5 ) / 1024.0 * Vref;


Automatic mode


You can get the ADC to trigger automatically, so it collects samples when some external event arrives, for example, a timer interrupt. This code demonstrates that:


const byte adcPin = 0;  // A0

const int MAX_RESULTS = 256;

volatile int results [MAX_RESULTS];
volatile int resultNumber;

// ADC complete ISR
ISR (ADC_vect)
  {
  if (resultNumber >= MAX_RESULTS)
    ADCSRA = 0;  // turn off ADC
  else
    results [resultNumber++] = ADC;
  }  // end of ADC_vect
  
EMPTY_INTERRUPT (TIMER1_COMPB_vect);
 
void setup ()
  {
  Serial.begin (115200);
  Serial.println ();
  
  // reset Timer 1
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  TCCR1B = bit (CS11) | bit (WGM12);  // CTC, prescaler of 8
  TIMSK1 = bit (OCIE1B);  // WTF?
  OCR1A = 39;    
  OCR1B = 39;   // 20 uS - sampling frequency 50 kHz

  ADCSRA =  bit (ADEN) | bit (ADIE) | bit (ADIF);   // turn ADC on, want interrupt on completion
  ADCSRA |= bit (ADPS2);  // Prescaler of 16
  ADMUX = bit (REFS0) | (adcPin & 7);
  ADCSRB = bit (ADTS0) | bit (ADTS2);  // Timer/Counter1 Compare Match B
  ADCSRA |= bit (ADATE);   // turn on automatic triggering

  // wait for buffer to fill
  while (resultNumber < MAX_RESULTS)
    { }
    
  for (int i = 0; i < MAX_RESULTS; i++)
    Serial.println (results [i]);

  }  // end of setup

void loop () { }


Timer 1 is generating interrupts at 50 kHz:


 1 / (62.5e-9 * 8 * 40) = 50000 Hz


I am feeding a sine wave at 4 kHz into A0, with these results:


378
710
899
992
966
827
609
367
159
38
35
147
350
593
817
965
1001
917
733
494
259
88
21
76
239
471
711
905
1001
977
839
620
376
164
39


It doesn't look exciting, until you graph it:



Now a square wave:


993
1003
1005
1006
1007
1008
1008
1008
1008
24
21
20
19
19
19
19
19
993
996
996
996
996
995
995
994
35
23
19
19
20
19
19
19
18
995




Or a sawtooth wave:


859
790
671
555
438
319
199
83
76
193
312
430
547
664
782
899
984
867
749
631
513
396
279
162
44
112
228
345
463
579
697
816
934
947
831




This shows how you can use the ADC as a nice little (low frequency) oscilloscope.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Reply #2 on Fri 27 Feb 2015 08:19 PM (UTC)

Amended on Sat 28 Feb 2015 03:11 AM (UTC) by Nick Gammon

Message
Sleep during reading


Another useful technique is to use the SLEEP_MODE_ADC (ADC sleep mode) while taking a reading. This lowers the processor's activity, reducing noise in the ADC circuitry.

It also reduces power consumption which can be handy if you have a battery-powered device and want to keep the power consumed, while you are reading the ADC, to a lower level.


#include <avr/sleep.h>    // Sleep Modes

// when ADC completed, take an interrupt 
EMPTY_INTERRUPT (ADC_vect);
  
// Take an ADC reading in sleep mode (ADC)
float getReading (const byte port)
  {
  ADCSRA = bit (ADEN) | bit (ADIF);  // enable ADC, turn off any pending interrupt
  ADCSRA |= bit (ADPS0) | bit (ADPS1) | bit (ADPS2);   // prescaler of 128
  ADMUX = bit (REFS0) | (port & 0x07);  // AVcc   

  noInterrupts ();
  set_sleep_mode (SLEEP_MODE_ADC);    // sleep during sample
  sleep_enable();  
  
  // start the conversion
  ADCSRA |= bit (ADSC) | bit (ADIE);
  interrupts ();
  sleep_cpu ();     
  sleep_disable ();

  // awake again, reading should be done, but better make sure
  // maybe the timer interrupt fired 
  while (bit_is_set (ADCSRA, ADSC))
    { }

  return ADC;
  }  // end of getReading
  
  void setup ()
    {
    int reading = getReading (0);
    
    Serial.begin (115200);
    Serial.println ();
    Serial.println (reading);
    }  // end of setup
  
void loop () { }


Digital input disable


If you are doing only ADC conversions on some or all of the analog inputs, you should disable the digital buffers, to save power, like this:


  bitSet (DIDR0, ADC0D);  // disable digital buffer on A0
  bitSet (DIDR0, ADC1D);  // disable digital buffer on A1
  bitSet (DIDR0, ADC2D);  // disable digital buffer on A2
  bitSet (DIDR0, ADC3D);  // disable digital buffer on A3
  bitSet (DIDR0, ADC4D);  // disable digital buffer on A4
  bitSet (DIDR0, ADC5D);  // disable digital buffer on A5


Once disabled, a digitalRead on those pins will always read zero.

Alternatives


You can get dedicated ADC chips (for example: MCP3201/2/4/8 with from 1 to 8 channels, and MCP3304) that interface by SPI. These can run alongside the Arduino, converting much faster than the inbuilt ADC, and have higher resolutions (eg. 12 to 14 bits).

Analog comparator


If you just want to test a single threshold (eg. are the lights on or off?) then the analog comparator may do the job and is much faster. Details here: http://www.gammon.com.au/forum/?id=11916

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Reply #3 on Sat 28 Feb 2015 11:38 PM (UTC)

Amended on Sun 01 Mar 2015 09:53 AM (UTC) by Nick Gammon

Message
TL431 voltage reference


Whilst working on the above thread, I started exploring using the TL431 voltage reference IC.

It is, apparently, very commonly used. For example it is described in this blog: Reverse-engineering the TL431: the most common chip you've never heard of.

It is widely used as a voltage reference in power supplies, computers, LED drivers and so on.

You can get 50 of them from eBay for $US 1.60.

I found the information in the datasheet a bit hard to follow, so here are my notes on using it.

Wire it up the right way!


It is easy to get the anode and cathode confused. Some datasheets list the pin order from the top, others from the bottom. Also the anode (positive side) goes to ground.





Make a 2.5V reference




The TL431 has an inbuilt "bandgap" reference, which provides a stable reference voltage.

Wikipedia: Bandgap voltage reference

The chip is designed to sink current from Cathode to Anode until the voltage at the reference pin is the reference voltage (2.5V). Thus, if VREF is higher than 2.5V the chip conducts, sinking current, until VREF is back at 2.5V.

The simple use then, is to make a 2.5V reference, as in the schematic above.

But, what value to use for R3? (I call it R3 because another couple of resistors, to be explained shortly, are called R1 and R2 in the datasheet).

(I'm genuinely interested to know why, in a datasheet, they label two resistors R1 and R2, but leave the third one unlabelled. Is it the "unimportant resistor"?).


We know these things:


  • The voltage across R3 must be 2.5V (in this case) because I have a 5V supply.
  • Assuming the current drawn by my load (Output) is negligible, the chip must sink enough current to cause that voltage drop over R3.
  • There is a minimum amount of current needed for the voltage reference to work. From the datasheet:



    That suggests that a minimum of 0.4 mA is needed, but 1 mA is the "maximum minimum".

  • There is a maximum amount of current that it will sink. From the datasheet:



    Thus we don't want to sink more than 100 mA.

  • The recommended operating conditions are to sink from 1 to 100 mA.



Given that, let's work out what resistance we need for this particular voltage drop using Ohm's Law:


R = V / I

R = 2.5 / 0.001

R = 2.5k


The nearest standard value is 2.2k, which gives us this current consumption:


I = V / R

I = 2.5 / 2200

I = 0.001136  (that is 1.136 mA)


Thus for this circuit, a 2.2k resistor lets us operate within parameters. Wiring it up confirms that it works as expected.

However, see below for choosing a different resistor (1.8k) to allow for a range of input voltages.

Make a 4V reference


The TL431 can provide other reference voltages by using a voltage divider (resistors R1 and R2) at the REF pin. The objective here is that the voltage divider divides down the voltage at the Cathode so that, if the desired voltage is at the Cathode, then 2.5V will be at the REF pin.



This is how we do that ...

We can use a Voltage Divider Calculator to simplify our work.

The datasheet hints that 10k is a reasonable value for R1, so let's start with that.



Hitting "Compute" gives us 16.666k for R2, which is not a particularly easy value to work with.


R2 = R1 * (1 / (Vin / Vout - 1))
R2 = 10000 * (1 / (4 / 2.5 - 1)) 
R2 = = 16666.67


As a guess, let's try 12k for R1 and see where that leads us:


R2 = R1 * (1 / (Vin / Vout - 1))
R2 = 12000 * (1 / (4 / 2.5 - 1))
R2 = 20000


That's better! A 12k resistor is a standard value, and we can make up 20k by putting 2 x 10k in series.

Now we need to calculate R3, such that over 1 mA flows through the device, but less than 100 mA.

First let's work out the current flow through R1 and R2.


R1 + R2 = 12000 + 20000 = 32000
I = V / R
I = 4 / 32000
I = 0.000125  (0.125 mA)


That means our target current through R3 is going to be 1.125 mA or more. (1 mA for the chip and 0.125 mA for R1 and R2 in series).

We now only have a 1V voltage drop over R3, so we need a suitable resistor, such that we get 1.125 mA from a 1V voltage drop.


R = V / I
R = 1 / 0.001125
R3 = 888


Since we want more, rather than less, current, let's go for the next standard resistor size down, namely 560 ohms.


I = V / R
I = 1 / 560
I = 0.001786 (1.786 mA)


This seems to be acceptable. We now have values for R1, R2 and R3.

Measurement confirms that in my test case, the output is 4.017V and the current consumption is 1.763 mA.


Tip:

The measured results seems little high, after all we were aiming for 4.000V and this is a precision voltage reference, right? The answer lies in the datasheet.


VKA = Vref * (1 + R1/R2) + Iref * R1


The (small) amount of current flowing into the REF pin (Iref) influences the final voltage. According to the datasheet it is around 2 µA, so we need to multiply that by R1 (12k), giving this result:


 2.5 * (1 + 12000/20000) + 2E-6 * 12000 = 4.024V


So we are really expecting 4.024V on the cathode, not 4.000V exactly. We didn't get precisely 4.024V either, but (apart from probable slight errors in resistor values) the reference voltage is closer to 2.495V in this particular chip.

Substituting that more accurate figure instead of 2.5V gives us:


 2.495 * (1 + 12000/20000) + 2E-6 * 12000 = 4.016


So the output was very close to the theoretical output.



Different supply voltages


The calculations above assume that the supply (input) voltage is 5V. But what if it varies from 4.5V to 5.5V? We need to ensure that the chip remains in spec.

Let's rework for different input voltage ...

2.5V reference - 4.5V supply



Voltage drop over R3 = 4.5 - 2.5 = 2V
R3 = 2 / 0.001
R3 = 2000  (2k)


2.5V reference - 5.5V supply



Voltage drop over R3 = 5.5 - 2.5 = 3V
R3 = 3 / 0.001
R3 = 3000  (3k)


Summary for 2.5V reference

So a resistor in the range 2k to 3k is required to handle that range of input voltages. Let's use the next lowest standard value: 1.5k.


For 5.5V: I = 3 / 1500 = 2 mA
For 4.5V: I = 2 / 1500 = 1.33 mA


Thus the 1.5k resistor will keep us in the operating range of 1 mA to 100 mA for input voltages of 4.5V to 5.5V.


4V reference - 4.5V supply



Voltage drop over R3 = 4.5 - 4 = 0.5V
R3 = 0.5 / 0.001125
R3 = 444 ohms


4V reference - 5.5V supply



Voltage drop over R3 = 5.5 - 4 = 1.5V
R3 = 1.5 / 0.001125
R3 = 1333  (1.3k)


Summary for 4V reference

So a resistor in the range 444 to 1333 is required to handle that range of input voltages. Let's use the next lowest standard value: 330.


For 5.5V: I = 1.5 / 330 = 4.5 mA
For 4.5V: I = 0.5 / 330 = 1.5 mA


Thus the 330 ohm resistor will keep us in the operating range of 1 mA to 100 mA for input voltages of 4.5V to 5.5V.

Accuracy


The datasheet quotes 30 ppm/°C, which means the output should vary by 30 µV per degree.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Reply #4 on Sun 01 Mar 2015 10:27 PM (UTC)
Message
TL431 current reference


While we are on the subject of references, after watching a video by Martin Lorton I made up a current reference as well:



The theory here is that R2 is a current sense resistor, and you can adjust it to give a desired current sink from the "Current In" pin, according to this formula:


Current = 2.5 / R2


In my case I used 2.2k for R2, so the expected current sink was:


2.5 / 2200 = 0.001136 (1.136 mA)


Tests showed that this was pretty close.

The idea is that the TL431 adjusts the current on the transistor base in such a way that 2.5V is on the reference pin. Since we know the voltage drop (2.5V) and the resistance (2.2k) therefore we know what the current through that resistor must be to achieve that.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Reply #5 on Sun 10 Apr 2016 04:16 AM (UTC)

Amended on Sun 10 Apr 2016 04:19 AM (UTC) by Nick Gammon

Message
How does the ADC work?


The ATmega328P features a 10-bit successive approximation ADC. The ADC is connected to an 8-channel Analog Multiplexer.



First, an input is selected from the multiplexer on the left. This allows any of 8 input channels to be active at one time (but only one). These are: A0 to A5 on the Uno (some boards also have A6 and A7). There are also additional input channels (not shown) which also allows the selection of:


  • The internal temperature sensor
  • Ground
  • Bandgap reference voltage



The input channel (multiplexer output) is connected to a "sample and hold" capacitor, as shown. Once an analog conversion starts the switch is opened after 1.5 ADC cycles (12 µs with the default prescaler of 128) so that the capacitor retains its current reading (ie. it holds it) so that the input voltage can now change without affecting the result. This would be useful for fast-changing signals, as the whole conversion can take 104 µs (depending on the prescaler).

To work out the voltage the logic applies the analog reference voltage (ARef) divided down by a DAC (digital-to-analog converter) under the control of the ADC logic. The output of the DAC is sent to the comparator which determines if the input voltage is higher or lower than the reference voltage, at its current divided state. For example, the high-order bit would be set or cleared by setting the DAC to divide the reference voltage by 2 (ie. 2.5V if ARef is 5V). If the input is higher than 2.5V then the high-order bit in the result is set. This takes one ADC cycle (8 µs at the default prescaler of 128 with a 16 MHz processor clock).

To get the next result bit the DAC would be set to the next step (ie. 0.25 of ARef or 0.75 of ARef depending on whether the first bit was set or not) and another comparison done. This process is repeated for all 10 bits.

Sample-and-hold capacitor timing


The time taken to charge the sample-and-hold capacitor will be affected by two things:


  • The frequency of the input signal
  • The impedance of the signal


A capacitor is reckoned as having fully charged (or fully discharged depending on which way you are going) in 5τ (where τ is R*C), so you could therefore calculate that for a given resistor and capacitor it will take:


5 * R * C


In fact it will charge to 99.33% in that time, where 99.33% comes from:


1 - e-5 = 0.9933


The input impedance is given in the datasheet as being from 1 to 100k (depending on the frequency), and they recommend a source signal impedance of 10k. Knowing that, and the value of the 14 pF capacitor, we can see that it will charge in:


5 * 110e3 * 14e-12 * 1e6 = 7.7 µs


This means it should fully charge (or near enough) in the 12 µs that you are allocated from the start of the conversion to when the sample-and-hold capacitor is disconnected from the source.

Conversion timing


As mentioned earlier in this thread, you can speed up ADC conversions by using a smaller prescaler. This reduces the amount of time available to calculate each bit value. The smallest prescaler which gave reasonable results was 16, meaning that each ADC cycle was 1 µs.

Time is required for:


  • The DAC output to settle to the configured fraction of ARef
  • The comparator to compare the DAC output to the sample-and-hold capacitor value
  • Logic to make decisions


To take 1 µs for all of the above sounds reasonable.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


57,863 views.

Postings by administrators only.

[Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at FutureQuest]