12

Questions:

  1. Does the conversion of the ADC count to voltage depend on the actual voltage of the +5 V pin?
  2. If yes, what is the accepted method of getting that voltage from the board?

Background/Detail:

I have a circuit in which I have an Arduino Nano (clone) running from a USB connector (from a hub). The job of the Arduino is to measure the voltage on a battery which will drive a second circuit switched on/off by the Nano. For reference it is a battery tester.

Test circuit

There is a Nokia 5110 screen which displays the voltage from the very simple sketch below.

void setup() {
  Serial.begin(9600);
  display.begin();
  // Init done

  // You can change the contrast around to adapt the display
  // for the best viewing!
  display.setContrast(50);

  // Text display tests
  display.setTextSize(1);
  display.setTextColor(BLACK);
}


void loop() {
  display.clearDisplay();   // Clears the screen and buffer
  display.setCursor(0,0);
  display.print("Vin=");
  int rawVIN = analogRead(VIN);
  float floatVin = (rawVIN*4.75)/1023.0;
  display.println(floatVin);
  Serial.println(rawVIN);
  display.display();
  delay(1000);
}
  • I measured the voltage of the battery using a DVM and it is 4.13 V, yet the Nano reports 4.35 V.
  • I have a common ground between the battery and Arduino.
  • Because the connection to test the voltage can float, I have a pulldown resistor to stop wild fluctuations (>10 kΩ)

After some investigation I found that the +5 V was actually outputting 4.75 V and changed my sketch from

float v = (rawVIN*5.0)/1024.0;

to

float v = (rawVIN*4.75)/1024.0;

and the voltage reading on the Arduino was now correct. I did this not because I understand what I did, I did it because I had a hunch it might change the value to the correct one.

  • 3
    As a side not you should be dividing by 1024, not 1023. This is a mistake which gets repeated over an over again, presumably stemming from the mistake being in the Arduino examples. Source? The datasheet for the AVR. – Tom Carpenter Apr 8 '17 at 17:25
  • @TomCarpenter I think this is a result of my fumbling around with the calculation and reading something after quite a long time of banging my head against the desk - I started with 1024 but as you see ended up with 1023 - I'll correct my question. – Caribou Apr 8 '17 at 22:29
15

The ADC inside the Arduino does not measure voltage, but rather a voltage ratio. Namely the ratio from the voltage at the analog input to the voltage at the Vref pin.

In the default configuration, the Vref pin is internally tied to the +5 V line. You can select to use instead an internal reference as Vref:

analogReference(INTERNAL);

This reference is about 1.1 V, and is is quite immune to fluctuations on the +5 V. The problem is that you cannot measure voltages higher than the reference.

For your battery tester, if you want some sort of “absolute” measurement, you could use the internal reference and a voltage divider to ensure the measured voltage is below 1.1 V.

Edit: Another option which does not require a voltage divider is to use Vcc as a reference to measure both the analog input and the internal 1.1 V “bandgap“ reference. Measuring the 1.1 V against Vcc is an indirect way to measure Vcc. This is not supported by the Arduino core library, but you can do it by programming directly the control registers of the ADC:

// Return the supply voltage in volts.
float read_vcc()
{
    const float V_BAND_GAP = 1.1;     // typical
    ADMUX  = _BV(REFS0)    // ref = Vcc
           | 14;           // channel 14 is the bandgap reference
    ADCSRA |= _BV(ADSC);   // start conversion
    loop_until_bit_is_clear(ADCSRA, ADSC);  // wait until complete
    return V_BAND_GAP * 1024 / ADC;
}

Beware that the very first reading after boot may be bogus.

  • Thanks for the prompt reply :) this makes perfect sense now and explains why by my code (fudge) adjustment I get the right answer. This combined with enrics answer give me all I need to continue. – Caribou Apr 8 '17 at 12:52
9

A USB-powered Arduino Nano will have an ADC voltage reference which can't be relied on, due to the +/- 5% tolerance of the incoming USB voltage. On top of that, the Nano has an MBR0520 Schottky diode (D1) that will drop between 0.1 and 0.5 V depending on its own manufacturing tolerances, its temperature, and the current draw of your board.

What can you do about it?

The MCU on-board the Arduino Nano is an ATmega328P. The Nano's ADC can scale its analog voltage readings according to several available references (and you can choose the one that suits you better). You can do this through the analogReference (type) function, and chose among the following reference types:

  • DEFAULT: the default analog reference of 5 volts (on 5 V Arduino boards) or 3.3 volts (on 3.3 V Arduino boards)
  • INTERNAL: An built-in reference, equal to 1.1 volts on the ATmega168 or ATmega328 and 2.56 volts on the ATmega8 (not available on the Arduino Mega) [...]
  • EXTERNAL: the voltage applied to the AREF pin (0 to 5 V only) is used as the reference.

Source: analogReference

This is the ADC schematic for what's inside the ATmega328 so you can see what's going on there:

ATmega328P ADC block

Source: ATmega328 datasheet

So the straightforward solution is building a weak voltage divider to get the voltage you want to measure below the INTERNAL 1.1 V reference, and then configure analogReference accordingly.

The voltage divider needs to be weak (high R values) so you don't draw too much current out of the battery, but not too weak as to be loaded by the ADC input impedance.

Bonus

However, if you need a voltage reference higher that the internal 1.1 V bandgap reference of the ATmega328 you're still out of luck. A option would be to use the 3.3 V LDO regulator output from the on-board FT232RL, which is available to you at pin 14 of the header, but I don't think it's reliable either. The FT232RL datasheet specifies it at 3.0 - 3.6 V (nominal 3.3 V)

So a universal solution would be to build an external voltage reference based on a cheap TL431. That could give you a reliable reference up to 4.0 - 4.25 V with a +/-1% accuracy.

The external voltage reference circuit would be something as easy as this, (and the TL431 is available in the breadboard friendly TO-92 package!):

TL431-based shunt voltage regulator

  • Thanks for this comprehensive answer and all the alternatives I could take up - I will probably go simple and do a voltage divider to scale my input between 0 and 1.1. The hint about resisteor values was valuable to me - thanks – Caribou Apr 8 '17 at 12:54
3

I have a page about the ADC converter on the Atmega328P. Somewhat down the page I describe voltage references. You can use the TL431 chip to provide assorted reference voltages, for example 4V:

4V voltage reference

By changing the resistors you can get other voltages (it looks like you want around 4.2V).

The output voltage is not influenced by the input voltage (5V in this case).

The linked page describes how you might choose resistor values.

2

Does the conversion of adc count to voltage depend on the actual voltage of the +5v pin?

yes and no: the adc module cares about Vref, which can be supply via Vdd, internally, or externally.

If yes what is the accepted method of getting that voltage from the board?

by configuring the adc module. the device datasheet should have registers / bits that need to be set up for that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.