Pages: 1 [2]   Go Down
Author Topic: Basic ADC/Analog read question  (Read 4904 times)
0 Members and 1 Guest are viewing this topic.
Salt Lake City
Offline Offline
Jr. Member
**
Karma: 1
Posts: 69
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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:
     // 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:
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!
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Thanks again for your help.
Bill
Logged

Salt Lake City
Offline Offline
Jr. Member
**
Karma: 1
Posts: 69
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 5
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Left Coast, CA (USA)
Offline Offline
Brattain Member
*****
Karma: 361
Posts: 17261
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

Salt Lake City
Offline Offline
Jr. Member
**
Karma: 1
Posts: 69
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Clarkwd, glad to see it works well!  Retrolefty, if you get a moment, could you explain what this line does?

Code:
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.
Logged

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 197
Posts: 12743
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


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

  AREF / IRV = 1024 / ADC

Solve for AREF...

  AREF = IRV * 1024 / ADC

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.
« Last Edit: December 31, 2012, 02:54:57 am by Coding Badly » Logged

Salt Lake City
Offline Offline
Jr. Member
**
Karma: 1
Posts: 69
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


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

 AREF / IRV = 1023 / ADC

Solve for AREF...

 AREF = IRV * 1023 / ADC

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!
Logged

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 197
Posts: 12743
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


Ooh.  That 1023 should be 1024...

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

Dang, @retrolefty.  Why didn't you catch that?  You're supposed to have my back.   smiley-wink
« Last Edit: November 03, 2012, 03:28:18 am by Coding Badly » Logged

Left Coast, CA (USA)
Offline Offline
Brattain Member
*****
Karma: 361
Posts: 17261
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Ooh.  That 1023 should be 1024...

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

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


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.  smiley-grin

Lefty
Logged

Global Moderator
Dallas
Online Online
Shannon Member
*****
Karma: 197
Posts: 12743
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


 smiley-grin

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

Offline Offline
Edison Member
*
Karma: 116
Posts: 2205
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Edison Member
*
Karma: 116
Posts: 2205
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Pages: 1 [2]   Go Up
Jump to: