AVRs measure their own VCC, but do it badly.

Go To Last Post
26 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The modern AVRs can measure their own VCC with no external parts, by configuring the ADC to attach AVCC to the "reference" input and attach the internal bandgap to the "input" input. That is the input that is normally attached to the mux. This ability is handy, but the error is considerable. The error varies from one AVR to another and is also a function of the supply voltage. There seems to be something strange happening inside the AVR.

I tested 3 AVRs, varying the supply voltage from around 3 volts to 4 volts. A mega324pv gave an error that varied from -48 mv to - 65 mv. A mega329pv gave an error that varied from +82 mv to +110 mv. A mega649pv gave an error that varied from -46mv to - 70mv. I have attached some graphs. The meandering curves look random, but they are repeatable.

I measured the supply voltage with a calibrated DMM and think it is accurate to within +- 3 millivolts, and it is repeatable. I also used the DMM to measure the actual voltage of the AVR's internal references.

I find the AVRs can measure voltage fairly accurately when used in the normal manner, with the reference voltage connected to the "reference" input and the voltage to be measured connected to the mux input. While running these tests, I also had the AVRs measure the supply voltage this way. I used a voltage divider to reduce the supply voltage to less than the internal reference voltage, and connected this divided voltage to the ADC mux. The voltages measured this way had an error of less than 3 mv, usually less than 2 mv. Of course the measured voltages were a fraction of the actual VCC. To convert these to the VCC voltage, the error would be multiplied by the scale factor. The computed VCC still had less than 10 mv error.

The fourth and last graph, in my second post, shows one such test of measuring VCC with a divider.

The conclusion is, if you want to measure VCC accurately, you need to do it the old fashioned way, with a voltage divider hooked to the ADC mux input.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's the result when I used the AVR to measure VCC the old fashioned way. With the internal reference connected to the AVR's reference input and the supply voltage divided down with a pair of resistors. The divided VCC was connected to an ADC mux input channel.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

The error varies from one AVR to another

I can believe that.

Quote:

is also a function of the supply voltage.

That is a bit harder to handle.

But you have the pictures to prove it. ;)

I'd like to see your code and know what speed your AVR is running at.

50mV over 3V is over 1%--sounds a bit suspicious.

If you put a 'scope on your AVcc is it absolutely rock steady with no ripple? [I'll guess some ripple.] Do multiple readings, averaged, give flatter results? [I'll guess 'yes'.]

Lee

[edit] I've got several apps that use this feature but it is for battery level checking and is not critical--there are more variables so it is coarse anyway. But in one app where we want to use it for "multiple range ADC" we do a one-time cal during the test sequence, with a "known" Vcc, to get the actual bandgap value for that particular chip. Quite painless, actually, once it is figured out.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When I did this with ATMega168 I got results that looked far worse. I ended up doing 16 dummy measurements on the bandgap before using the value (never heard back from atmel on this one):

It appeares to me as if the output impedance of the bandgap reference going to the input mux is really high. When doing a conversion, the result is 90% the expected value, and 10% the previous reading on the ADC.

If you still have the experiment on your desk, try dummy conversions without any gap. They have to be conversions, waiting does not help.

/Kasper

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

KKP wrote:
When I did this with ATMega168 I got results that looked far worse. I ended up doing 16 dummy measurements on the bandgap before using the value (never heard back from atmel on this one):

It appeares to me as if the output impedance of the bandgap reference going to the input mux is really high. When doing a conversion, the result is 90% the expected value, and 10% the previous reading on the ADC.

You are right. :!:

I made exact the same experience.
Following consecutive readings (ATtiny25) after switching the MUX:

    228
    228
    228
    228
    Channel: 1 (external signal near VCC)
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    1014
    Channel: 12 (internal Bandgap)
    774
    350
    264
    229
    229
    229
    229
    228
    228
    228
    228
    228
    228
    228
    228
    228
    228
    228
    228
    228

You can see, that the external input gives always right readings.
But the bandgap gives about 7 wrong readings at first.
It changes slowly from the previous selected input.

Thus I use this workaround:
I reject the first 16 readings and trust the 17th reading.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So if i understand you right, you are reading one of the internal refs connected to the input mux with a avcc reference? much like reading V divider with resistor and diode but all internal right?

Are you writing the MUX before the read? even if it's the same, do a reading that doesnt write to the MUX and see if that fixes it. I was going diferential reads and not changing the mux but writing the same value to the mux every read (pass the mux to the read adc function) and it gave me bogus readings. If i change the mux with my change mux function and then read the adc without writing to the mux at all, i get the right thing. Try this and see if that works.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
u16 get_adc( u8 channel )
{
  ADMUX = channel;                              // VCC = reference, ADLAR = 0
  ADCSRA = 1<<ADIF^1<<ADEN^1<<ADSC^1<<ADPS1^1<<ADPS0;   // 1MHz/8 = 125kHz
  while( (ADCSRA & 1<<ADIF) == 0 );
  return ADCW;
}


u16 measure( u8 channel )
{
  u8 i;

  if( channel == 12 )                   // if Band-Gap
   for( i = 16; i; i-- )
     get_adc( channel );                // reject garbage
  return get_adc( channel );
}

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
[I'd like to see your code and know what speed your AVR is running at.
My AVRs run at 1.843200 MHz. I don't think you want to see my code. I use C++. I build it in a peculiar manner so I can compile and run it on a PC, using Visual Studio. I have code in most modules that allow me to see and control almost everything that goes on in the AVR from my PC. I would insist you promise not to laugh if I show you my code. Do you still want to see it? :)

Quote:

If you put a 'scope on your AVcc is it absolutely rock steady with no ripple? [I'll guess some ripple.]
Send me your scope and I'll scope it out. :)
Quote:

Do multiple readings, averaged, give flatter results? [I'll guess 'yes'.]
You guess wrong. I actually perform the measurement 6 times in a row, and I can see the results on my PC. The measurements vary by one count at the most. Depending on what I'm measuring, the first count can be off more than that, but the rest are either the same or differ by one. If it matters, I use interrupts and ADC noise reduction sleep to make the measurements. As soon as one measurement is done and processed, I start another one, until I've done 6.

Quote:
I've got several apps that use this feature but it is for battery level checking and is not critical--there are more variables so it is coarse anyway. But in one app where we want to use it for "multiple range ADC" we do a one-time cal during the test sequence, with a "known" Vcc, to get the actual bandgap value for that particular chip. Quite painless, actually, once it is figured out.
I'll bet if you check the AVR reading against a voltmeter for different battery voltages, you will be in for a shock. That's precisely how I learned of this AVR "feature". I use LSD NiMH cells in a Sony portable radio. I want to easily tell when the battery needs recharging. Some day I'll have a good battery level indcator,maybe with some LEDs.

But for a quick and dirty solution, I merely stuck a Butterfly inside the radio, and powered it from the radio's battery. When I turn the radio on or off, the Butterfly measures it's supply voltage and beeps it out of it's piezo buzzer. Every few days I would open up the battery compartment and measure the voltage with my DMM. I was surprised to find that one day the Butterfly would report a voltage 40 mv too low, and a few days later it would report a voltage 90 mv too low.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

You can see, that the external input gives always right readings.
But the bandgap gives about 7 wrong readings at first.
It changes slowly from the previous selected input.

Thus I use this workaround:
I reject the first 16 readings and trust the 17th reading.


That is well-known, and makes sense when you think about it. If you change from a higher to lower REF, the reference voltage has to "die down" to the lower value. Since there isn't much draw on it, it will take some time (or some number of small draws) to bring it down.

When you appliy a higher reference voltage, the REF value quickly "charges up".

That's why I asked steve17 for the test program, along with the AVR speed (and that will give the ADC clock).

Quote:

I don't think you want to see my code.

I want to see an ADC program that demonstrates your results. At least the ADC setup, invocation sequence(s), and results handling.

I'll have to remember to run a similar test when I have an appropriate board on the bench. I should be able to do a good recreation using ISP power from STK500 and varying Vcc.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
That is well-known, and makes sense when you think about it. If you change from a higher to lower REF, the reference voltage has to "die down" to the lower value. Since there isn't much draw on it, it will take some time (or some number of small draws) to bring it down.

Sorry, but you are wrong.

Please look on my code.
I change only the ADC-MUX, the refence was always set to VCC (LM7805 regulator) on all measurements.
Also the brownout was enabled, so the Band-Gap was permanently active.

It make also no sense to measure the Band-Gap or inputs near VCC with the Band-Gap as reference.

And if I apply a voltage near GND on channel 1, I have seen, that the reading increase slowly after switching to channel 12 (Band-Gap).

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

danni wrote:
I change only the ADC-MUX, the refence was always set to VCC (LM7805 regulator) on all measurements.
Also the brownout was enabled, so the Band-Gap was permanently active.

It takes some time for one MOSFET to turn off and another to turn on in the mux, the MOSFETs has some rise- and fall time. Maybe you start measuring before the pin-input MOSFET in the mux has turned fully off and the bandgap MOSEFET in the mux has turned fully on? The two MOSFETs in the mux must never be on at the same time or you would short-circuit the pin input voltage with the bandgap voltage. So they need a bit of dead-time to make sure one MOSFET is turned off before the other one is turned on. Similar to the way you got to have dead-time in a MOSFET H-bridge when switching current direction.

And another theory, the bandgap/ADC has to charge/discharge the mux MOEFET capacitance before the voltage is table. The higher impedance the ADC-input and bandgap has, the longer time it will take to discharge the capacitance in the MOSFET mux when switching from a higher/lower voltage than the bandgap voltage. The greater difference between the voltage source you switch from and the bandgap voltage, the longer it will take before the MOSFET mux capacitance is charged/discharged.

Last Edited: Fri. Jun 19, 2009 - 12:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To re-iterate, I get repeatable results. If I make the same test run a second time, the results are usually absolutely identical. So there is no reason to show both runs, because the second graph covers the first one, so all you see is the second graph.

There was one slight exception, where the second run produced two adjacent points that were slightly different from the first run. If you look at the third chart I attached to my original post, you will see it. There are two complete runs plotted there, but the second run obscures the first one except for the two points. Why those two are different, is a good question. Investigating that might shed some light on what is going on.

I modified my ADC software so I can do 18 consecutive conversions instead of the 6 I usually do. Still no difference between conversions. Usually all 18 are identical. Occasionally either the second or third reading differs from all the others by one count.

If I switch inputs just before I do the conversions, the first reading can differ by 1 or 2 counts, but the following 17 conversions produce identical results.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

On the topic of selecting the internal bandgap reference as input:

http://wap.taur.dk/bandgap.png

This is the settling of the bandgap amplifier vs. time, which the datasheet has this to say about: "The internal 1.1V reference is generated from the internal bandgap reference (VBG) through an internal amplifier" and "When the bandgap reference voltage is used as input to the ADC, it will take a certain time for the voltage to stabilize. If not stabilized, the first value read after the first conversion may be wrong."
Now this 'certain time' is hard to find, so I did the experiment that produced the graph. If we assume that this is some internal amplifier that has to wake up and settle, then the reading done previously on the ADC won't matter. From the measurements it is clear that it does matter. It is also clear that a lot more than the first value read is wrong. In the above experiment, VREF is tied hard to VCC, VCC can sink and source in excess of 2A.

Therefore, to read the bandgap, the fastest procedure I know is:
- write admux to select internal zero
- wait 4us
- write admux to select the bandgap
- wait 200us
- perform the conversion

Lee: Please point me a document describing this well known behaviour.

/Kasper

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Lee: Please point me a document describing this well known behaviour.

Oh, my, making me try to think of some good Search keywords, eh? ;)

A very similar thread a year ago:
http://www.avrfreaks.net/index.p...

http://www.avrfreaks.net/index.p...
which referred back to the thread I was thinking of
http://www.avrfreaks.net/index.p...

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

KKP wrote:
On the topic of selecting the internal bandgap reference as input:

http://wap.taur.dk/bandgap.png

Wow :!: :!: :!:

Very nice :!:

You should it post to Atmel, that it must be included on the datasheet.

It should be able to save tons of gray hairs and million cups of coffee.

Especially the dependency from the former measurement was very irritating.
Since it cause, that it seems working sometimes and sometimes not.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

danni wrote:
KKP wrote:
On the topic of selecting the internal bandgap reference as input:

http://wap.taur.dk/bandgap.png

Wow :!: :!: :!:

Very nice :!:

You should it post to Atmel, that it must be included on the datasheet.

It should be able to save tons of gray hairs and million cups of coffee.

Especially the dependency from the former measurement was very irritating.
Since it cause, that it seems working sometimes and sometimes not.

Peter


You soon will get a personal message from the lawyers for the Coffee Farmers Association, Peter. ;-)

/Jesper
http://www.yampp.com
The quick black AVR jumped over the lazy PIC.
What boots up, must come down.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I'll have to remember to run a similar test when I have an appropriate board on the bench. I should be able to do a good recreation using ISP power from STK500 and varying Vcc.

OK, I'm a partial believer.

I had a Mega164P app on the bench and "converted" it to do the BG test.

In the app, as in most of my industrial apps, I do continuous conversions on the used ADC channels in round-robin fashion. Interrupt-driven; every 10ms the latest conversion result is added to a total; every 500ms the average is used in the application.

As the monitor panel for this module shows A/D counts for each channel it seemed a good one to try. I powered it from STK500 to vary the supply voltage from 3V to 5V. AVcc hooked to Vcc and used for Vref. 1uF cap on Aref pin to Gnd. ADC clock rate 57kHz.

Results look "flat":

Avcc	Counts	BGv
5.04	224	1.104
4.56	249	1.110
4.08	280	1.117
3.57	319	1.113
3.08	372	1.120

Yes, there is a bit of wiggle if you graph them, but my AVcc/Vref measurements were using the readout on my 'scope, and some of the channel readings (even with the multiple sample and average) were on the edge of one count or the next. Let's just say that all the results were within 1 A/D count. I'd call that just fine; I don't see any evidence of variation of the BG or readings thereof with a changing supply/reference voltage; the computed values are very close to the nominal 1.1V.

HOWEVER, I am a partial believer as I now accept what others have posted: There does seem to be a lot of "bleed through" when selecting the BG channel. My usual signals have decent drive and I have no problems going from one channel to another round-robin fashion. If I attempt this with the BG "channel", tho, I get nonsensical results till it settles down. It looks like with my setup I need 6 conversions in a row on the BG channel following a 5V/1023 count channel to get the proper number (5V AVcc/Vref):

Conv	A/D
1	865
2	536
3	332
4	246
5	226
6	224

If I had to guess from an uninformed bit-pusher's perspective, it looks like the BG doesn't sink well and can't draw down the S/H cap during sampling. I should try it after a "low" channel conversion (near 0 counts) rather than my "high" channel conversion (1023 counts).

If it really matters I can repeat with a Mega48 sometime.

So I (with my single sample) do not agree about BG varying with supply V, nor do I agree that it can't be measured correctly. However, there sure is a "right" way to measure it, and it ain't by doing a single conversion of the "channel" amidst others.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yet another chapter:

I ran another test with the 5V setup (the second result set above) with the prior channel A/D counts being >>below<< the A/D counts for the BG. While the first conversion was just a hair off (about 2-3 counts below the target 222) it was much better than when coming off a saturated A/D of 1023 counts. So the BG can "source" but not "sink"?

In doing that, I noticed something really interesting and would really add to confusion if one were not aware of the issues. My code for yesterday's runs:

...ADC ISR ...
//	ADMUX = ADC_VREF_TYPE + ad_index;
#if 1
	// Bandgap test
	if (ad_index < 6)
		{
		ADMUX = ADC_VREF_TYPE + 0x1e; // bandgap
		}
	else
		{
		ADMUX = ADC_VREF_TYPE + ad_index;
		}
#endif

// Start the AD conversion
	ADCSRA |= (1 << ADSC);

When I wanted to run the code again today to check on the results when the prior channel had low A/D counts, I forgot to comment out the "production" ADMUX setting and had

	ADMUX = ADC_VREF_TYPE + ad_index;
#if 1
	// Bandgap test
	if (ad_index < 6)
		{
		ADMUX = ADC_VREF_TYPE + 0x1e; // bandgap
		}
	else
		{
		ADMUX = ADC_VREF_TYPE + ad_index;
		}
#endif

// Start the AD conversion
	ADCSRA |= (1 << ADSC);

>>This resulted in all BG readings of ~860 counts--obviously useless. I wouldn't have thought ADMUX selection itself would be that important. Is the S/H cap always being charged by the selected channel? <<

I've used this BG method in a couple production apps in the past. And yes, I tested it. ;) In retrospect given the above, I think that what "saved" me was that this was for battery Vcc monitoring, and there was no other A/D in the app, so when the ADC was periodically (like every 30-60 minutes) enabled I did a bunch of conversions over a few ms to get my reading. As the ADC was dead asleep I expected to discard one (or a few) readings anyway and I was quite satisfied with my results. Hence my skepticism of claims of "useless" or nearly so.

So in summary I'll vote for both sides:

-- Conversions of the BG "channel" can be a valuable tool and give good results ...

-- ... But those good results are only obtained when aware of the above. A short answer would be danni's summary: do a bunch of conversions sitting on the BG channel, toss the results, then do a few and average for the "real" value.

NB: Going back to steve17's original post and the results with variances of a few 10's of mV: Aren't those variances like +/-1 A/D count? One count at 3V is 30mV, right? At 5V it is 50mV. As the ADC specs are much worse than 1LSB I can't really call the wiggles in his graph a dire problem.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

NB: Going back to steve17's original post and the results with variances of a few 10's of mV: Aren't those variances like +/-1 A/D count? One count at 3V is 30mV, right? At 5V it is 50mV. As the ADC specs are much worse than 1LSB I can't really call the wiggles in his graph a dire problem.

your math is off a bit, unless you are only running the ADC at 7 bit resolution.

For 10bit, 1 count at 3V is ~3mV, and at 5V it is ~5mV.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At 5V one count is 5mV. The bandgap is 1.1V. That's 225 counts. since VCC=1024*Vbg/225, stepping 1 lsb to 224/226 means a vcc change of 5V/225=22mV.
At 3V 1 lsb means a vcc change of 3V/375=8mV
At 2.7V 1 lsb means a vcc change of 2.7V/417=6.4mV
At 1.8V 1 lsb means a vcc change of 1.8/626=2.9mV

using the ADC this way around is useful, but quickly loses precision as VCC goes up.

/Kasper

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

your math is off a bit,

Yep.

"Well, I'm off...but only a little...and it hardly shows."

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A bit more data.
On the M168 I'm testing, an analog-in pin has a capacitance of 9.2 pF to the supplies. The capacitance to the next pin is 2.3 pF. I've never measured those before.

Of interest to this thread is the capacitance of the common node of the analog multiplexer. The figure I measure is 1.79pF+/-0.02pF.

Knowing this figure, and the slew rate of the bandgap driving it, the sink current becomes 5V/340us*1.79pF= 26nA.

tau is roughly 50us, giving an output impedance on the order of 28Mohm.

If this hold, the leakage required to cause 1lsb error on the bandgap is 5mV/28Mohm=179 pA. This is on the internal node and does not infer than the leakage on the pins is this low.

Since I know that settling to 1lsb is faster than about 6us on a pin input, tau for the mux itself is on the order of 1us, which in turn means that the on resistance of the mux is less than 500k. Which in turn means it's been made up of tiny little transistors with correspondingly less leakage than the bulky ones found in a HC4053.

/Kasper

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am also trying to measure vcc of microcontroller but I m not getting its result..

below is code that i have used ...

Can any1 suggest modification...?

ADMUX=B01011110; //1.1vbg
delay(2);
ADCSRA|=B11000000;//adc enable and 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
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Problem is shifting high, which is 8 bits, left 8 bits. Are you getting zero?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:

I tested 3 AVRs, varying the supply voltage from around 3 volts to 4 volts. A mega324pv gave an error that varied from -48 mv to - 65 mv. A mega329pv gave an error that varied from +82 mv to +110 mv. A mega649pv gave an error that varied from -46mv to - 70mv.

 

What clock rate are you using for the ADC?

 

From the datasheet for the ATmega324PV - Section 20.5:

By default, the successive approximation circuitry requires an input clock frequency

between 50 kHz and 200 kHz to get maximum resolution.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Praful Prajapati wrote:
but I m not getting its result..

What result >>are<< you getting?  In ADC counts, please.  What value did you expect? 

 

Did you try after casting the shift, as mentioned?

 

Please tell ADC clock rate, as mentioned.

 

Please tell the ADC counts from a series of conversions, as discussed earlier [by some years] in the thread.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.