For an battery driven application I want to measure the Vcc (using an Atmega 32u4). That is my code:
int readBandGap() {
ADMUX = (B01 << REFS0) // VCC
| (0 << ADLAR) // right-adjusted result
| (B011110 << MUX0) // 1.1V
;
// switch off any ADC conversion
ADCSRA &= ~(1 << ADEN);
// enable ADC
ADCSRA |= 1 << ADEN;
// start ADC
ADCSRA |= 1 << ADSC;
// wait until finished
while (bit_is_set(ADCSRA, ADSC));
// first read low, then high!
uint8_t low = ADCL;
uint8_t high = ADCH;
// switch off any ADC conversion
ADCSRA &= ~(1 << ADEN);
return (high << 8) | low;
}
void setup() {
Serial.begin(9600);
}
void loop() {
int value = readBandGap();
float vcc = 1.1 * 1023 / value;
Serial.print(value);
Serial.print(" ");
Serial.println(vcc);
delay(1000);
}
My Olimexino 32u4 board has the ability to switch the voltage between 5V and 3.3V. There are two strange things:
the first measurement is always significantly different than the later measurements
with 5V I'm getting 450, followed by ~367; with 3.3V I'm getting 422, followed by ~395. I would have expected a higher Vcc resulting in a lower measured value (bandgap should be measured compared to Vcc).
Any ideas what is wrong with my approach?
2 Answers 2
The bandgap voltage reference needs some time to stabilize, after you enable it. See http://jeelabs.org/2012/05/12/improved-vcc-measurement/
Doing 3 dummy measurements, gives it enough time to do that.
After tweaking a lot, I've now have found following code to work quite good.
float readVcc() {
ADMUX = (B01 << REFS0) // VCC
| (0 << ADLAR) // right-adjusted result
| (B011110 << MUX0) // 1.1V
;
// enable ADC
ADCSRA |= (1 << ADEN);
// seems necessary
delay(2);
// start ADC
ADCSRA |= 1 << ADSC;
// wait until finished
while (bit_is_set(ADCSRA, ADSC));
// first read low, then high!
long value = ADCL;
value |= ADCH << 8;
return 1.1 / value * 1023;
}
readVcc
always reports ~1100, no matter whether I'm using 3.3 or 5V. Maybe for the 32u4 some different flags have to be used.