Low-level ADC control: ADMUX


While discussing the 3 phase sketch implementation here: http://openenergymonitor.org/emon/node/467 by Pcunha which makes use of the AVR analog read commands rather than the arduino analog_read function which is a wrapping for these commands JBecker suggested that the analog reference voltage was not being selected correctly, so I had to do some reading up on this on ADMUX and bitwise operators.

So for anyone interested, this is how it works first ADMUX is an 8-bit byte that holds the settings for the analog reference voltage and the analog pin to select:

ADMUX is 8-bit:

 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
ADMUXREFS1REFS0 ADLAR MUX3 MUX2 MUX1 MUX0 
Where:

 REFS1REFS0 Voltage Reference Selection 
 0AREF, Internal Vref turned off 
 0AVcc with external capacitor on AREF pin 
 1Reserved 
 1Internal 1.1V (ATmega168/328) or  2.56V on (ATmega8)
and:

MUX 3...0Single Ended Input
 0000ADC0 
 0001ADC1 
 0010ADC2 
 0011ADC3 
 0100ADC4 
 0101ADC5





So if we want to use AVcc with external capacitor on AREF pin as our voltage reference (which is the default arduino voltage reference) and we want to select ADC2 for example, we would need to set ADMUX to:

 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit 
ADMUX011

We can use bitwise operations to get there:

This is how its done in wiring_analog.c found in Arduino-1.0/hardware/arduino/cores/arduino

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


Starting with (analog_reference << 6). The variable analog_reference is set to 1 as default for the atmega328 so 1 << 6 means bitshift 1 left 6 places where 1 in binary is 00000001 and shifting the bits left 6 places moves the 1 to 01000000. The desirable result for setting the 6th bit REFS0 to 1.

If you dont need to select different analog_reference values then we can get 01000000 directly by specifying its hexadecimal: 0x40.

Next looking at the  (pin & 0x07) part.

  • The & 0x07 is actually just for validation, it essentially says only allow selection of ADC0 to ADC5. 
  • The & bitwise operator is the AND operation.
  • 0x07 is hexadecimal whose binary representation is 00000111
  • Selecting ADC2 which is pin = 2 or in binary 00000010
  • The result of 2 & 0x07 = 00000010 AND 00000111 = 00000010.

The last part is the | bitwise operator: OR


(analog_reference << 6) | (pin & 0x07) becomes in our example here 01000000 OR 00000010. Which is equall to: 01000010 the result we where looking for above.

If you would like to read more on the AVR ADC commands see these helpful articles:

https://sites.google.com/site/qeewiki/books/avr-guide/analog-input
http://www.protostack.com/blog/2011/02/analogue-to-digital-conversion-on-an-atmega168/

To engage in discussion regarding this post, please post on our Community Forum.