Reading Byte with digital voltmeter

OC71

New Member
Hi all
this is my first post.
I am using a DVM to display a number in a byte (I don't know if that is the correct terminology)
I found this code by erco and it seems to work great but I don't understand how.
Can someone explain the fomulas in the code.
Thank you
 

Attachments

1968neil

Senior Member
a quick look at the code would suggest that an analogue meter is being driven (needle type) the value from the variables are used to drive a PWM output (pulse width modulation)
This wont work with a DVM.
 

eggdweather

Senior Member
The code is reading an ADC input, then it scales the input with 215/100 or ~ 2 then adds 12, so if the ADC input was 128 w0 becomes 287 then it outputs a PWM signal that is a function of the applied voltage using 'PWMOUT PWMDIV4,pin, period, duty cycles' the resultant average voltage as measured by a meter is the input voltage.
 

premelec

Senior Member
I hope you have read erco's original postings on this... it's an approximation to display a byte's decimal value with cheap DVM... [< $2.]
 

OC71

New Member
Thanks for the replies.
I have read erco's original thread and have tested it and it works.
If I let b2 = 255 I get a reading of 267 on the DVM, which is pretty close.
I am really curious how erco came up with the numbers.
 

erco

Senior Member
Just a simple curve fit. Actually linear, just y= mx+b. It's voltage dependent, that caibration was dead nuts accurate until my batteries wore down. I'll have to add a 5v regulator and recalibrate.
 

OC71

New Member
Thanks erco,
The advantage of being able to use a $1.60 3wire voltmeter to read a
variable is enormous.
 

AllyCat

Senior Member
Hi,

If I let b2 = 255 I get a reading of 267 on the DVM, which is pretty close. I am really curious how erco came up with the numbers.
Hmm, IMHO 267 isn't particularly close, I'd expect to get within a couple of digits. But I'm puzzled by the "+12" in the code listing, is the "zero" really that far offset?

I'll have to add a 5v regulator and recalibrate.
There's not really any need for additional hardware (except perhaps a R+C low-pass filter for some DMMs) with CALIBADC available in all M2 devices. However, it is rather a struggle to get adequate maths accuracy / resolution with PICaxe Basic using CALIBADC10. But try the following (only on real hardware, not the simulator) which should give reasonable accuracy over a supply range of at least 3.0 to 5.5 volts. You may need to adjust CALIBPWM and BIAS by a few digits to get the fullscale (say 250) and low (say 5) values about correct.

Code:
 #picaxe 08m2 
#no_data 
#terminal 4800  
symbol BIAS = 2                        ; Trim to "sit up" low output values (e.g. 5) 
symbol CAL_PWM = 127                    ; Trim to adjust full-scale values (e.g. 250) 
symbol POTIN = c.1                    ; Analogue voltage input 
symbol PWMPIN = c.2                    ; PWM "Analogue" voltage output  
   pause 1000 
do     
   calibadc10 w1                        ; w1 = 1024mV (nominal FVR) * 1024 (ADC steps) / Vdd (in mV)     
   readadc POTIN,b1                    ; Read in a value between 0 and 255    
   w1 = w1 * CAL_PWM                    ; "Calibrate" the FVR and also make w1 as large as practical    
   w1 = b1 * 5 ** w1 + BIAS        ; Calculate required PWM value (** is equivalent to * /65536)     
   pwmout PWMDIV4,PWMPIN,255,w1    ;     
   sertxd("Pot= ",#b1," PWM= ",#w1,cr,lf)
    pause 1000 
loop
I could explain where the "magic numbers" come from, but it's not simple. ;)

Cheers, Alan.
 
Last edited:

premelec

Senior Member
@erco - we look forward to your version that uses the PICAXE _internal_ voltage reference... :)

or for AllyCats'... [simulpost...]
 

erco

Senior Member
But I'm puzzled by the "+12" in the code listing, is the "zero" really that far offset?
You sound disappointed. Me, I'm just pleased as punch that it was perfectly linear. The offset could be due to the PWM or the DMM or both. Yes, that +12 is exactly what it took to get the calibration right to the last digit, per numerous videos in that thread. Certainly each PICAXE/DVM combo will require individual calibration to achieve that accuracy.

BTW, your calibadc routine to read a voltage with an internal voltage-referenced ADC then display it on the DVM using PWM is the long way around. Why not display the voltage on the DVM directly? :) The entire point of this exercise is to use a $1 DVM (and just one output pin) as a numerical data display, as shown at https://www.youtube.com/watch?v=-6leQnKHJF8 . That will most definitely require an external voltage regulator for consistency.
 
Last edited:

AllyCat

Senior Member
Hi,

Yes, I suppose the +12 corresponds to only around 1% PWM duty cycle or a zero offset of perhaps 40 mV ("4 digits"), which isn't too bad for a (10 volts ?) economy DVM.

Certainly each PICAXE/DVM combo will require individual calibration to achieve that accuracy.
Why not display the voltage on the DVM directly? :)
Perhaps I shouldn't have used the term DMM, it's just what I used to test my code. The point about using CALIBADC is that there is NO "calibration" stage, the code directly generates the required PWM value (at ANY supply voltage) to drive the "final" DVM.

Certainly Microchip do pessimistically quote a very wide tolerance on the FVR voltage, but in practice it seems to be about as good as an economy DVM. So the only calibration that is required is to trim the two program calibration constants so that the DVM displays the "expected" digits near to the two ends of the voltage range.

For simplicity, my "multiplier" calibration constant has steps of +/- 0.8% so you might be able to calibrate only to within one digit of the required value. It is possible to achieve higher accuracy, but the OP seemed happy with an error or 10+, so I didn't bother with the added complexity.

Cheers, Alan.
 

premelec

Senior Member
It seemed to me that you could read the external battery voltage using an internal Vref and adding correction factors to the PWM which comes out at 0 to Vbatt peak... complicated but avoiding the external accurate voltage... possibly pretty linear for any usable Vbatt - [Q.E.D.? :) ]
 

AllyCat

Senior Member
Hi,

..... adding correction factors to the PWM ...... possibly pretty linear ....- [Q.E.D.? :) ]
There's no need for any correction factors, the relationship is perfectly linear (within the quantising and other limitations of the ADC and PWM modules and the constraints of PICaxe Basic's 16-bit integer maths).

The "normal" problem with CALIBADC{10} is that calculation of the actual supply rail voltage (Vdd) rerquires a division process which is one of PICaxe's main weaknesses. But for this particular (PWM) application there's no need to perform any division within the program code. Unfortunately, the "proof" requires a little algebra which isn't easy to show in a forum post, but the basic formulae are:

CALIBADC10 [result] = 1024 [number of ADC steps] * 1024 [nominal FVR voltage in mV] / Vdd [supply rail in mV]

PWM [duty period] = 1024 [maximum duty cycle] * (2560 [average fsd outpuit in mV] / Vdd [in mV]) * (b1 / 256)

Substitute the first formula in the second to give: PWM = CALIBADC10 * b1 * 5 / 2 * [a binary scale factor] ...QED ?

Then it's just a matter of choosing suitable calibration and scale values to keep within the bounds of PICaxe Basic maths. The code I posted in #9 uses only 16-bit maths, however, it looks to be quite feasable to use a 10-bit ADC (Pot) input value, a 10-bit calibration factor and an "extended CALIBADC" subroutine to create a 32-bit word, using the standard * and ** multiplication operators. Of course the practical output range will be limited to about 9 bits because the PWM range at (say) 5 volts Vdd is only between a duty period (pulses) of 0 and about 511, for a 2.5 v nominal output to the DVM.

Cheers, Alan.
 

premelec

Senior Member
Hi Alan, I'm not entirely following your code here - the external DVM has an 'absolute' internal reference so when the Vcc of the PICAXE changes from battery slump etc the PWM peak voltage output changes - I guess use of CALIBADC does the job that's great - thanks! I'm not currently using this sort of readout but will keep your code for reference and empirically test it later.
 

AllyCat

Senior Member
Hi,

I guess use of CALIBADC does the job ...
Yes, that's correct. IMHO CALIBADC is one of PICaxe's most under-rated and useful features (and READINTERNALTEMP the most useless - but "watch this space" ;) ).

Sorry about this late reply; I had been trying to see if it is possible for the (256) DVM values to exactly reflect the byte values, but I got "caught" by a bug in the PICAXE firmware. Of course exact "mapping" depends on the linearity and stability of the DVM, etc. but it's not too difficult to make a linear 10-bit ADC. As far as the "offset zero" is concerned, the manufacturers may do this on purpose: Some users might consider a DVM showing "1" when there is known to be no voltage present (e.g. when not connected) to be "faulty", so the "design centre" for the zero level may be a small negative value, such that the "worst tolerance" devices show nothing higher than zero. It's similar to automobile speedometers which are legally required NOT to under-read, so they are (all) designed to over-read by a few percent.

Having "fixed" the bug, it does seem possible to get an exact match, but my test program code might be considered rather complex so I'll leave that for later. However, it should be possible to get within one digit, even over a full supply range of 2.5 to 5,5 volts with "simple" code, which I'm posting below. Perhaps unintuitively, the higher supply voltages are actually more difficult and the code continues to work down to 2.1 volts (when the 2 volt FVR drops out) although obviously the analogue output voltage must limit at the supply rail (and a DVM on the same rail stop working).

Adding the "Zero Offset" directly onto the calculated PWM value would give optimum resolution, but also be affected by (large) Vdd variations, so in my code I add it directly onto the (scaled up) "Byte" value. Also, adding a value of 1/2 LSB here effectively "rounds" the value (up/down) which may assist the subsequent integer calculations.

For reasonable accuracy, the (integer) maths probably needs to work to around 10-bit resolution. Thus, from my previous post, we need to multiply three 10 (or 11) bit words together and then divide to produce a 10-bit result for the PWM module. Multiplying the input byte value by 4 (to then add in the "zero" bias) cannot exceed 10 bits (1020). A typical calibration/scale factor might be 1024 but reducing it by (say) 6% can ensure that it never needs to exceed 10 bits (1023). These two words can be multiplied together using the * and ** operators and a single word recovered by dividing by 16 (I have used slightly "clumsy" repetition of the code to avoid introducing another variable). Then multiply this word result by a (10 - 11 bit) CALIBADC value using the ** operator (which effectively also divides by 65536) to create the required 10-bit PWM word.

Unfortunately CALIBADC10 doesn't normally return a "real" 10-bit number, but only 8 or 9 significant bits (typically values between 200 and 400) which might not give sufficient accuracy. However, there are methods to increase the resolution, for example as I described in this code snippet. That code may look rather complex, but we only need to increase the resolution here by a factor of 2 or 3, so I've used only the "higher reference voltage" method. It may still seem rather obscure, so you may need to consider it as "magic", but it does use only "real" PICaxe commands (no peek/poke of the SFRs! ) :) . The DAC setting can be arranged to give exactly a 2.5 times multiplier (as is needed by the equtions in my previous post), but I've increased it by about 6% to accommodate some "headroom" in the calibration constant, as previously described.

So here is a first version of my revised code. The displayed digits should be within a few percent (say +/- 5 digits) even before ANY calibration is done and probably within +/- 1 digit when calibrated. Incidentally, I used a "3 1/2" digit multimeter (on its 2 v range) for my tests, because the extra "mV" digit makes it much esier to see the "margin" between the (10 mV) digit changes. In future posts I may show a "higher accuracy" version, together with an interactive test harness to assist the calibration process.

Code:
#picaxe 08m2
#no_data
#terminal 4800

symbol BIAS = 2                        ; Adjust for correct "low" displayed value
symbol CAL_PWM = 970                        ; Adjust for correct "high" displayed value
symbol byt = b1                        ; Value to be reported
symbol CAL_VDD18 = 58837                ; Calibration factor for 18 mV steps (nominally 1024 * 1024 / 18)
symbol PWM_PIN = c.2
symbol Vdd = w3
init:
    byt = 250                             ; Or 198 to optimise with a 3.5 digit multimeter
    pause 1000
do
    calibadc10 w1                    ; Measure the supply rail
     Vdd = CAL_VDD18 / w1 * 18            ; Supply voltage in mV (18 mV steps). Only for testing
    adcconfig 0                    ; Connect ADC ref to Vdd (already configured by calibadc)
     fvrsetup fvr2048                    ; Select Nominal Fixed Voltage Reference = 2048 mV
    dacsetup $88                    ; Top of (DAC) Reference chain to FVR
    daclevel 26                     ; Allow 6% overrange margin (nominal value is 24/32 * FVR)
     readdac10 w2                    ; Read voltage on "wiper" of DAC
      w1 = w1 + w2                   ; Add Calibadc(ref 1v) to Calibadc(ref 1.5v +6%) = Calibadc ref 2.5v (+6%)
     w2 = byt * 4 + BIAS ** CAL_PWM * 4096       ; Top 4 bits
     w2 = byt * 4 + BIAS * CAL_PWM / 16 + w2     ; Lower 12 bits (lowest 4 lost) and add msbs
     w2 = w1 ** w2 max 1023                      ; Final PWM value
    PWMOUT PWM_PIN,255,w2                ; NB: Use PWMDUTY with 20M2s due to a firmware bug
     sertxd("Vdd= ",#Vdd,"Bias=",#BIAS," CAL=",#CAL_PWM," Byte=",#byt," PWM= ",#w2,cr,lf)  ; For testing
    pause 2000
loop
Cheers, Alan.
 
Last edited:

Hansb56

New Member
Hi Allen,

I'm very interested in this thread, I'm also playing with a cheap DMM and PWM.
I like to use it for a thermometer, with a 08M2 and a MCP9700.
That works, but it's not lineair (enough).
Your software works fine. I think I understand what you're doing but I'm not sure if I really do :).
I need values from about zero (not very importing) til about 4 - 4,5 Volt.
How do I tweek it??

Thanks

Hans
 

AllyCat

Senior Member
Hi Hans,

Yes, I have been planning to post an "update" in the "finished projects / code snippets" section (I will put a link from this thread) but I got diverted onto other tasks (which happen to be related to PICaxe temperature measurements).

I like to use it for a thermometer, with a 08M2 and a MCP9700. That works, but it's not lineair (enough).
The MCP9700/PICaxe ADC, PWM output and DMM should all be quite linear, do you know which is not? You should be able to identify which it is by outputting the measured Temperature/Voltage via SERTXD to the PE Monitor and using a better quality multimeter to check the (PWM) output voltage.

Things to try are: (1) connect a 100 nF capacitor on the ADC input pin to ground. (2) Use FVR2048 for the ADC reference voltage (ADCCONFIG). (3) Put a low-pass filter between the PWM output pin and the meter (perhaps 10k + 100 nF) and/or try changing the frequency of the PWM.

Cheers, Alan.
 

Hansb56

New Member
Hi Allen,

Thanks for your reply.

I'll try your suggestions. I think it's in the PWM or in the 3-digit-DMM.

I've experimented with de code below to get a propper reading from two 9700. I've tried it with some different settings of FVR and compared is with a ds18b20.
Those results are accurate enough.

Code:
Code:
' DECLARATIONS ********************************************
'programma om een DS18B20 en twee MCP9700 te testen en af te regelen.

#picaxe 08M2
#no_data
setfreq m4
#terminal 4800

init:
symbol W1820=W8 	'waarde DS18B20
symbol graden = b17
symbol tiende = b16
symbol gradenl = b8
symbol tiendel = b9
symbol sign = b13
symbol dtoets=pinc.3
symbol T1820=C.2	'Ds18B20
symbol led=c.0 	'ledindicatie
Symbol Tbin=C.4	'de bovensate sensor
Symbol Toin=C.1	'de laagste sensor
Symbol Thoog=W1	'variabele met temp Thoog
Symbol Tlaag=W2	'variabele met temp Tlaag.
symbol Tel=B12  	'hulpvariabele
symbol temp=w5

pause	1000
adcconfig %011 ' Vref+ is FVR, Vref- is 0V

Main:
readtemp12	T1820,W1820
sertxd(".w1820..",#w1820,cr,cr,lf)
Sign = "+"			'Display +                                      
IF graden > 127 THEN                       
	Sign = "-"		'Display - 
	W1820 = - W1820                                     
ENDIF
W1820 = W1820 * 16		'Celsius -55.0C to 125.0C   
tiende= tiende / 4 * 25 / 16 + 5 / 10 'rounding up 0.05 to 0.09 (0.1)resolutuion
SERTXD ("   Temperatuur  ",Sign,#graden,".",#tiende,"  Graden C", CR, LF)

gosub een:
gosub twee:
gosub drie:
pause 8000
goto main


een:
fvrsetup FVR4096 			' set FVR at 4096 mV
pause	1000
readadc10 Tbin, Thoog 		' lees Thoog in
Thoog=Thoog-1			;correctie tussen twee 9700
sertxd(".hoog1..", #Thoog,cr,lf)
readadc10	Toin,Tlaag		; lees Tlaag in
Tlaag=Tlaag+2
sertxd(".laag1..",#Tlaag,cr,cr,lf)

thoog=thoog*4-500
tlaag=tlaag*4-500
graden = Thoog/ 10
tiende = Thoog//10
gradenl = Tlaag/ 10
tiendel = Tlaag//10
' display data to terminal
sertxd(".hoog2..", #graden, ".", #tiende, "?",cr,lf)
sertxd(".laag2..",#gradenl, ".", #tiendel, "?",cr,cr,lf)
return

twee:
fvrsetup FVR2048 ' set FVR at 2048 mV
pause	1000
readadc10 Tbin, Thoog ' lees	Thoog	in
Thoog=Thoog-5			;correctie	tussen	twee	9700
sertxd(".hoog1..", #Thoog,cr,lf)
readadc10	Toin,Tlaag	;	lees	Tlaag	in
Tlaag=Tlaag+3
sertxd(".laag1..",#Tlaag,cr,cr,lf)

thoog=thoog*2-500
tlaag=tlaag*2-500
graden = Thoog/ 10
tiende = Thoog//10
gradenl = Tlaag/ 10
tiendel = Tlaag//10
' display data to terminal
sertxd(".hoog2..", #graden, ".", #tiende, "?",cr,lf)
sertxd(".laag2..",#gradenl, ".", #tiendel, "?",cr,cr,lf)
return

drie:
fvrsetup FVR1024 ' set FVR at 1024 mV
pause	1000
readadc10 Tbin, Thoog ' lees	Thoog	in
Thoog=Thoog-7			;correctie	tussen	twee	9700
sertxd(".hoog1..", #Thoog,cr,lf)
readadc10	Toin,Tlaag	;	lees	Tlaag	in
Tlaag=Tlaag+8
sertxd(".laag1..",#Tlaag,cr,cr,lf)

thoog=thoog-500
tlaag=tlaag-500
graden = Thoog/ 10
tiende = Thoog//10
gradenl = Tlaag/ 10
tiendel = Tlaag//10
' display data to terminal
sertxd(".hoog2..", #graden, ".", #tiende, "?",cr,lf)
sertxd(".laag2..",#gradenl, ".", #tiendel, "?",cr,cr,lf)
return

(I use it to measure two temps at the back of the absorption fridge of my caravan, one below and one above the 'radiator'. With the result I drive with pwm a coolingfan.
I saw the article of Erco, and with a 3-digit DMM for less than a dollar a can get some extra functionality. :) )

The valuwe I get from the 9700 after I make it to degrees, must be the same value on the DMM. And that's exact where there is a difference between the 9700 value end de DMM value. With your piece of code the readings are the same.
While i need values bigger than your 'BYT' ...........................:)

With a propper DMM I'll find out if it's de 08m2 or de 3-digit-DMM.

will be continued


Hans
 

AllyCat

Senior Member
Hi Hans,

fvrsetup FVR1024 ' set FVR at 1024 mV
pause 1000
readadc10 Tbin, Thoog ' lees Thoog in
PICaxe Manual 2 and the base PIC Data Sheet specify that FVR1024 should not be used for the ADC reference (the minimum reference voltage is specified as 1.8 volts). That might be because the "monoticity" (i.e. no missing or out-of-sequence values) cannot be guaranteed, but I have recently found a "bug" in the silicon (chip hardware) design which causes non-linearity when driving the DAC from FVR1024, so there might be a similar issue with the ADC.

You will not be able to accurately resolve more that 256 output values using PWM but if you want to increase the output voltage to the meter (obviously it cannot exceed the supply rail) then the simplest method is to multiply w1 by 3/2 or perhaps up to 2.. For example, the line before the PWMOUT could be changed to: w2 = w1 * 3 / 2 ** w2 max 1023 . But I have now improved the numerical calculations (to reduce the risk of overflows) in the more accurate code that I hope to post soon.

Cheers, Alan.
 

Hansb56

New Member
Hello Allen,

Yes I know that FVR1024 is not recommanded in the manuel. The results looked hopefull, I can use the 10mV=0,1gr/C using FVR1024. But if that's possible a cause I'll try with FVR2048. I don't need the extra accuracy.
I've experimented with the value of w1 and yes, I can get the reach that I need. Thanks for that.

And of course I'm looking forward your update.

Greetings,

Hans
 

AllyCat

Senior Member
Hi all,

Sorry that over a month has passed, but I have finally posted an "Interactive Calibration and Demonstration" version of my PWM - DMM driver program in the Finished Projects - Miscellaneous section of the forum. However, at over 200 lines (600+ bytes) it may be rather overwhelming, so I've also posted the two PWM-specific subroutines in the Code Snippets section.

The "Interactive" part of the program allows the "Bias" (zero end) and "PWM" (multiplier for the high values) calibration constants to be adjusted for "best fit", using the PE's terminal interface. It is probably best to set the "bias" at a byte value around 5 and then the "high" end caibration at 250 (or perhaps 195). Also, it's much easier to test with a true 4-digit meter (or a 3.5 digit meter on its 2 volts range) so that the "mV" digit gives an indication of how close to "rollover" (or roll-under) is the present setting. Personally, I tested the code using a DVM with concurrent capture of max/min values, whilst the Vdd was swept down from 5v to <3v (another "finished" project, hopefully soon to be documented). The "mV" digit just about stayed within a range of 0 ... 9 (i.e. a constant byte value) , but it was a close thing. ;)

The rest of the program allows the FVR and Internal Temperature sensor values to be calibrated, so that an accurate Vdd value can be reported via SERTXD. Also, an indication of the chip's temperature supplied via SERTXD and to the DMM. Note that the temperature must be calibrated before use (the base chips can vary by more than 100 degrees), but need be done only at room temperature for reasonable results. The READINTERNALTEMP algorithm is a preview of a "medium resolution" version of code that I'm still developing; it uses the "four diodes in series" temperature sensor, so can be accurate only down to about 3.2 volts Vdd.

Once calibrated, all the constants can be hard-coded into the program and then compiled in the fixed demonstration format (~300 bytes).

Cheers, Alan.
 

techElder

Well-known member
Alan, what the heck does this mean? " just about stayed within " Aw, just kidding you! :D

This is great stuff you are doing. We'll all benefit from it. Thanks!

PS. And that's not to minimize ERCO's original contribution that was eye opening.
 
Top