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.
2 Answers 2
... 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– ryeager06/02/2014 17:34:17Commented Jun 2, 2014 at 17:34
-
1The ADC impedance is only 100Mohm. Consider measuring the internal reference instead.Ignacio Vazquez-Abrams– Ignacio Vazquez-Abrams06/02/2014 23:33:14Commented Jun 2, 2014 at 23:33
-
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.