The Atmel/Microchip AT series of microprocessors has the capability of measuring processor Vcc using an internal reference voltage (colloquially known as the “secret voltmeter” on line), but the ATmega32u4 data sheet is a bit confusing on the topic.
There is a nominal 2.56V ADC reference (Vref), and also a nominal 1.1V band gap reference, but the 2.56V is derived from the 1.1V reference using an internal amplifier. The reference voltages are stable, but not calibrated, Vref can be anywhere between 2.4-2.8V, according to the data sheet.
The ADC can be internally connected to either reference, but in different ways.
The following Arduino code runs on my AStar Mini ULV and when plugged into the USB port, reads Vcc correctly as 5046 mV, after calibration against a high quality digital voltmeter. It uses the nominal 1.1V bandgap, which can actually range from about 1.0 to 1.2V.
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
while (!Serial);
Serial.print("AStar Vcc is ");
Serial.print(readVcc());
Serial.println(" mV");
}
long readVcc() {
// measure Vcc using the internal 1.1V reference
// 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
// read it a second time, as recommended
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;
//calibration constant, different for each processor
result = 1125380L / result; //1.099*1024*1000
return result; // Vcc in millivolts
}
void loop() {}