Problems finding Vcc using bandgap voltage method

Following on from this oldish post:

retrolefty:
There is a method to make your A/D measurements independent with variation of the Vcc used as the A/D reference. The trick is to read the bandgap voltage (not use it as the reference) and come up with a correction factor to utilize in mapping your analog input readings.

I have attempted to do that with rather variable results. For example:

Barebones board:

Battery Vcc volts =  412
Analog pin 0 voltage = 111

Uno:

Battery Vcc volts =  480
Analog pin 0 voltage = 134

Ruggeduino:

Battery Vcc volts =  491
Analog pin 0 voltage = 60

The "bare bones" board was supplied direct from 5V supply, checked with a meter to be around 4.99V. The other two were supplied from the USB cable. In those cases I measured around 5.03V on the Vcc pin.

The error amount on the bare bones board in particular seems to be a rather high 18%.

I am trying to document ways of reliably knowing if a battery-powered processor "in the field" can self-detect if its battery level is getting a bit low. Since batteries have rather flat discharge curves I was hoping for a better than 18% error rate, and also hoping not to have to have some calibration needed for each individual device.

Page 262 of the datasheet has some confusing data regarding using the internal voltage reference, specifically it says:

REFS0 and REFS1 set: Internal 1.1V Voltage Reference with external capacitor at AREF pin

What external capacitor? Is one required for this to work reliably?

Can anyone who was worked with this cast some light on it please? Thanks very much.

Have you looked at the Rugged Circuits code for their "Capacitor holdup" circuit, I think that's what they do.

http://ruggedcircuits.com/html/circuit__13.html


Rob

What external capacitor? Is one required for this to work reliably?

Many recommend installing a .1ufd cap between ground and the Aref pin. It can help remove a little noise from the reference voltage. Wouldn't account for the error size you are seeing.

Can anyone who was worked with this cast some light on it please? Thanks very much.

You didn't post your code so I don't know what to say about the error size you are seeing, other then it seems too high to me. My first question is what constant value are you using for the 1.1vdc reference in your sketch? 1.1vdc is a nominal datasheet value ( can vary from 1.0 to 1.2) and every chip will have a certain variation around this value, but still pretty constant. Coding Badly had a method where you could measure the actual band gap value for your specific chip by measurement of the Aref pin with a decent quality DVM, after first selecting it as the reference in a small 'test' sketch. I choose to use a back 'door method' where I powered the chip with a known and accuratly measured Vcc and keep 'tweeking' the bandgap constant value in the sketch until the calculated and displayed value did indeed match my measured Vcc. After that I checked that the 'dynamic calibration' value used to correct analogRead() values were accurate as the Vcc was adjusted downward. I was pleased with the results but sorry I don't recall the actual accuracy value, but I'm pretty sure they were in the 1-2% max error range.

The Bandgap value I ended up with for my specific Seeeduino mega (1280) board was Aref = 1115 millivolts and my Arduino 328p board was Aref = 1056 millivolts. Once I arrived at those specific values I was quite pleased with the accuracy results. I didn't write down the accuracy result at least that I can fine, but possibly I might have mentioned them in the long thread that Coding Badly and I were work with?

So what bandgap value did you use?

Lefty

The sketch from the thread above, namely:

// 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(9600);
     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 * 1023L) / ADC) + 5L) / 10L; // calculates for straight line value 
     return results;
 
    }

The values in the two 'blued' constants are what I wondered what you used. These are specific to my two boards, your boards (chips really) will be different and need to be changed for the actual bandgap voltage your chip(s) have.

#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);

By the way I found the original posting thread where Coding Badly and I first worked on this method. It might give you a better sense of the what, why, and how of this method: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1294478456/all

Lefty

OK well I made up this sketch to find it out:

// Find internal 1.1 reference voltage on AREF pin
void setup ()
{
  ADMUX = _BV (REFS0) | _BV (REFS1);
}
void loop () { }

Measuring the AREF pin I got 1.084. After changing the sketch to read:

const long InternalReferenceVoltage = 1084L;

I got the voltage displayed: 504 (compared to measured 5.03) so that is OK I suppose.

Then on the bare bones board I measured 1.088 on AREF. Adjusting for that, and then re-uploading the main sketch I got 480 (compared to measured 4.995) so that wasn't as far out, anyway (around 4%).

It seems that the bandgap idea is fairly sensitive to getting the reference voltage right. I suppose that makes sense. If you were going to deploy this in the field you probably need to calibrate each unit (perhaps keep the figure in EEPROM).

It seems that the bandgap idea is fairly sensitive to getting the reference voltage right. I suppose that makes sense. If you were going to deploy this in the field you probably need to calibrate each unit (perhaps keep the figure in EEPROM).

Yes EEPROM could hold the band-gap constant value for a given specific chip, it's not like those two thing could become separated. :wink:

I think it kind of depends on the application. If your just interested in a low Vcc alarming function then it's not really all that important that the Vcc value is 'accurate' or not, as long as it's repeatable, linear, and sensitive enough for the function, which it is, as the error is just with the constant value being used, it's not a 'variable error' with any more measurement uncertainty then if the constant value was right on value. So you could just use 1100 millivolts as the constant and just compensate for the 'error' in the setpoint value used to use to test against the reading you get. That is, is the result greater or less then the alarm setpoint value. Also as a low battery alarm a lot depends on the battery chemistry used. Lead acid and li-po have a reasonably linear discharge voltage slope, where as nicads an nimh have a very flat discharge voltage until very near the end of the charge capacity. However if the application is dealing with using the analog input pins for sensor inputs, in the presence of a gradually falling Vcc voltage, then proper calibration is probably desirable and useful.

The main thing is that this demonstrates that there is a method to maintain reasonable accuracy and calibration for reading of the analog input pin even with a variable Vcc/Avcc voltage without requiring any external components.

Right, thanks!

I've modified my posts here about power savings:

Included are the effects of lowering Vcc, detecting low battery, battery self-discharge, and so on.

Nick,

Great information, thank you! After reading several threads about this notion of giving a warning if the battery voltage gets low, I am still curious about something: is the only way of measuring the Vbg (band gap voltage) to run the sketch that sets the ADC to measure AVcc and then use a DVMM (multimeter) to measure the voltage at the AVcc pin, and it is that voltage reading that is then your Vbg? There seemed to be reference to a way of determining Vbg by software (by changing an initial approximated Vbg up or down based on some math calculations). I understand of course that the actual Vbg will vary from chip to chip, so it needs to be measured on each unit. The question I am attempting to get a definitive answer on is whether or not you need to use a DVM to measure the Vbg and that's the only way of determining it. Do I have that right?

Thanks!

Jerry

Measure it once you mean? The rough approximation is probably OK (ie. no measurement), but if you measure it you get a more precise result. I don't know about "the only way". You could probably set up a reference voltage with a special reference voltage chip or zener diode to work it out a different way.

The question I am attempting to get a definitive answer on is whether or not you need to use a DVM to measure the Vbg and that's the only way of determining it.

You may determine Vbg from Vcc, provided you do know the Vcc voltage (ie 3.30V or 5.00V).

Hi Nick,

Do you know how to transpose this (measure input Vcc) to an Arduino Due ? Any pointers or directions ?

Thanks!

No I don't. It's a completely different processor, and one with which I have not played much.

Thanks,

Any idea how I might do this with external hardware ? Maybe connecting the power supply to a voltage regulator (or voltage divider) and then to the Vin ?

The goal would be to just inform the user if the power supply (or energy) is not achieving the required 3.3V.

Thanks again,

If you have a spare analog input pin, then a simple solution for any micro is to just wire a precision voltage reference chip to one input and use that value to map the correct range used for the other analog inputs of the ADC.
One of many possible examples:

Thanks, I do have a spare analog input.

Would you mind giving more details (or other pointers) ? I am total beginner in this area.

Thanks again,

retrolefty:
If you have a spare analog input pin, then a simple solution for any micro is to just wire a precision voltage reference chip to one input and use that value to map the correct range used for the other analog inputs of the ADC.
One of many possible examples:
http://www.analog.com/en/special-linear-functions/voltage-references/ad580/products/product.html

I plan to use this method to protect my LiPo battery pack. Does it make sense to monitor vcc to determine if there is enough power in my 7.4 V battery connected to Vin ? I hope drop in Vcc signals battery undervoltage but I have doubts.

duntax:
I plan to use this method to protect my LiPo battery pack. Does it make sense to monitor vcc to determine if there is enough power in my 7.4 V battery connected to Vin ? I hope drop in Vcc signals battery undervoltage but I have doubts.

No it won't. The Vin voltage goes to a voltage regulator that maintains a steady Vcc voltage even as Vin is dropping over time. A LiP0 two cell battery will have a voltage output from 8.4 vdc at full charge down to 3 6 vdc where most recommend to stop drawing power from it. You are better off using a voltage divider and measuring the battery directly via a analog input pin to decide when to alarm or shutdown.

Just as I guessed. One analog pin lost. :frowning:

And if I have two single cell batteries with a tap between them? Is it enough to measure the voltage at the tap without the divider?