Playing with Arduino
A page to record my playing with Arduino
analogRead()

Abstract

The analogRead() reads the value from an analog pin using analog-digital(AD) converter.

The Arduino Uno has 6 10-bit AD converters. The analogRead() uses 4 registers named ADMUX, ADCSRA, ADCL and ADCH.

ADMUX

The ADMUX(ADC Multiplexer Selection Register) controls the reference voltage, the presentation of the ADC conversion(left adjust or right adjust) and analog channel selection.

ADMUX
Bit76543210
NameREFS1REFS0ADLAR-MUX3MUX2MUX1MUX0

The REFS0 and REFS1 set the reference voltage. The meanings of the combination of these bits are shown in the next table.

REFS1REFS0MeaningsArgument of analogReference()
00Voltage applied to AREF pin.EXTERNAL(0)
01Default referece voltage(5V in case of Arduino Uno).DEFAULT(1)
10Reserved.-
11Internal reference voltage(1.1V in case of Arduino Uno)INTERNAL(3)

The ADLAR determines the presentation of the ADC conversion result. If it is 1, the ADC conversion result is left adjusted. If 0, right adjusted. It is set to 0, right adjusted, by the Arduino software.

MUX0 to MUX3 selects the analog pin.

If MUX3…MUX0 is 1000, it reads the value from an internal temperature sensor. But the ability to read internal temperature sensor is disabled in Arduino software.

MUX3MUX2MUX1MUX0Analog Pin
00000
00011
00102
00113
01004
01015
01106

ADCSRA

The ADCSRA(ADC Control and Status Register A) is a register to control AD conversion.

ADCSRA
Bit76543210
NameADENADCSADATEADIFADIEADPS2ADPS1ADPS0

Setting the ADEN to 1 enables the AD conversion. The init(), which is called from the main(), sets the bit.

Setting theADSC(ADC Start Conversion) to 1, the chip begins AD conversion. While AD conversion is executed, this bit is 1. After finishing the conversion, it becomes 0.

The ADATE(ADC Auto Trigger Enable) controls automatic trigger of AD conversion. The bit is not used by the Arduino software.

The ADIF(ADC Interrupt Flag) and ADIE(ADC Interrupt Enable) control the interruption. The bits are not used by the Arduino software.

The ADPS are the bits to determine the division factor between the system clock frequency and the input clock to the AD converter.

ADPS2ADPS1ADPS0Division Factor
0002
0012
0104
0118
10016
10132
11064
111128

To get 10bit precision, clock from 50kHz to 200kHz must be supplied. If you need less precision, you can supply higher clock. The Arduino software sets the ADPS to 0b111, that is, the division factor is 128. As Arduino Uno uses 16MHz system clock, the clock to the AD convert is 125kHz(=16MHz/128). It requires 13 clock for conversion, it takes 0.000104(=1/125kHz*13) seconds or 104 micro seconds to execute AD conversion.

ADCH and ADCL

The ADCH and ADCL are the registers to store the result of the AD conversion. The ADLAR bit of the ADMUX register controls how the result is stored. The Arduino software sets the ADLAR to 0, the first 2 bits of the conversion result is stored in ADXH and the remaining 8 bits are stored in the ADXL.

Source Code

The analogRead() is defined in hardware/arduino/avr/cores/arduino/wiring_analog.c as below. Only the source code for Arduino UNO is quoted. The original source code supports many tips using #if’s.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int analogRead(uint8_t pin)
{
    uint8_t low, high;
 
    if (pin >= 14) pin -= 14; // allow for channel or pin numbers
 
    // set the analog reference (high two bits of ADMUX) and select the
    // channel (low 4 bits).  this also sets ADLAR (left-adjust result)
    // to 0 (the default).
    ADMUX = (analog_reference << 6) | (pin & 0x07);
 
    // without a delay, we seem to read from the wrong channel
    //delay(1);
 
    // start the conversion
    sbi(ADCSRA, ADSC);
 
    // ADSC is cleared when the conversion finishes
    while (bit_is_set(ADCSRA, ADSC));
 
    // we have to read ADCL first; doing so locks both ADCL
    // and ADCH until ADCH is read.  reading ADCL second would
    // cause the results of each conversion to be discarded,
    // as ADCL and ADCH would be locked when it completed.
    low  = ADCL;
    high = ADCH;
 
    // combine the two bytes
    return (high << 8) | low;
}

The input argument is pin and its type is uint8_t. It returns int, the result of the AD conversion which is 10 bits.

First, pin number is converted.

5
    if (pin >= 14) pin -= 14; // allow for channel or pin numbers

The analog pin is represented by number, for example 0, or symbol, for example A0. In Arduino Uno the A0 is defined as 14, A1 is 15 and so on. See hardware/arduino/variants/standard/pins_arduino.h. So if the pin is higher than 14, the function considers the user used the symbol. Analog pins can be used as digital pins, the A0 is defined as 14.

Next ADMUX register is set.

10
    ADMUX = (analog_reference << 6) | (pin & 0x07);

The analog_reference, which is set by analogReference(), is set to REFS1 and REFS0 of the ADMUX. The logical OR of analog_reference «6 and logical AND of pin and 0x07 is set to ADMUX. The ADLAR is not set explicitly, it is 0.

This is why we can not use internal temperature sensor with Arduino Uno. To use the internal temperature sensor, we need to set the MUX3…MUX0 to 0b1000, but this calculation forces the MUX3 to 0.

Now AD conversion is started.

15
16
    // start the conversion
    sbi(ADCSRA, ADSC);

The sbi() is a macro to set the bit(the second argument) of the address(the first argument) to 1. Setting the ADSC bit of ADCSRA to 1 begins the AD conversion.

Wait for the conversion to finish.

18
19
    // ADSC is cleared when the conversion finishes
    while (bit_is_set(ADCSRA, ADSC));

The bit_is_set() is a macro that checks if the second argument bit of the first argument is 1. If the conversion finishes, the ADSC bit of ADCSRA register becomes 0.

Reading ADCL and ADCH, where the result of the conversion is stored, and connect them to make 16bit value.

21
22
23
24
25
26
27
28
29
    // we have to read ADCL first; doing so locks both ADCL
    // and ADCH until ADCH is read.  reading ADCL second would
    // cause the results of each conversion to be discarded,
    // as ADCL and ADCH would be locked when it completed.
    low  = ADCL;
    high = ADCH;
 
    // combine the two bytes
    return (high << 8) | low;

Once the ADCL is read, the value of the ADCH would not be changed unless the ADCH is read. So the ADCL must be read before reading the ADCH.

Version

Arduino 1.8.13

Last Update

June 19, 2020

inserted by FC2 system