7

I am using a 5v Arduino pro mini (with regulator and power led removed).

I have been reading how to accurately measure Li-ion batteries when powering from the same battery you are trying to measure from. Using the 1.1V internal analog reference to measure a draining VCC source by using a voltage divider on it, and math to show that converted reading.

Reading the Arduino forums I came across this answer.

 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;
}
void setup() {
 Serial.begin(9600);
}
void loop() {
 Serial.println( readVcc(), DEC );
 delay(1000);
}

However, I cant see a analogRead anywhere in this sketch. (Is this a strictly internal reference?) At the bottom of the thread someone is saying that you can do this without a voltage divider on the analog pin.

Can someone confirm or explain this? I don't want to fry my Arduino...

asked Oct 13, 2020 at 21:46
0

3 Answers 3

5

Using the 1.1V internal analog reference to measure a draining VCC source by using a voltage divider on it

You could indeed use a voltage divider, and measure a scaled-down Vcc against the internal 1.1 V reference. This is, however, not what the code you posted is doing. It is instead measuring the internal reference against Vcc, as stated in the comment within the code. The REFS0 bit selects Vcc as the reference for the ADC. The bits MUX1...MUX3 select the internal reference as the input channel.

result = ADCL;
result |= ADCH<<8;

This idiom dates back from a time when the compiler did not know how to read 16-bit I/O registers: you had to explicitly read one byte at a time and then put these together into a 16-bit word. This time is long gone, and you can now just access the 16-bit register as ADC directly. I would even get rid of the result variable and just:

return 1126400L / ADC; // Vcc in mV

Edit: As suggested by the busybee, here is the explanation of the factor 1126400: The ADC meaures the ratio of its input voltage to its reference voltage, and returns this ratio scaled by a factor 1024. In this case,

ADC = 1024 ×ばつ (Vbg ÷ Vcc)

where Vbg is the voltage of the 1.1 V bandgap reference. Thus,

Vcc = 1024 ×ばつ Vbg ÷ ADC

If we want Vcc in millivolts, we plus the Vbg voltage in millivolts in the above equation and get

Vcc (in mV) = 1024 ×ばつ 1100 ÷ ADC = 1126400 ÷ ADC

answered Oct 14, 2020 at 7:31
6
  • You might add that 1126400 is 1024 * 1100, which is scaling the result, and why. Commented Oct 14, 2020 at 10:37
  • @thebusybee: Thanks for the suggestion. Added. Commented Oct 14, 2020 at 14:21
  • Outstanding answer, as usual. (voted) Commented Oct 14, 2020 at 15:28
  • 1
    wouldn't it be 1.1v x 1023 x 1000? the 1023 being the 10bit total value for the ADC? Commented Oct 14, 2020 at 17:02
  • 2
    @LindsayCox: No, it's 1024, although 1023 is a very widespread error, presumably because it's the highest reading you can get. According to the datasheet, "the maximum value represents the voltage on the AREF pin minus 1 LSB." (emphasis mine) and "ADC = Vin ⋅ 1024 ÷ Vref". The datasheet is the only authoritative source. Note that, given the uncertainty in Vbg, it doesn't really matter. Commented Oct 14, 2020 at 20:42
3

If anyone else is confused by this:

Will it hurt your Arduino?

  • No, this is a internal voltage reference between VCC & the 1.1v internal analog reference.

Is a voltage divider necessary?

  • Not unless you have something external of the Arduino to measure!

Is the internal reference actually 1.1v?

  • No, it seems like each pro mini I have the the 1.1v reference is slightly different. My pro minis do not have a break out for the AREF pin, so you need to figure out what the value is for each individual Arduino.

I used this,

1.15 x 1023 x 1000 = [value in quotation from second to last line in the Function]
return "1125300L" / ADC; 

I just incremented the 1.1v reference by 0.01 a couple of times until the output matched the voltage reading on my digital multi-meter. I would think with a known voltage you could do this math backwards to find what AREF actually is. I am sure that would be easier.

A big thanks to JRobert & @ EdgarBonnet for your answers!

answered Oct 14, 2020 at 17:20
2

Instead of calling analogRead(), this sketch performs the equivalent actions by directly manipulating the hardware registers to begin a conversion, wait until the conversion is complete, and collect the converted value.

Just reading the final value is accomplished by the statements:

result = ADCL;
result |= ADCH<<8;

All of the statements following delay(2) up to and including the above 2, taken together, do what analogRead() does.

answered Oct 13, 2020 at 23:12
2
  • 1
    Re "All of the statements following delay(2)": Note that analogRead() does set ADMUX. Commented Oct 14, 2020 at 7:36
  • 1
    Yup - I missed that. Thanks, @EdgarBonet. Commented Oct 14, 2020 at 13:32

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.