Measure different Vcc using 1.1V bandgap When I power my ATmega328P chip from 5.16V and 3.3V, there doesn't seem to be a single bandgap voltage compensation figure that works for both.

I'm using the following sketch, and I need the following InternalReferenceVoltage values to read the correct voltage from A0:


1255L to correctly output 5.16V.
1358L to correctly output 3.3V.


I have a couple of questions:


Should I be able to use a single InternalReferenceVoltage compensation value whe powering the ATmega328P from different voltages?
Aren't both of these values outside the specced 1.0 to 1.2 range?


The code is from, http://forum.arduino.cc/index.php?topic=56750.msg407897#msg407897, retolefty post @ Mar 28, 2011, 02:27 pm.

#include &lt;SPI.h&gt;

void setup()
{
  delay(5000);

  Serial.begin(115200);
}

void loop(void)
{
  // Determines what actual Vcc is, (X 100), based on known bandgap voltage
  int battVolts = readVcc1();

  Serial.print("Battery Vcc volts =  ");
  Serial.println(battVolts);
  Serial.print("Analog pin 0 voltage = ");
  Serial.println(map(analogRead(0), 0, 1023, 0, battVolts));
  Serial.println();

  delay(1000);
}

// Returns actual value of Vcc (x 100)
int readVcc1()
{
  uint8_t oldADMUX = ADMUX;

  const long InternalReferenceVoltage = 1255L; // Adjust this value to your boards specific internal BG voltage x1000

#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  // For mega boards
  // REFS1 REFS0          --&gt; 0 1, AVcc internal ref. -Selects AVcc reference
  // MUX4 MUX3 MUX2 MUX1 MUX0  --&gt; 11110 1.1V (VBG)         -Selects channel 30, bandgap voltage, to measure
  ADMUX = (0 &lt;&lt; REFS1) | (1 &lt;&lt; REFS0) | (0 &lt;&lt; ADLAR) | (0 &lt;&lt; MUX5) | (1 &lt;&lt; MUX4) | (1 &lt;&lt; MUX3) | (1 &lt;&lt; MUX2) | (1 &lt;&lt; MUX1) | (0 &lt;&lt; MUX0);

#else
  // For 168/328 boards
  // REFS1 REFS0          --&gt; 0 1, AVcc internal ref. -Selects AVcc external reference
  // MUX3 MUX2 MUX1 MUX0  --&gt; 1110 1.1V (VBG)         -Selects channel 14, bandgap voltage, to measure
  ADMUX = (0 &lt;&lt; REFS1) | (1 &lt;&lt; REFS0) | (0 &lt;&lt; ADLAR) | (1 &lt;&lt; MUX3) | (1 &lt;&lt; MUX2) | (1 &lt;&lt; MUX1) | (0 &lt;&lt; MUX0);

#endif
  delay(50); // Let mux settle a little to get a more stable A/D conversion
  // Start a conversion
  ADCSRA |= _BV(ADSC);
  // Wait for it to complete
  while (((ADCSRA &amp; (1 &lt;&lt; ADSC)) != 0));

  // Scale the value
  int results = (((InternalReferenceVoltage * 1023L) / ADC) + 5L) / 10L; // calculates for straight line value

  ADMUX = oldADMUX;

  return results;
}


I have read these other references to try and find a solution:


Measure Vcc using 1.1V bandgap
https://hackingmajenkoblog.wordpress.com/2016/02/01/making-accurate-adc-readings-on-the-arduino/
http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/
The internal reference won't change much. That's why it's a reference.

Running this code on my Atmega328P:

void setup ()
{
  ADMUX = bit (REFS0) | bit (REFS1);  // Internal 1.1V reference
}
void loop () { }


That outputs the reference voltage on Aref. I measured it as follows with different values for Vcc:

Vcc     Aref
------------
5.00   1.085
4.00   1.081
3.30   1.080
3.00   1.079


So you can see that it retains, more or less, the same voltage regardless of Vcc. In any case, there is no way of measuring Aref, unless you have another reference voltage around to compare it to.