Using CALIBADC10 to show the PICAXE Supply Voltage to 2 decimal places


Senior Member

The CALIBADC command is perhaps the nearest thing to a "free lunch" with a PICaxe. It can indicate the supply rail voltage (on the Program Editor's Terminal) using only a few lines of code, with absolutely NO external hardware or PICaxe pins required. Often, I now include code similar to the following at the top of my development programs, to check that the battery is good (and also indicate if the program restarts unexpectedly for any reason).

Code to report the voltage to one decimal place has been described previously and is generally "good enough" when using Alkaline cells (whose voltage falls drastically over their full discharge life). I couldn't find it with a quick forum search but it's basically:

calibadc10 w1
b1 = 10500 / w1
bintoascii b1,b4,b5,b6
sertxd ("Vdd= ",b5,".",b6," Volts",cr,lf)
However, particularly when using only 2 x rechargeable NiMH cells, or most lithium types, a resolution of 100 mV is hardly sufficient to give a good indication of their state of charge. The "magic number" (10500) cannot be multiplied by 10 (because it would overflow the word variable), but it can be multiplied by 5, then the division performed, then the result multiplied by 2. This gives a result in tens of millivolts, but the final digit will always be even. Therefore, I devised the following code which resolves the Vdd to 10 mV, at least for lower voltages (at higher voltages the ADC and PICaxe's limited division resolution let us down).
 symbol CALVDD = 52429	; 1024 * 1.024 * 1000 / 20	(DAC steps * Ref V / Resolution in mV)  
	calibadc10 w1				; Measure FVR (nominal 1.024 v) relative to Vdd (1024 steps)
	b1 = CALVDD / w1			; Result fits into a single byte if Vdd < 5 volts 
	inc w1
	w1 = CALVDD / w1 + b1			; Average Vdd in tens of mV - then fall into Show2dp routine
	b1 = w1 / 100				; Integer volts
	w1 = w1 // 100				; Decimal part (remainder)
	b3 = b2 // 10				; Hundredths digit
	b2 = b2 / 10				; Tenths digit
	sertxd("Vdd=",#b1,".",#b2,#b3," Volts",cr,lf)
;	return
Just a few comments on this implementation. Firstly, CALVDD is a nominal, theoretical, calculated value. But if a reasonably "accurate" indication of the supply voltage is required, then the code must be loaded into the specific PICaxe chip, the actual Vdd measured with a multimeter and then this constant modified accordingly.

Secondly, I have not used the normal BINTOASCII command because it is rather inefficient (in Program and Variable memory space). Strictly, it requires 5 bytes for the ASCII values (in practice 3 appear to be accepted, or the first two can be "dummy" or repeated variables) and the Program Editor does not permit the ASCII output variables to re-use the input word. My alternative routine uses only 3 bytes (a "temporary byte" and a "temporary word"), but the byte value will overflow if Vdd > 5.11 volts. Also, it relies on the "overlay" of b2 and b3 on w1.

Finally, the "trick" used to obtain the increased resolution assumes that the integer result from CALIBADC10 is truncated, so that incrementing it may actually produce a more "accurate" result. However, the base PIC data sheet suggests that the ADC might already give a "rounded" (up or down) result. Also it's arguable whether CALVDD should be based on a value of 1023 or 1024 (ADC steps). But these are only theoretical considerations which will be "corrected" by the necessary calibration procedure already mentioned.

Cheers, Alan.


Senior Member

Although the above code does give a "cosmetically better" result by displaying both odd and even final digits, it doesn't necessarily give any better accuracy. :rolleyes:

So some time ago I improved it by adding half of the divisor to the numerator as a better way of "rounding up" the potential result. Or strictly, one could add and subtract a quarter of the divisor, but it's hardly worth the complication. Also, with hindsight, reducing the variables to just 3 bytes (and limiting the result to 5.11 volts) was perhaps an economy too far, so I now use two words (4 bytes).

Furthermore, a current thread has revealed that there may be quite a lot of "noise" on the CALIBADC10 returned value, so it can be worthwhile to take the average of several readings. The repeated read has very little penality in terms of speed or code size, because CALIBADC is a "real" interpreter command (i.e. coded inside the PICaxe), not just a pseudo (macro) command inserted by the Program Editor.

Therefore, my preferred code for a two-decimal-point supply calculation is now as follows. The Vdd calculation itself uses about 24 bytes of program space, compared with 18 of the version above (and 4 bytes of variables compared with 3).

symbol CALVDD = 52429	; 1024*1.024*1000/20  (DAC steps * Ref V / Resolution in mV)  
	calibadc10 w1		; Measure FVR (nominal 1.024 v) relative to Vdd (1024 steps)
	w2 = w1 / 2 + CALVDD	; Effectively round up CALVDD by half a (result) bit
	w2 = w2 / w1		; Take the reciprocal to calculate (half) Vdd (tens of mV)
	calibadc10 w1		; Read the value again because noise may be present :)
	w1 = CALVDD / w1 + w2	; Calculate Vdd/2 again and add in the first value
	w2 = w1 / 100		; Integer volts
	w1 = w1 // 100		; Decimal part (remainder)
	b3 = b2 // 10		; Hundredths digit
	b2 = b2 / 10		; Tenths digit
	sertxd("Vdd= ",#w2,".",#b2,#b3," Volts ")
Cheers, Alan.