Arduino: Making accurate ADC readings

Post Reply
tong
Site Admin
Posts: 2386
Joined: Fri 01 May 2009 8:55 pm

Arduino: Making accurate ADC readings

Post by tong »

The problem is Vref is not precision 5V. Fortunately, the Arduino 328 and 168 have a built in precision voltage reference of 1.1V. This is used sometimes for precision measurement, although for Arduino it usually makes more sense to measure against Vcc, the positive power rail.

The chip has an internal switch that selects which pin the analogue to digital converter reads. That switch has a few leftover connections, so the chip designers wired them up to useful signals. One of those signals is that 1.1V reference.

So if you measure how large the known 1.1V reference is in comparison to Vcc, you can back-calculate what Vcc is with a little algebra. That is how this works.

Code: Select all

long readVcc() 
{
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2);    // Wait for Vref to settle
  ADCSRA |= _BV(ADSC);    // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = 1126400L / result;    // Back-calculate AVcc in mV
  return result;    // The voltage is returned in millivolts.
}
Ref:
https://web.archive.org/web/20160505044910/https://code.google.com/p/tinkerit/wiki/SecretVoltmeter
http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/
tong
Site Admin
Posts: 2386
Joined: Fri 01 May 2009 8:55 pm

Re: Arduino: Making accurate ADC readings

Post by tong »

So now, using that, your ADC code could now look like this:

Code: Select all

float Vcc = readVcc() / 1000.0;
int ADCValue = analogRead(0);
float Voltage = (ADCValue / 1024.0) * Vcc;
And it will be a whole lot more accurate.
tong
Site Admin
Posts: 2386
Joined: Fri 01 May 2009 8:55 pm

Re: Arduino: Making accurate ADC readings

Post by tong »

SecretThermometer

Accessing the secret thermometer on the Arduino 328.

Code: Select all

long readTemp()
{
  long result;
  // Read temperature sensor against 1.1V reference
  ADMUX = _BV(REFS1) | _BV(REFS0) | _BV(MUX3);
  delay(2);    // Wait for Vref to settle
  ADCSRA |= _BV(ADSC);    // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = (result - 125) * 1075;
  return result;
}
Temperature is returned in milli °C. So 25000 is 25°C.

Ref:
https://web.archive.org/web/20160505044909/https://code.google.com/p/tinkerit/wiki/SecretThermometer
Post Reply