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 <SPI.h>

void setup()


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

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


// 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          --> 0 1, AVcc internal ref. -Selects AVcc reference
  // MUX4 MUX3 MUX2 MUX1 MUX0  --> 11110 1.1V (VBG)         -Selects channel 30, bandgap voltage, to measure
  ADMUX = (0 << REFS1) | (1 << REFS0) | (0 << ADLAR) | (0 << MUX5) | (1 << MUX4) | (1 << MUX3) | (1 << MUX2) | (1 << MUX1) | (0 << MUX0);

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

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

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


  return results;

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

  • I don't get the question. The idea is not to measure the 3.3v or 5v using the internal reference voltage as a reference, but the other way around. You measure the internal reference voltage in relation to Vcc.
    – Gerben
    Apr 24, 2016 at 18:26
  • Thanks. Do you know how I should change the code or the measurements then? I thought measuring Vcc using the internal reference would allow me to calculate how my particular internal reference varies from 1.1V.
    – Paul Grime
    Apr 24, 2016 at 18:47
  • After reading your question one more time, I finally get it. Forget what I said, as it's not related.
    – Gerben
    Apr 25, 2016 at 14:56

1 Answer 1


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.

  • Thanks. So do I measure the voltage on the AREF pin with a multimeter to calibrate an individual chip? I'm sure I read somewhere that the internal voltage reference was never made physically available externally. I'm still not clear on how to fine tune an individual chip to get more accurate voltage readings of the battery powering the chip.
    – Paul Grime
    Apr 25, 2016 at 6:34
  • What I posted above was done by measuring with a multimeter after running that code which puts the internal voltage reference onto the Aref pin. And yes, it varies from chip to chip. As you can see, mine is slightly under 1.1V.
    – Nick Gammon
    Apr 25, 2016 at 9:29
  • Thanks. I measure 1.078V on AREF (using multimeter) following your sketch, but when I use that value as the InternalReferenceVoltage value (from your "Internal Voltage Sensor" code - gammon.com.au/adc) I get 4.35V printed, when my voltage is 5.16V. I measure Vcc continuously in the loop. Confused.
    – Paul Grime
    Apr 25, 2016 at 16:30
  • What raw ADC reading do you get? Mine reads low (4.95V - raw reading: 226) but not that low. See this thread on AVR Freaks - it appears that there are some inconsistencies between theory and practice.
    – Nick Gammon
    Apr 25, 2016 at 23:14
  • The difference between 4.95V and 5V (that I got) is only 1%, so that is probably acceptable for checking input voltage. Your results of 4.35V sounds like some sort of error. I tried a different approach using an external reference voltage (TL431 chip) and got better results. I can amend my answer if you want to see that. However that may have other drawbacks (depending on whether you are using the analog inputs for other purposes as well).
    – Nick Gammon
    Apr 26, 2016 at 0:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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