Read internal voltage referance

I have been using this little script for some time now on my MEGA

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
}

For some reason It returns the voltage correctly the first time it is called and after that it returns -1
does any one have any ideas as to why it would do that and what I can to fix it?

1 Like

Here is sketch that Coding Badly and I worked on a couple of years ago. It's purpose was to read the actually voltage applied to the chips Vcc/Avcc pins by using the band-gap voltage as a known value against the unknown Vcc value. It also comes up with a 'calibration factor' to adjust ADC readings to compensate for Vcc voltage variations. Useful in direct battery powered arduino projects. Seems to work will and perhaps something in it can help you with your sketch?

Lefty

// Function created to obtain chip's actual Vcc voltage value, using internal bandgap reference
// This demonstrates ability to read processors Vcc voltage and the ability to maintain A/D calibration with changing Vcc
// Now works for 168/328 and mega boards.
// Thanks to "Coding Badly" for direct register control for A/D mux
// 1/9/10 "retrolefty"

int battVolts;   // made global for wider avaliblity throughout a sketch if needed, example a low voltage alarm, etc

void setup(void)
    {
     Serial.begin(38400);
     Serial.print("volts X 100");
     Serial.println( "\r\n\r\n" );
     delay(100);
    }
    
void loop(void)
    {
     battVolts=getBandgap();  //Determins what actual Vcc is, (X 100), based on known bandgap voltage
     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);
    }

int getBandgap(void) // Returns actual value of Vcc (x 100)
    {
        
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
     // For mega boards
     const long InternalReferenceVoltage = 1115L;  // Adjust this value to your boards specific internal BG voltage x1000
        // 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);
  
#else
     // For 168/328 boards
     const long InternalReferenceVoltage = 1056L;  // Adjust this value to your boards specific internal BG voltage x1000
        // 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);
       
#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 & (1<<ADSC)) != 0 ) );
        // Scale the value
     int results = (((InternalReferenceVoltage * 1024L) / ADC) + 5L) / 10L; // calculates for straight line value 
     return results;
 
    }

retrolefty:
Here is sketch that Coding Badly and I worked on a couple of years ago. It's purpose was to read the actually voltage applied to the chips Vcc/Avcc pins by using the band-gap voltage as a known value against the unknown Vcc value. It also comes up with a 'calibration factor' to adjust ADC readings to compensate for Vcc voltage variations. Useful in direct battery powered arduino projects. Seems to work will and perhaps something in it can help you with your sketch?

Lefty

Sorry -- new to Arduino world ... but will this work with a Micro (ATmega32u4)?

Thank you

Hi arduinoPI,

Same problem here with my MEGA, if analogRead() is ever performed on PIN A8 or above readVcc() stops working for good. I am clueless, did you find a solution for this problem?

#define DATA A9
#define LED  A10

void setup(){                
  Serial.begin(115200);
}

void loop(){
  float value;  

  // SHARP dust sensor test
  Serial.print("Base Vcc=");        
  Serial.print((float)readVcc()/1000);
  Serial.print(" Volts");  
  
  pinMode(LED,OUTPUT);
  digitalWrite(LED,LOW); // power on the LED
  delayMicroseconds(280);

  value = analogRead(DATA); // read the dust value

  delayMicroseconds(40);
  digitalWrite(LED,HIGH); // turn the LED off

  delayMicroseconds(9680);
  
  Serial.print(", Analog=");    
  Serial.print(value);

  // 0 - 5V mapped to 0 - 1023 integer values recover voltage
  value = value / 1024.0 * 5.0; // Want to use correct Vcc here, but it is returning 0 when using analogRead() on A8 or above

  // linear eqaution taken from http://www.howmuchsnow.com/arduino/airquality/
  value = (0.17 * value) - 0.1; // Dust Density in mg/m3    
  
  Serial.print(", Dust Density=");  
  Serial.print(value);
  Serial.println(" mg/m3");        

  delay(1000);
}

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  

  ADCSRA |= _BV(ADEN);  // Enable the ADC

  delay(20); // Wait for Vref to settle

  ADCSRA |= _BV(ADSC); // Start conversion

  while (bit_is_set(ADCSRA,ADSC)); // Detect end-of-conversion

  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
}

I met with about the same problem. Add this after if-then-else-endif

  #if defined(__AVR_ATmega2560__)
    ADCSRB &= ~_BV(MUX5); // Without this the function always returns -1 on the ATmega2560
  #endif
1 Like