Normally things would be easier if you have a voltage regulator and you can know that your reference voltage is fixed.

Assuming I have a 10-bit A/D, and counts are in au (arbitrary units), we can map from a count to a voltage (think y = m*x + b). Take the example of getting a reading of 620au from an ADC register with a reference provided by a 3.3V regulator:

V_sample(counts) = (V_ref - 0)V / (1023 - 0)au * counts au
= 3.3V / 1023au * 620au
= 2V


Assume a system powered by a single-cell lipo (V_nominal = 3.7V, charges to 4.2V).

Now instead of an external voltage reference, all I have is an internal fixed voltage reference of 2.048V.

How does the math work out to cancel out the negative effect of my battery voltage (and A/D high reference) slowly dropping over time?

This has to be a common thing for low-cost projects, no?

So Vadc = 1V => code 500

Unless I've misunderstood your question, the battery does not matter (within reason) because the reference Voltage is stable at 2.048V

• I should clarify it. A/D VREF+ = V_bat, and then I have another voltage I generate internally at 2.048V. 2.048V is NOT the reference to the A/D module – tarabyte Apr 30 '14 at 17:07
• Ahh. I thought that was too easy! Have you considered calibrating the battery voltage ADC reference by sampling the 2.048V reference and calculating the battery voltage from that. – akellyirl Apr 30 '14 at 19:20

Counts linearly map to a voltage:

V_sample(V_bat, counts) = ((V_bat - 0)V / (1023 - 0)au) * counts


Unfortunately, this also depends on the battery voltage. If only there were a way to cancel that out...

What if at (roughly) the same moment we sample our voltage, we also sample our internal reference?

When we sample the internal fixed voltage reference of 2.048V:

V_fvr = (V_bat / 1023) * fvr_counts

We can rewrite this as:
fvr_counts(V_bat) = V_fvr / (V_bat / 1023)
V_bat = V_fvr / fvr_counts * 1023


When we sample the voltage of interest:

V_sample = (V_bat / 1023) * sample_counts

Substitute out V_bat, from our V_fvr equation:
V_sample = ((V_fvr / fvr_counts * 1023) / 1023) * sample_counts
= (2.048 V / fvr_counts au) * sample_counts au