Hi,
CALIBADC10 is a useful facility which allows the PICaxe to calculate its own supply rail (Vdd) to moderate accuracy, without using any additional pins. Positive features are that it requires only a very small amount of program codespace, it works over the entire range of supply voltages (down to 1.5 volts for a 14/20M2 with its "brownout" disabled), and is available on all PICAXEs (some earlier versions may need a different calculation).
However, it works "backwards" by using the (unknown) supply voltage as a "reference" to the ADC and then "measures" the (known) FVR1024 voltage. This may be rather confusing for a novice, but more significantly, the "reversing" process requires numerical division, which is one of PICaxe Basic's weak points. Also, the typical ADC value returned is only around 200 - 300 (even when using CALIBADC10) so the "best" that can be expected is a resolution of 0.4% (at around Vdd = 4.1 volts), which becomes worse at the extremes of voltage. Thus, it cannot directly calculate Vdd accurately to two decimal places, and maybe only to within +/- 30 mV above 5 volts.
Therefore, why not use the FVR as the ADC reference voltage and measure the Vdd against it, thus avoiding the division process (and also using the full range of the ADC)? Of course Vdd is higher than the reference voltage, but most PICaxe chips contain a voltage divider, aka the "DAC". As a Digital-Analogue Converter Output Module it's rather "rubbish" (IMHO), because it has only 32 levels (5 bits), it has a high (and varying) output impedance and with most M2s it must share a pin with the serial (programming) output.
However, as a Programmable Potential divider (POT), the DAC can be very useful when operating entirely inside the chip. Typically its top would be connected to the supply rail and it delivers an appropriate fraction directly to various internal modules, such as the ADC. The PICaxe READADC{10} command is configured to measure the analogue voltage only on Port pins, but PICaxe Basic has also a READDAC{10} command, which does exactly what we want.
Another issue is that "officially" the lowest FVR voltage which may be used for the ADC reference is 2 volts, so cannot be used for the lowest possible Vdds. But such voltages are rather unusual in PICaxe projects, and in practice, for most purposes, the ADC appears to work quite well from the 1 volt FVR (but the DAC does not). I've discussed these potential issues in more detail in this recent thread.
So here is a sample test program to measure and display Vdd in volts, to a genuine resolution of two decimal places. It uses entirely "normal" PICaxe commands (i.e. no POKESFRs), but of course it cannot be used/tested in the PE simulator:
This particular version uses FVR1024 as the reference voltage for the ADC, but FVR2048 can be used if the operational supply rail is never expected to fall below 2.2 volts. With an absolute maximum of 6 volts (Vdd) applied to the top of the DAC resistor chain, the voltage supplied to the ADC input should not exceed 1 volt, so "DAC Level 5" (of 32) is selected. The nominal ADC step is 1 mV (1.024 v / 1024 steps) which is equivalent to 32 / 5 = 6.4 mV per step at the top of the divider chain. Thus to convert the measured ADC10 units to hundredths of a volt, we need to multiply the value by 6.4 / 10 , or 0.64 .
For calibration, I normally use the ** operator, which first multiplies by the specified variable/constant and then divides by 65526. This effectively allows any "fractional" multiplier (i.e. less than 1) to be used for high accuracy calibration. However, it may not be possible to calculate the required constant directly within the Program Editor, in which case a pocket calculator can be used. For this example we want 1 volt to be read as 100 (i.e. displayed as 1.00) so "CALVDD10" has a nominal value = 65536 * 0.64 = 41942 (which may need to be adjusted by +/- 10 units for each mV error in the measured Vdd value around 4 volts).
This Vdd measurement method uses a few more program bytes than CALIBADC plus the required Basic division, but it is still significantly smaller than the code needed to "print" the result to 2 decimal places. In the version above, I have used a custom subroutine to display the decimal value, which uses fewer program bytes and two less byte-variables than the normal BINTOASCII macro (that does not permit the output bytes to "overlay" the input variable word).
But generally, I prefer to calculate and display the voltage directly in millivolts to the best convenient resolution. The following example cannot give a true 1 mV resolution (it's about 7 mV), and this time the 2 volt FVR is used, so the DAC Level is set to 10. The nominal calibration factor is 6.4 / 7 * 65536 = 59919 (which again may need to be adjusted by about +/- 15 for each mV error at a Vdd of 4 volts).
In practice, consecutive ADC measurements may have some "dither", due to electrical noise inside the chip (which occurs also with CALIBADC10). The noise might be slightly higher when using the DAC, but this is not necessarily a disadvantage: If a number of consecutive measurements are made and averaged (taking care not to lose any useful resolution) in the presence of a small amount of noise, then the effective resolution of the ADC can be increased beyond 10 bits. Therefore, in a subsequent post, I'll show some sample code to average the Vdd over several measurements, to give a smoothed result with "true" 1 mV resolution.
Cheers, Alan.
CALIBADC10 is a useful facility which allows the PICaxe to calculate its own supply rail (Vdd) to moderate accuracy, without using any additional pins. Positive features are that it requires only a very small amount of program codespace, it works over the entire range of supply voltages (down to 1.5 volts for a 14/20M2 with its "brownout" disabled), and is available on all PICAXEs (some earlier versions may need a different calculation).
However, it works "backwards" by using the (unknown) supply voltage as a "reference" to the ADC and then "measures" the (known) FVR1024 voltage. This may be rather confusing for a novice, but more significantly, the "reversing" process requires numerical division, which is one of PICaxe Basic's weak points. Also, the typical ADC value returned is only around 200 - 300 (even when using CALIBADC10) so the "best" that can be expected is a resolution of 0.4% (at around Vdd = 4.1 volts), which becomes worse at the extremes of voltage. Thus, it cannot directly calculate Vdd accurately to two decimal places, and maybe only to within +/- 30 mV above 5 volts.
Therefore, why not use the FVR as the ADC reference voltage and measure the Vdd against it, thus avoiding the division process (and also using the full range of the ADC)? Of course Vdd is higher than the reference voltage, but most PICaxe chips contain a voltage divider, aka the "DAC". As a Digital-Analogue Converter Output Module it's rather "rubbish" (IMHO), because it has only 32 levels (5 bits), it has a high (and varying) output impedance and with most M2s it must share a pin with the serial (programming) output.
However, as a Programmable Potential divider (POT), the DAC can be very useful when operating entirely inside the chip. Typically its top would be connected to the supply rail and it delivers an appropriate fraction directly to various internal modules, such as the ADC. The PICaxe READADC{10} command is configured to measure the analogue voltage only on Port pins, but PICaxe Basic has also a READDAC{10} command, which does exactly what we want.
Another issue is that "officially" the lowest FVR voltage which may be used for the ADC reference is 2 volts, so cannot be used for the lowest possible Vdds. But such voltages are rather unusual in PICaxe projects, and in practice, for most purposes, the ADC appears to work quite well from the 1 volt FVR (but the DAC does not). I've discussed these potential issues in more detail in this recent thread.
So here is a sample test program to measure and display Vdd in volts, to a genuine resolution of two decimal places. It uses entirely "normal" PICaxe commands (i.e. no POKESFRs), but of course it cannot be used/tested in the PE simulator:
Code:
#picaxe 20m2 ; Or any other M2
#no_data
#terminal 4800
symbol CALVDD10 = 41942 ; "Fractional" multiplier to calibrate Vdd in 10s of mV (nominally 41942)
main:
do
sertxd(cr,lf,"Vdd= ")
call ReadVdd10 ; Measure the PICaxe supply rail to a resolution of 10mV into w1
call show2dp ; Report the value. (Or use BINTOASCII w1,units,tenths,hundredths )
sertxd(" Volts")
pause 3000
loop
ReadVdd10: ; Read the supply rail in hundredths of a volt into w1
fvrsetup fvr1024 ; Select the 1 volt Fixed Voltage Reference
dacsetup $80 ; Enable the DAC referenced to Vdd (NOT connected to output pin)
daclevel 5 ; = Vdd * 5 / 32 , must be less than the FVR2048 reference (Vdd < 6v)
adcconfig 3 ; Use the FVR as reference voltage for READADC (and READDAC)
readdac10 w1 ; Read the DAC voltage (nominally Vdd * 5 / 32)
w1 = w1 ** CALVDD10 ; Calibrate to 10 mV units
return
show2dp: ; Display the value in w1 with two decimal places
b1 = w1 / 100 ; Integer part
b2 = w1 // 100 ; Fractional part ( 0 - 99)
b3 = b2 // 10 ; Hundredths
b2 = b2 / 10 ; Tenths
sertxd(#b1,".",#b2,#b3) ; Show as a decimal value
return
For calibration, I normally use the ** operator, which first multiplies by the specified variable/constant and then divides by 65526. This effectively allows any "fractional" multiplier (i.e. less than 1) to be used for high accuracy calibration. However, it may not be possible to calculate the required constant directly within the Program Editor, in which case a pocket calculator can be used. For this example we want 1 volt to be read as 100 (i.e. displayed as 1.00) so "CALVDD10" has a nominal value = 65536 * 0.64 = 41942 (which may need to be adjusted by +/- 10 units for each mV error in the measured Vdd value around 4 volts).
This Vdd measurement method uses a few more program bytes than CALIBADC plus the required Basic division, but it is still significantly smaller than the code needed to "print" the result to 2 decimal places. In the version above, I have used a custom subroutine to display the decimal value, which uses fewer program bytes and two less byte-variables than the normal BINTOASCII macro (that does not permit the output bytes to "overlay" the input variable word).
But generally, I prefer to calculate and display the voltage directly in millivolts to the best convenient resolution. The following example cannot give a true 1 mV resolution (it's about 7 mV), and this time the 2 volt FVR is used, so the DAC Level is set to 10. The nominal calibration factor is 6.4 / 7 * 65536 = 59919 (which again may need to be adjusted by about +/- 15 for each mV error at a Vdd of 4 volts).
Code:
#picaxe 20m2 ; Or any other M2
#no_data
#terminal 4800
symbol CALVDD7 = 59919 ; "Fractional" multiplier to calibrate Vdd/FVR value (nominally 59919)
do
ReadVdd7:
fvrsetup fvr2048 ; Select the 2 volt reference (or can use FVR1024)
dacsetup $80 ; Enable the DAC referenced to Vdd
daclevel 10 ; = Vdd * 10 / 32 so always should be less than the FVR2048 reference (Vdd < 6v)
adcconfig 3 ; Use the FVR as reference voltage for ADC and READDAC
readdac10 w1 ; Read the voltage on the "wiper" of the DAC
w1 = w1 * 7 ** CALVDD7 ; Calibrate the reported value to mV units
sertxd(cr,lf,"Vdd= ",#w1," mV")
pause 3000
loop
Cheers, Alan.