Go Down

### Topic: Internal Voltage Ref (Read 2215 times)previous topic - next topic

#### JHawk88

##### Jun 19, 2013, 06:07 pmLast Edit: Jun 19, 2013, 09:58 pm by JHawk88 Reason: 1
Hello,
I ran an internal voltage ref sketch using the following on my MEGA ADK.  I put a 10uF cap between AREF and GND.

Code: [Select]
analogReference( INTERNAL1V1 )

When I measure the voltage of AREF, using a Fluke 179, I read 1.074 volts.  But from here, I'm not entirely certain how to apply this.  The 5V supply is measured at 5.064 V.

Code: [Select]
analogReference( INTERNAL2V56 )

This provides an AREF voltage of 2.506V and the 5V supply still remains the same at 5.064V.  With no analogRef code, the AREF is the same as the supply.  This is where I got into the Mux

Code: [Select]
ADMUX = (0<<REFS1) | (0<<REFS0) | (0<<ADLAR) | (1<<MUX3) | (1<<MUX2) | (1<<MUX1) | (0<<MUX0);
}

void loop( void )
{
int value;

// Start a conversion

// Wait for it to complete

// Scale the value
value = (((InternalReferenceVoltage * 1023L) / ADC) + 5L) / 10L;

Serial.println( value );
delay( 1000 );
}

My ATD readings of the 5V supply were still pretty off at this point.  I notice that there's a .5V addition to make up for what I believe was the LSB jitter.  I believe I am still pretty much .5V off.  Can anyone maybe explain a step I am missing or a potential solution to get my readings more accurate without simply adding more in the "value"?
-------------------ARDUINO-------------------
A Radical Developing Unit In Northern Oklahoma

#### johnwasser

#1
##### Jun 20, 2013, 03:14 am
What is it that you are trying to do?

Note that the "1.1V" reference of the UNO is only about 10% accurate (1.0V to 1.2V is the expected range).  I wouldn't expect the MEGA references to be any more accurate.
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

#2
##### Jun 20, 2013, 04:56 am
I put a 10uF cap between AREF and GND.

Certainly you mean 0.1uF capacitor.

#3
##### Jun 20, 2013, 04:58 am
Code: [Select]
value = (((InternalReferenceVoltage * 1023L) / ADC) + 5L) / 10L;

The 1023L is wrong.  It should be 1024L.  (Sorry about that.)  (It won't make much difference, though.)

#4
##### Jun 20, 2013, 04:59 am

InternalReferenceVoltage?  What value is it?

#### JHawk88

#5
##### Jun 20, 2013, 03:40 pm
Quote
Certainly you mean 0.1uF capacitor.

I changed to .1uF CAP.  Didn't make any difference as I expected.

Quote
The 1023L is wrong.  It should be 1024L.  (Sorry about that.)  (It won't make much difference, though.)

I believe 1023 is correct.  0-1023 is 1024 bytes yes?

Quote
Note that the "1.1V" reference of the UNO is only about 10% accurate (1.0V to 1.2V is the expected range).  I wouldn't expect the MEGA references to be any more accurate.

It's not.  My int ref for Mega reads 1.074 V.  So still w/in the same accuracy range.

Quote
What is it that you are trying to do?

I am trying to read in a sinusoidal wave in one of the analog inputs.  Normally, my equation for this would be something like:
Code: [Select]
float voltage = sensorValue * (5.07 / 1023.0);
But the issue here is that the 5.07 is the measured Voltage of the supply.  This value will not of course be the same from board to board and I can't guarantee it will be constant 5.07.  So, in order to provide accuracy for my ATD, I wish to poll the internal ref every once in a while (which of course is also going to need to be measured).

-------------------ARDUINO-------------------
A Radical Developing Unit In Northern Oklahoma

#### JHawk88

#6
##### Jun 20, 2013, 04:04 pm
Thank CodingBadly and retrolefty for the posts they provided a couple of years ago found here: http://forum.arduino.cc/index.php/topic,38119.0.html

Quote
InternalReferenceVoltage?  What value is it?

I am entering a value of 1074 as a result of the 1.074 V measured when 1.1V ref.
-------------------ARDUINO-------------------
A Radical Developing Unit In Northern Oklahoma

#### Sembazuru

#7
##### Jun 20, 2013, 05:27 pm
http://www.catb.org/jargon/html/I/I-didn-t-change-anything-.html

#### JHawk88

#8
##### Jun 20, 2013, 05:43 pm
Quote
But those analogReads() do take a boatload of clock cycles so that might be the major limiting factor for sample rate even with floating point math...

I have actually been thinking this same thing and was curious if it was possible to see the code behind the AnalogRead() in the libraries?  Digging through the files, the library doesn't appear to be visible.  But my thought here is, say I wanted to read from 5 analog ports and the port reading is interrupt triggered.  The datasheet for the mega says that the conversion time is 13-260us at a speed up to 15kSPS at Max Res.  I am starting to wonder if it would just be best to use an external ref(some sort of precision ref - diode/res) and just use that for scaling...which would then require an AnalogRead() channel.  From here I would just make my own "AnalogRead()" function that would speed up the data read due to caching.  I feel like I am going in circles now.

Quote
This may be a stupid question, but do you have spare analog pins to have your code read your references?

This is actually what sparked my questions from the beginning.  I did all the mux and internal ref and found that my results from the 5V supply were being read as 499 (4.99V) when the actual was 5.07V - more than the +/- 2 LSB suggested in the datasheet.  I am beginning to really think that an external ref is the answer at this point.
-------------------ARDUINO-------------------
A Radical Developing Unit In Northern Oklahoma

#### retrolefty

#9
##### Jun 20, 2013, 06:01 pm
Quote
Digging through the files, the library doesn't appear to be visible.

It's one of the arduino core files called wiring_analog.c

Code: [Select]

/*
Part of Arduino - http://www.arduino.cc/

Copyright (c) 2005-2006 David A. Mellis

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA  02111-1307  USA

Modified 28 September 2010 by Mark Sproul

\$Id: wiring.c 248 2007-02-03 15:36:30Z mellis \$
*/

#include "wiring_private.h"
#include "pins_arduino.h"

uint8_t analog_reference = DEFAULT;

void analogReference(uint8_t mode)
{
// can't actually set the register here because the default setting
// will connect AVCC and the AREF pin, which would cause a short if
// there's something connected to AREF.
analog_reference = mode;
}

{
uint8_t low, high;

#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
if (pin >= 54) pin -= 54; // allow for channel or pin numbers
#elif defined(__AVR_ATmega32U4__)
if (pin >= 18) pin -= 18; // allow for channel or pin numbers
#elif defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644P__)
if (pin >= 24) pin -= 24; // allow for channel or pin numbers
#elif defined(analogPinToChannel) && (defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__))
pin = analogPinToChannel(pin);
#else
if (pin >= 14) pin -= 14; // allow for channel or pin numbers
#endif

#if defined(__AVR_ATmega32U4__)
pin = analogPinToChannel(pin);
ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
// the MUX5 bit of ADCSRB selects whether we're reading from channels
// 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#endif

// set the analog reference (high two bits of ADMUX) and select the
// to 0 (the default).
ADMUX = (analog_reference << 6) | (pin & 0x07);
#endif

// without a delay, we seem to read from the wrong channel
//delay(1);

// start the conversion

// ADSC is cleared when the conversion finishes

// cause the results of each conversion to be discarded,
#else
// we dont have an ADC, return 0
low  = 0;
high = 0;
#endif

// combine the two bytes
return (high << 8) | low;
}

// Right now, PWM output only works on the pins with
// hardware support.  These are defined in the appropriate
// pins_*.c file.  For the rest of the pins, we default
// to digital output.
void analogWrite(uint8_t pin, int val)
{
// We need to make sure the PWM output is enabled for those pins
// that support it, as we turn it off when digitally reading or
// writing with them.  Also, make sure the pin is in output mode
// for consistenty with Wiring, which doesn't require a pinMode
// call for the analog output pins.
pinMode(pin, OUTPUT);
if (val == 0)
{
digitalWrite(pin, LOW);
}
else if (val == 255)
{
digitalWrite(pin, HIGH);
}
else
{
switch(digitalPinToTimer(pin))
{
// XXX fix needed for atmega8
#if defined(TCCR0) && defined(COM00) && !defined(__AVR_ATmega8__)
case TIMER0A:
// connect pwm to pin on timer 0
sbi(TCCR0, COM00);
OCR0 = val; // set pwm duty
break;
#endif

#if defined(TCCR0A) && defined(COM0A1)
case TIMER0A:
// connect pwm to pin on timer 0, channel A
sbi(TCCR0A, COM0A1);
OCR0A = val; // set pwm duty
break;
#endif

#if defined(TCCR0A) && defined(COM0B1)
case TIMER0B:
// connect pwm to pin on timer 0, channel B
sbi(TCCR0A, COM0B1);
OCR0B = val; // set pwm duty
break;
#endif

#if defined(TCCR1A) && defined(COM1A1)
case TIMER1A:
// connect pwm to pin on timer 1, channel A
sbi(TCCR1A, COM1A1);
OCR1A = val; // set pwm duty
break;
#endif

#if defined(TCCR1A) && defined(COM1B1)
case TIMER1B:
// connect pwm to pin on timer 1, channel B
sbi(TCCR1A, COM1B1);
OCR1B = val; // set pwm duty
break;
#endif

#if defined(TCCR2) && defined(COM21)
case TIMER2:
// connect pwm to pin on timer 2
sbi(TCCR2, COM21);
OCR2 = val; // set pwm duty
break;
#endif

#if defined(TCCR2A) && defined(COM2A1)
case TIMER2A:
// connect pwm to pin on timer 2, channel A
sbi(TCCR2A, COM2A1);
OCR2A = val; // set pwm duty
break;
#endif

#if defined(TCCR2A) && defined(COM2B1)
case TIMER2B:
// connect pwm to pin on timer 2, channel B
sbi(TCCR2A, COM2B1);
OCR2B = val; // set pwm duty
break;
#endif

#if defined(TCCR3A) && defined(COM3A1)
case TIMER3A:
// connect pwm to pin on timer 3, channel A
sbi(TCCR3A, COM3A1);
OCR3A = val; // set pwm duty
break;
#endif

#if defined(TCCR3A) && defined(COM3B1)
case TIMER3B:
// connect pwm to pin on timer 3, channel B
sbi(TCCR3A, COM3B1);
OCR3B = val; // set pwm duty
break;
#endif

#if defined(TCCR3A) && defined(COM3C1)
case TIMER3C:
// connect pwm to pin on timer 3, channel C
sbi(TCCR3A, COM3C1);
OCR3C = val; // set pwm duty
break;
#endif

#if defined(TCCR4A)
case TIMER4A:
//connect pwm to pin on timer 4, channel A
sbi(TCCR4A, COM4A1);
#if defined(COM4A0) // only used on 32U4
cbi(TCCR4A, COM4A0);
#endif
OCR4A = val; // set pwm duty
break;
#endif

#if defined(TCCR4A) && defined(COM4B1)
case TIMER4B:
// connect pwm to pin on timer 4, channel B
sbi(TCCR4A, COM4B1);
OCR4B = val; // set pwm duty
break;
#endif

#if defined(TCCR4A) && defined(COM4C1)
case TIMER4C:
// connect pwm to pin on timer 4, channel C
sbi(TCCR4A, COM4C1);
OCR4C = val; // set pwm duty
break;
#endif

#if defined(TCCR4C) && defined(COM4D1)
case TIMER4D:
// connect pwm to pin on timer 4, channel D
sbi(TCCR4C, COM4D1);
#if defined(COM4D0) // only used on 32U4
cbi(TCCR4C, COM4D0);
#endif
OCR4D = val; // set pwm duty
break;
#endif

#if defined(TCCR5A) && defined(COM5A1)
case TIMER5A:
// connect pwm to pin on timer 5, channel A
sbi(TCCR5A, COM5A1);
OCR5A = val; // set pwm duty
break;
#endif

#if defined(TCCR5A) && defined(COM5B1)
case TIMER5B:
// connect pwm to pin on timer 5, channel B
sbi(TCCR5A, COM5B1);
OCR5B = val; // set pwm duty
break;
#endif

#if defined(TCCR5A) && defined(COM5C1)
case TIMER5C:
// connect pwm to pin on timer 5, channel C
sbi(TCCR5A, COM5C1);
OCR5C = val; // set pwm duty
break;
#endif

case NOT_ON_TIMER:
default:
if (val < 128) {
digitalWrite(pin, LOW);
} else {
digitalWrite(pin, HIGH);
}
}
}
}

An external voltage reference can be very useful. Also useful are the many external I2C or SPI external ADC chips, many have internal voltage reference, programmable gain, single or differinial inputs and more resolution available in the 12,16,24 bit ADCs. I really like the TI chips that adafruit sells breakout boards for:

Very easy to work with and they even have arduino library support files for them.

Lefty

#10
##### Jun 20, 2013, 09:29 pm
I believe 1023 is correct.  0-1023 is 1024 bytes yes?

ATmega328P datasheet ... 24. Analog-to-Digital Converter ... 24.7 ADC Conversion Result ...

ADC = (VIN * 1024) / VREF

VIN is the internal reference.  We're solving for VREF.
ADC * VREF = VIN * 1024
VREF = (VIN * 1024) / ADC

The correct value is 1024.

#### JHawk88

#11
##### Jun 20, 2013, 11:24 pm
Quote
ATmega328P datasheet ... 24. Analog-to-Digital Converter ... 24.7 ADC Conversion Result ...

ADC = (VIN * 1024) / VREF

VIN is the internal reference.  We're solving for VREF.
ADC * VREF = VIN * 1024
VREF = (VIN * 1024) / ADC

The correct value is 1024.

My apologies Coding Badly, you are absolutely correct.  I can code pretty badly sometimes.
-------------------ARDUINO-------------------
A Radical Developing Unit In Northern Oklahoma

#12
##### Jun 21, 2013, 12:06 am
Quote
I did all the mux and internal ref and found that my results from the 5V supply were being read as 499 (4.99V) when the actual was 5.07V - more than the +/- 2 LSB suggested in the datasheet.

A reading of 499 indicates you are using 1023 instead of 1024 and the ADC reading is +3 too high.  There is definitely something fishy afoot.  With InternalReferenceVoltage = 1074 and Vcc = 5.064 you should get an ADC reading of 217 which comes out to 507 (5.07; 0.12% error).  A 1 LSB error (which is the most I've seen) comes out to 509 or 504 (~0.5% error).

Quote
When I measure the voltage of AREF, using a Fluke 179, I read 1.074 volts.

Measured at the AREF pin?  With the 0.1uF capacitor in place?  With the processor idle?

#### JHawk88

#13
##### Jun 21, 2013, 03:30 pm
Quote
Measured at the AREF pin?  With the 0.1uF capacitor in place?  With the processor idle?

Yes, yes, no.

Quote

Switched to 1024, the values were not much different.  I think I am going to take another approach.
-------------------ARDUINO-------------------
A Radical Developing Unit In Northern Oklahoma

#### retrolefty

#14
##### Jun 22, 2013, 03:33 am

Quote
I did all the mux and internal ref and found that my results from the 5V supply were being read as 499 (4.99V) when the actual was 5.07V - more than the +/- 2 LSB suggested in the datasheet.

A reading of 499 indicates you are using 1023 instead of 1024 and the ADC reading is +3 too high.  There is definitely something fishy afoot.  With InternalReferenceVoltage = 1074 and Vcc = 5.064 you should get an ADC reading of 217 which comes out to 507 (5.07; 0.12% error).  A 1 LSB error (which is the most I've seen) comes out to 509 or 504 (~0.5% error).

Quote
When I measure the voltage of AREF, using a Fluke 179, I read 1.074 volts.

Measured at the AREF pin?  With the 0.1uF capacitor in place?  With the processor idle?

By idle do you mean not performing any analogRead statements? If not I wouldn't have a clue on how one idles a avr chip outside of pulling reset low?

Lefty

Go Up

Please enter a valid email to subscribe