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 AV CC 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
|