CALIBADC10 is a very useful PICAxe command which allows the supply rail to be calculated purely by softaware, without the need for any external hardware or PICaxe pins. Normally a voltage resolution to 1 or 2 decimal places (as described here) is sufficient for estimating the condition of a battery, for example. But for more advanced uses, I have devised a family of subroutines which offer higher resolutions, through to "CALIBADC16" (i.e. up to 64 times that of CALIBADC10), still without the need for any external hardware.

Two different techniques are used, which may be employed independently or in unison. The first method employs a higher voltage FVR, normally FVR2048, but FRV4096 might be used occasionally. The second method measures and accumulates multiple ADC values to create a longer (i.e. higher resolution) data word. However, this can be effective only if there is a degree of "noise" or randomness in the values, which is achieved here by taking measurements via the on-chip DAC (Digital to Analogue Converter), set to various different attenuation factors. In principle, any combination of DAC settings could be used with a suitable calibration factor, but these subroutines employ carefully selected levels such that the accumulated total is always an exact "power of two" (i.e. 2, 4, 8, 16, etc.) times greater than the normal CALIBADC10 value).

Occasionally, the "CALIBADC" result may be used directly, but normally it needs to be converted to an actual supply voltage by dividing the CALIBADC value into a "Magic Number". However, unfortunately even "CALIBADC11" needs a higher accuracy division calculation than is available in PICaxe Basic, so here I have used the "Double Word" division subroutine which I devised some time ago.

The "Magic Number" is determnined by the exact voltage of the on-chip FVR and the resolution (number of bits) in the CALIBADC measurement, so in principle it needs to be different for each subroutine. However, to avoid this, the value has been "normalised" for CALIBADC16, with the lower resolution CALIBADC measurements multiplied by the appropriate power of 2 to give the required result. For the code here, the Magic Number has 1024 in the High Byte (i.e. it's 1024 * 65536), so the voltage can be trimmed in 0.1% steps without needing to change the Low Byte away from zero.

Three sample subroutines are shown below: CALIBADC12 should give a modest increase in resolution but is mainly intended to show the principle of operation of the code; CALIBADC13* requires only FVR1024 so can be used with 14 and 20M2s, even at their lowest supply voltages and CALIBADC14 probably gives the best compromise between resolution and execution time. The subroutines use b1 and w1 to w3 explicitly, because I normally reserve these as "local" variables throughout my programs. In general, any word variables may be used, except that the code as shown does require the bit-addressing capability of W1.

I have intentionally kept the explanatory detail here quite brief but plan to start another thread in the Active forum to discuss further options and design details. In subsequent posts, I hope to add some additional subroutines using Conditional Compilation (#IFDEFs), and a "Universal resolution" subroutine which is configured by passing a single entry variable.

*EDIT: At low voltages the CALIBADC13 routine doesn't behave entirely as expected, because there appears to be a "bug" in the PIC silicon hardware (FVR1024-DAC) of all the chips I've tested. I plan to report this and update the thread in due course.

Cheers, Alan.

Two different techniques are used, which may be employed independently or in unison. The first method employs a higher voltage FVR, normally FVR2048, but FRV4096 might be used occasionally. The second method measures and accumulates multiple ADC values to create a longer (i.e. higher resolution) data word. However, this can be effective only if there is a degree of "noise" or randomness in the values, which is achieved here by taking measurements via the on-chip DAC (Digital to Analogue Converter), set to various different attenuation factors. In principle, any combination of DAC settings could be used with a suitable calibration factor, but these subroutines employ carefully selected levels such that the accumulated total is always an exact "power of two" (i.e. 2, 4, 8, 16, etc.) times greater than the normal CALIBADC10 value).

Occasionally, the "CALIBADC" result may be used directly, but normally it needs to be converted to an actual supply voltage by dividing the CALIBADC value into a "Magic Number". However, unfortunately even "CALIBADC11" needs a higher accuracy division calculation than is available in PICaxe Basic, so here I have used the "Double Word" division subroutine which I devised some time ago.

The "Magic Number" is determnined by the exact voltage of the on-chip FVR and the resolution (number of bits) in the CALIBADC measurement, so in principle it needs to be different for each subroutine. However, to avoid this, the value has been "normalised" for CALIBADC16, with the lower resolution CALIBADC measurements multiplied by the appropriate power of 2 to give the required result. For the code here, the Magic Number has 1024 in the High Byte (i.e. it's 1024 * 65536), so the voltage can be trimmed in 0.1% steps without needing to change the Low Byte away from zero.

Three sample subroutines are shown below: CALIBADC12 should give a modest increase in resolution but is mainly intended to show the principle of operation of the code; CALIBADC13* requires only FVR1024 so can be used with 14 and 20M2s, even at their lowest supply voltages and CALIBADC14 probably gives the best compromise between resolution and execution time. The subroutines use b1 and w1 to w3 explicitly, because I normally reserve these as "local" variables throughout my programs. In general, any word variables may be used, except that the code as shown does require the bit-addressing capability of W1.

Code:

```
; Higher Resolution CALIBADC subroutines - AllyCat July 2015
#no_data
symbol CALIBCONST = 1024 ; Adjust this slightly to correct for any minor errors in the FVR voltage
calibadc12: ; Measure the PICaxe supply rail using "12 bits resolution"
fvrsetup FVR2048 ; Nominal 2 volt reference voltage
dacsetup $88 ; Reference chain to FVR
adcconfig 0 ; ADC ref to Vdd
calibadc10 w3 ; Initialise accumulator to the CALIBADC10 value
for b1 = 8 to 24 step 8 ; 1/4, 1/2 & 3/4 of FVR2048 = CALIBADC10 * 3
daclevel b1 ; Set the DAC attenuation value (32 steps to full-scale)
readdac10 w2 ; Read the voltage on the "wiper"
w3 = w3 + w2 ; Accumulate the sum
next b1
w1 = 0 ; Clear low byte of numerator (quotient)
w2 = CALIBCONST ; High byte of numerator, normalised to CALIBADC16
w3 = w3 * 16 ; Compensate for the 16-bit normalised calibration factor
call div31 ; Divide w2:w1 by w3
return ; Result is in w1, CALIBADC12 value still in w3
calibadc13: ; Measure the supply rail using "13 bits resolution"
fvrsetup FVR1024 ; Nominal 1 volt reference voltage
dacsetup $88 ; Reference chain to FVR
adcconfig 0 ; ADC ref to Vdd
calibadc10 w3 ; Initialise the accumulator
for b1 = 3 to 29 step 2 ; 7 pairs of daclevels (eg 5 + 27) each totalling 32
daclevel b1 ; Set the DAC attenuation value (32 steps to full-scale)
readdac10 w2 ; Read voltage on "wiper"
w3 = w3 + w2 ; Accumulate the sum
next b1
w1 = 0 ; Clear low byte of numerator
w2 = CALIBCONST ; High byte of numerator, normaised to CALIBADC16
w3 = w3 * 8 ; Compensate for 16 bit normalised calibration factor
goto div31 ; Divide w2:w1 by w3 and use the subroutine's "return"
calibadc14: ; Measure supply rail using "14 bits resolution"
fvrsetup FVR2048 ; Nominal 2 volt reference voltage
dacsetup $88 ; Reference chain to FVR
adcconfig 0 ; ADC ref to Vdd
calibadc10 w3 ; Initialise accumulator
for b1 = 2 to 30 step 2 ; 7 pairs of daclevels each totalling 32 + middle value (=CALIBADC10)
daclevel b1 ; Set the DAC attenuation value (32 steps to full-scale)
readdac10 w2 ; Read voltage on "wiper"
w3 = w3 + w2 ; Accumulate the sum
next b1
w1 = 0 ; Clear low byte of numerator
w2 = CALIBCONST ; High byte of numerator, normaised to CALIBADC16
w3 = w3 * 4 ; Compensate for 16-bit calibration factor
; ; Fall into division routine
div31: ; Divide numerator (w2:w1) by divisor (w3) no error check
for b1 = 0 to 15 ; Repeat for each bit positions
w2 = w2 + w2 + bit31 ; Shift High word of numerator left (top bit is lost), adding carry from w1
w1 = w1 + w1 ; Shift Low word of numerator left
if w2 >= w3 then ; Skip if can't subtract
w2 = w2 - w3 ; Subtract divisor, then..
w1 = w1 + 1 ; Add the flag into the result (in low word)
endif
next b1
return ; Result is in w1, remainder in w2, divisor in w3 is unchanged
```

*EDIT: At low voltages the CALIBADC13 routine doesn't behave entirely as expected, because there appears to be a "bug" in the PIC silicon hardware (FVR1024-DAC) of all the chips I've tested. I plan to report this and update the thread in due course.

Cheers, Alan.

Last edited: