Go Down

Topic: of Atmega ADC and supply reference (Read 6 times) previous topic - next topic

Eliminateur

I'm making a project and hit this slight question i'm exhibiting here on the hopes someone could help me guide my train of thought....
(The board i'm using is arduino pro mini), letme start by the beginning:
Taking VDc measurements with the circuit powered by USB, i found that different computers have wildly different variations, on one i saw 4.9, on other 4.85, they're all inside the 5% range of course and can be negligible... but...
That same source is being used as an ADC reference by default, and that 0.15V of difference means 31 codes of difference, now... this wouldn't be an issue if all my circuit where based off the same source as the ratio would be 100%(for example, if i put the ADC input to Vcc, the resulting code will be 1023, since Vcc=Vref, doesn't matters what the absolute value is -somewhat, bear with me here-).
But what if i need the utmost precision?, let's take the above example, if i do any calculations assuming 5Vcc, the result will be off by "0.15V"/31 codes

So my question is, do any of you guys use the internal 1.1Vref to read the real Vcc and then map the ADC reads based on that value?

another option would be to power the board with a precision 5Vref, but those Refs aren't made for power draw, and the board has a lot of devices connected that draw power....

another option is to use a precision/low noise linear reg(i have no idea the line reg of the arduino pro mini, has 150mA output which isn't enough for all the system)?
or split several regs, for power and for the avr itself.... umfortunately the board does not give me full access to the atmega pins(or i could use a precision 0.1% reg ext 5vref on the AREF pin)

robtillaart


What is the range of voltages you want to read?

If it is above the 1.1V internal you can't unless you use a voltage divider.
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Eliminateur

i forgot to mention it in the first post because it was implied that i'll be using a divider to calibrate the Vcc from the bandgap reference.

WillR

Elimi:

You have to look at it as a system.

1) What are you supplying to the sensor.
2) What is your reference.

If you are only considering the built in sensors then presumably the internal reference is the same as the supply to the sensors.

If you are using external sensors, then you have to deal with the sensor as well as the reference in supplying matched voltages -- AREF to External supply -- as measured at the (analog) sensor.

Some senors like the BMA180 are more accurate when supplied with 2.4V -- go figger.

So bottom line --
1) are you only talking about the built in Arduino ADC sensors?; or,
2) Is this a general question? -- including things like an external Analog sensor...

I think there might be different answers depending on what you are doing...

Just another Hacker

retrolefty

#4
Mar 28, 2011, 04:27 pm Last Edit: Mar 28, 2011, 04:46 pm by retrolefty Reason: 1
Quote
So my question is, do any of you guys use the internal 1.1Vref to read the real Vcc and then map the ADC reads based on that value?


Yes!

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. This is especially useful if you are powering the processor directly with batteries. Anyway give it a reading and see if you might get some ideas.

Edit:

PS: Keep in mind that this doesn't solve the electrical problem that an input voltage to a analog input pin must not be above the Vcc +.5vdc specification when Vcc is being used as the normal default reference voltage, because the positive input pin clamping protection diode will start to conduct with possible pin damage resulting. So make sure your analog input signals being read can never exceed the lowest Vcc value you expect your power source to provide.

Code: [Select]
// 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(38400);
    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;

   }



Lefty


Go Up