Go Down

#15
##### Oct 30, 2012, 02:03 pm
Hi clarkwd,

So I'll try to answer the questions I know before going onto the things I'm more unsure of.  To begin, let's outline the differences between Vin, Vcc, and AVcc.  Looking at this image http://i.imgur.com/Ap12L.png, you can see that vin leads into the ncp1117 voltage regulator, which drops the Vin down to 5V.  So any battery you plug into the barrel jack connector or Vin pin will be fed into the voltage regulator and be converted into 5V.  Looking at page 3 of the atmega328 data sheet (http://www.atmel.com/Images/doc8161.pdf), you can see that Vcc is the digital supply voltage pin for the 328 chip itself. The 5V from the voltage regulator output will be connected to this pin to provide the Arduino with power.  Looking at page for of the atmega328 data sheet, you can also see that AVcc is the supply voltage pin for the analog components of the 328 chips such as the A/D converter.  The data sheet also says that AVcc should be connected to Vcc through a low pass filter.  This allows for more accurate A/D conversions.

Now let's break down retrolefty's code, focusing on the following groups of code:
Code: [Select]
`const long InternalReferenceVoltage = 1115L;  // Adjust this value to your boards specific internal BG voltage x1000`
The internal bandgap voltage should nominally be 1.1V, but is rarely ever perfectly 1.1V.  Therefore you should measure your bandgap voltage (let me know if you need to know how), multiply it by a thousand, and insert it where the "1115" is.  Retrolefty's bandgap must have been 1.115 volts.

Code: [Select]
`     // 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);`
This chunk of code essentially sets up an analogRead() with some custom parameters.  It sets the analog reference to the AVcc pin, and then tells the arduino to read the ADC channel connected to the internal bandgap voltage.  The next few lines in the code start the reading and waits for it to finish.

Unfortunately, I don't exactly understand what is happening in the following line of code where all the magic happens:
Code: [Select]
`int results = (((InternalReferenceVoltage * 1023L) / ADC) + 5L) / 10L; // calculates for straight line value`  However, retrolefty says it computes the voltage at the AVcc pin, which is simply the filtered, regulated five volts discussed above.

Looking at your usb serial monitor output, it seems like you are getting Vcc voltages of 4.74 volts.  Looking at the voltages you pasted in your original post, the USB gives you a Vcc of 4.72V, a dead 9V battery gives you a Vcc of 4.60 volts, and a fresh 9V gives you a VCC of 4.74 volts.  What you may have to do is find a correlation between Vcc an Vin.  Then you can put your cutoff point in the code.  Alternatively, the TMP36 is an analog temperature sensor, correct?  You can run the sensor off of the 5V pin, and correct for the deviation in low power by measuring Vcc.  Let me know if you want to know more about that.

I'm sorry if you knew most of this stuff, it's a bit of a long post.  Hopefully it helps!

#### clarkwd

#16
##### Oct 31, 2012, 04:09 am
Thanks for the help.  I measured the Internal Reference Voltage and made the adjustment.  I've got it set up to measure Vcc with the lefty routine and Vin with a voltage divider and at the same time use the TMP36 to make a temperature measurement.  As soon as Vcc starts to drop then the temperature measurement will go out of control.   As you pointed out, the trick is to find the voltage where the errors start.  Unfortunately, my battery is not dead yet and I need to take a nap.  I'll leave it running, writing to my SD card.
Why does Vin, when connected to the USB cable, measure to be lower than Vcc?
Please send me your thoughts on correcting the measurements as Vcc drops
Bill

#17
##### Oct 31, 2012, 06:24 am
Hi Clarkwd,

What I would do is calculate the temperature from the TMP36 using the Vcc measurement.  For example, with no compensation, if you took an analog reading of the TMP36 of 512, the way you would convert it to volts is like such: 512*5/1023 = 2.502 Volts.  With compensation, you would calculate it like such: 512*VBatt/1023.  As long as you are running the TMP36 off of the 5V pin, you should be able to get an accurate measurement since you've accounted for any voltage variance.  If you don't mind me asking, what is the application of this project?  It sounds like you are working on a weather station, which is what I do most of my experimentation on as well.

#### clarkwd

#18
##### Nov 03, 2012, 03:09 am
It looks like this Vcc calculation is gonna work just fine.   It doesn't mean much until Vcc goes below 5 volts, but then I use it to correct the analog readings for my TMP36 and other analog sensors.  I measured the temperature of a glass of ice water and it measured a pretty constant 32 degrees as the battery went dead.   I added a routine that posts like a low battery on the LCD when Vcc drops below 5 Volts and stops the routine when Vcc gets below 4 volts.  Thanks to Lefty and Coding Badly for the routine and the others for your comments and help. My first project is to log temperatures along the fuel system of an antique car to investigate vapor locking.  Bill

#### retrolefty

#19
##### Nov 03, 2012, 03:44 am

It looks like this Vcc calculation is gonna work just fine.   It doesn't mean much until Vcc goes below 5 volts, but then I use it to correct the analog readings for my TMP36 and other analog sensors.  I measured the temperature of a glass of ice water and it measured a pretty constant 32 degrees as the battery went dead.   I added a routine that posts like a low battery on the LCD when Vcc drops below 5 Volts and stops the routine when Vcc gets below 4 volts.  Thanks to Lefty and Coding Badly for the routine and the others for your comments and help. My first project is to log temperatures along the fuel system of an antique car to investigate vapor locking.  Bill

Very good on getting useful results using the 'proof of concept' CB and I posted long ago, of the advantages of being able to measure the actual voltage (Vcc and Avcc) the AVR is seeing, and using it be able to dynamically compensate (or calibrate if you like) any analogRead values your are using. That was it's intended primary purpose and your effort has shown it indeed can be both useful and effective in maintaining accurate ADC measurements even with a possible changing Vcc voltage bus, as can happen when using battery power.

Lefty

#20
##### Nov 03, 2012, 04:54 am
Clarkwd, glad to see it works well!  Retrolefty, if you get a moment, could you explain what this line does?

Code: [Select]
`int results = (((InternalReferenceVoltage * 1023L) / ADC) + 5L) / 10L; // calculates for straight line value`

It may be obvious but I wasn't able to figure it out.

#21
##### Nov 03, 2012, 08:51 amLast Edit: Dec 31, 2012, 08:54 am by Coding Badly Reason: 1

AREF, IRV (internal reference voltage), and ADC (value from the analog-to-digital converter) are related by a ratio...

[font=Courier New]  AREF / IRV = 1024 / ADC[/font]

Solve for AREF...

[font=Courier New]  AREF = IRV * 1024 / ADC[/font]

The "+5L" rounds fractions up.  The "/10L" converts from millivolts to centivolts (volts * 100) giving three significant digits (which is the most we can expect from a 10 bit converter).  The basic equation is the one above.  The rest is to avoid floating-point math.

Make sense?

Edit: values corrected.

#22
##### Nov 03, 2012, 08:58 am

AREF, IRV (internal reference voltage), and ADC (value from the analog-to-digital converter) are related by a ratio...

[font=Courier New]  AREF / IRV = 1023 / ADC[/font]

Solve for AREF...

[font=Courier New]  AREF = IRV * 1023 / ADC[/font]

The "+5L" rounds fractions up.  The "/10L" converts from millivolts to centivolts (volts * 100) giving three significant digits (which is the most we can expect from a 12 bit converter).  The basic equation is the one above.  The rest is to avoid floating-point math.

Make sense?

Makes perfect sense now, thank you for your help!

#23
##### Nov 03, 2012, 09:26 amLast Edit: Nov 03, 2012, 09:28 am by Coding Badly Reason: 1

Ooh.  That 1023 should be 1024...

[font=Courier New]int results = (((InternalReferenceVoltage * 1024L) / ADC) + 5L) / 10L; // calculates for straight line value[/font]

Dang, @retrolefty.  Why didn't you catch that?  You're supposed to have my back.

#### retrolefty

#24
##### Nov 03, 2012, 09:56 am

Ooh.  That 1023 should be 1024...

[font=Courier New]int results = (((InternalReferenceVoltage * 1024L) / ADC) + 5L) / 10L; // calculates for straight line value[/font]

Dang, @retrolefty.  Why didn't you catch that?  You're supposed to have my back.

That's why I remain a happy hardware guy and do my best programming work in solder while you software types are never done debugging your stuff, always seem to be off by one and can never make up your mind if a given sequences start with zero or one.

Lefty

#25
##### Nov 03, 2012, 10:00 am

Sadly, a quick check of any Atmel datasheet would have revealed my mistake.    I've even corrected others when they made the same mistake.

#### dhenry

#26
##### Nov 03, 2012, 04:05 pm
Quote
I don't exactly understand what is happening in the following line of code where all the magic happens:

The adc module basically works to convert Vref (whatever it is) to 0x3ff (=1023). So when you read an ADC word (0 - 0x3ff), you can convert it to a voltage linearly (DNL aside).

What that piece of code does is to use AVcc as reference and measure a known voltage (in this case, Vbg).

So AVcc -> 0x3ff, and Vbg -> ADC, and the following must be true:

AVcc / 0x3ff = Vbg / ADC. Or AVcc = Vbg * 0x3ff / ADC.

Since we are talking about 10-bit adc, many times people simply left shift Vbg (effectively * 1024); or if you want to be precise, do this:

Vbg * 0x3ff = (Vbg * 1024 - Vbg) and Vbg * 1024 can be done with a left shift.

That's essentially it.

#### dhenry

#27
##### Nov 03, 2012, 04:08 pm
Quote
I thought that I would have to build a voltage regulating circuit

Depending on your needs, you may have to. The BG reference on the chip isn't that great, and the adc module isn't that great either.

Go Up