Go Down

Topic: Read internal voltage referance (Read 9000 times) previous topic - next topic

arduinopi

I have been using this little script for some time now on my MEGA
Code: [Select]
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?

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

Code: [Select]

// 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;

   }




mhectorgato

#2
Jun 21, 2013, 09:22 pm Last Edit: Jun 21, 2013, 09:56 pm by mhectorgato Reason: 1

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

macedo123

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?

Code: [Select]

#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
}

Buzzy

I met with about the same problem. Add this after if-then-else-endif
Code: [Select]
  #if defined(__AVR_ATmega2560__)
    ADCSRB &= ~_BV(MUX5); // Without this the function always returns -1 on the ATmega2560
  #endif

Go Up