8

I am looking to possibly monitor battery power to the Arduino using its ADC. This is fairly straight forward and simple (especially if using the Arduino API); however, if the battery is powering the Arduino and is unregulated externally, won't the ADC reference voltage constantly be dropping with the battery? In other words, wouldn't the ADC value would constantly read the same value (the max value) even though the battery would actually be decreasing in voltage?

If this is the case, it would be both inefficient and pointless to measure the battery voltage.

4

... won't the ADC reference voltage constantly be dropping with the battery?

Yes, which is why you either use or measure an internal bandgap reference instead.

Use the analogReference() function to select a reference appropriate for the board in use. Note that you will need to use a voltage divider to reduce the battery voltage to a value below that of the selected reference if you wish to measure it.

To measure the bandgap voltage instead (using AVCC as a reference and working "backwards") you will need to set MUX[3:0] in ADMUX to 0b1110 and then perform an ADC reading directly (set ADSC in ADCSRA and wait until it resets, then read from ADC[H:L]).

As always, see the MCU datasheet for details.

  • Great information, now I think I can proceed on my own. I was hoping there was some sort of internal reference that could be used. As you suggested, I will need to voltage divide the power voltage down below AVccref, however, I am looking to maximize battery life in all possible aspects so I was going to use a large equivalent resistance for the divider to ensure I have less than a uA of leaking current. Do you see any issues with this? – ryeager Jun 2 '14 at 17:34
  • 1
    The ADC impedance is only 100Mohm. Consider measuring the internal reference instead. – Ignacio Vazquez-Abrams Jun 2 '14 at 23:33
  • 1
    FYI I found this awesome blog after the answer @Ignacio posted: link – ryeager Jun 8 '14 at 21:36
1

@ryeager's link to http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/#comment-71836 has this code for reading the Arduino's battery voltage:

long readVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = _BV(MUX3) | _BV(MUX2);
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif  

  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring

  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both

  long result = (high<<8) | low;

  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
  return result; // Vcc in millivolts
}

The trick here is that it measures its internal reference of 1.1V using the battery voltage, and then inverts it to calculate the unknown reference voltage.

The ADMUX magic in this code can enable other interesting ADC readings, such as differential measurements, and differential ADC measurements with gain, depending on the component and datasheet.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.