Picaxe Maths

rmeldo

Senior Member
Hi,

I am having trouble with the conversion of the calibadc10 into a decimal number to display.

the code below aims to calculate two variables containing the units and two decimal points.

Code:
getVoltage:
   calibadc10 voltUnit
   voltDec   = Vref*50/voltUnit *2
   voltUnit  = Vref/voltUnit
   voltUnit  = voltUnit * 50
   voltUnit  = voltUnit * 2
   voltDec   = voltDec - voltUnit
   voltUnit  = voltUnit / 2
   voltUnit  = voltUnit / 50
This code however gives me the following issues:

1) it never gives uneven decimals (it will never give 4.57V, only 4.56 or 4.58).
2) when the battery progressively discharges I get the following: 5.12 ... 5.10 ... 5.00 ... 5.8

I think the error might be in my (mis)understanding of the Picaxe maths. Could someone give me a pointer?

Also the only use for these variables is to create a string to sent to the LCD screen and to a web server (via a serial call to ESP8266). Is there a better way of doing this and save on code space?

Many Thanks
Riccardo
 

rmeldo

Senior Member
Thanks for that.
I have just fitted 4 NiMH batteries and their initial voltage was 5.18V so it will overflow, according to your post. I think I could live with an even last digit from the resolution point of view.
By the way, what is the minimum voltage I can discharge the batteries to?

Many thanks
Riccardo
 

AllyCat

Senior Member
Hi,

You can use the same code with a supply above 5.11 volts by changing b1 to a word variable, e.g. w2.

Generally, there's not much energy left in a NiMH cell when it gets down to 1.2 volts (at least under moderate load). 1.1 volts per cell is often used as a design cutoff level, so perhaps 4.5 volts for your application.

Cheers, Alan.
 

marks

Senior Member
Hi rmeldo,
Using maths you cant really improve on your 02 resolution.
for a slight improvement you may be able to try some averaging.
using readadc10 gives some extra resolution but then requires an inconvenient voltage divider.
Rich (BB code):
#picaxe 18m2  '  AXE132 8Bit marks
#no_data
#terminal 9600
SETFREQ M8
dirsB = %11111111
dirsC = %11001111 
'           DB7  = B.7
'           DB6  = B.6
'           DB5  = B.5
'           DB4  = B.4
'           DB3  = B.3
'           DB2  = B.2
'           DB1  = B.1
'           DB0  = B.0
SYMBOL  E  = C.6
SYMBOL RS  = C.7
SYMBOL senddata    = pinsb
SYMBOL index       = b0   : SYMBOL avr = b0

      SYMBOL ADCval      = w1
      SYMBOL batV        = w2
      SYMBOL D0          = b6
      SYMBOL D1          = b7
      SYMBOL D2          = b8
      SYMBOL D3          = b9
            
InitialiseLCD:   
      FOR  index = 0 to 6
          LOOKUP index, ($38,$38,$38,$0C,$01,$02,$06),senddata : PULSOUT E,1 ' Initialise LCD/OLED
          '(8bit/2line/5x8)*3(Display On)(Clear Display)(Return Home)(Entry Mode Set)
      NEXT index : PAUSE 10
            
Main: 
batV = 0
FOR avr = 1 TO 25
      CALIBADC10 ADCval : ADCval = 41902 / ADCval : batV = ADCval+batV 
NEXT avr

BinToAscii batV,D3,D3,D2,D1,D0
sertxd("Vdd = ", d3,".", d2, d1,"  ", d0," Volts",cr,lf)

DisplaySupplyLCD:
LOW RS  :senddata = 128 :   PULSOUT E,1
      HIGH RS
      FOR  index = 0 TO 15
          LOOKUP index,("LCD Supply ",D3,".",D2,D1,"V "),senddata
                                 pinsB = senddata : PULSOUT E,1 ' sending characters to line one
      NEXT index

    GOTO Main
 
Last edited:

BESQUEUT

Senior Member
Note that d0 in
sertxd("Vdd = ", d3,".", d2, d1," ", d0," Volts",cr,lf)
is an illusion : you cannot get more significative digits than 2 1/5 from the CALIBADC10 ...
 

AllyCat

Senior Member
Hi,

Yes, averaging is only useful if the input value is continuously changing. There is no point in "averaging" the same value over and over again, i.e. unless there is some "noise" in the system.

Therefore, it might work if you're using PWM, have "forgotten" to fit a decoupling capacitor and/or are using a poorly regulated mains supply. Also, it may "soften" the CALIBADC transitions (between integer values) and perhaps reduce "dither" with a rapidly updated display.

So, I have often wondered about injecting a controlled amount of noise onto an ADC input to increase the resolution (with averaging), using for example, the "touch" oscillator, but that can't be done with CALIBADC, since it's entirely an internal PICaxe function. Therefore, I devised these code snippets which can resolve the Vdd to sub-mV levels.

At the moment I'm rebuilding my test-rig, but will try to post a real (i.e. hardware-based) comparison soon.

Cheers, Alan
 

hippy

Technical Support
Staff member
the code below aims to calculate two variables containing the units and two decimal points.
Can you explain how your code is meant to work because I was intrigued enough to try it, and while it may appear to work, it doesn't seem to do what I would be expecting it to. Though it could depend on what your 'vref' value is.
 

AllyCat

Senior Member
Hi,

The ADC Reference/Vdd voltage ratio is read 14 times (with different levels of "noise"), added to the original CALIBADC10 value and averaged, which gives approximately the 4 bits of extra resolution claimed (and achieved! ). The exact improvement will depend on whether it's a 2 volt or a 5 volt Vdd (which you seem to br assuming).

It may need the CALIBADC15 or CALIBADC16 versions to get to truly sub-mV resolution. :)

Cheers, Alan.
 

AllyCat

Senior Member
Hi,

4 bits of extra resolution claimed (and achieved! ).
I don't believe I have ever claimed a high degree of accuracy, but the method does give quite good repeatability. Also, I don't claim 14-bits resolution (for CALIBADC14), only that it is about 4 bits better than CALIBADC10, which you (and I) have pointed out is typically only around 8 bits resolution in practice. So perhaps 12 bits resolution, which of course represents 1 mV on a 4V fsd.

In the code link posted at #5, I specifically state: "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". The other snippet hinted at in that post only offers "higher resolution/accuracy" (than CALIBADC10 with a rather dubious conversion to Vdd).

My method does work, but of course it can be used only on real PICaxe hardware, not the simulator. It's in several of my programs including using PWM as a "stabilised" DAC, even with an unregulated Vdd, and an improved version of READINTERNALTEMP, which is repeatable to about 1 degree over a wide Vdd range. The latter is sufficiently "accurate" (in compensating for Vdd variations) that I've found both the tempco (-130 ppm/degC) and the regulation (0.270 %/V) of the FVR need to be taken into account.

I hope to get these projects formally documented in due course, but there's a "teaser" showing an improved readinternaltemp graph (which uses the same basic method) here.

Cheers, Alan.
 

BESQUEUT

Senior Member
Code:
Calibadc14:					; Measure supply rail using "14 bits resolution"
...Also, I don't claim 14-bits resolution (for CALIBADC14), only that it is about 4 bits better than CALIBADC10, which you (and I) have pointed out is typically only around 8 bits resolution in practice. So perhaps 12 bits resolution, which of course represents 1 mV on a 4V fsd.
.

OK for resolution, not accuracy, and OK for 12 bits, not 14...
From a 10 bits reading and some noise, this seems to be possible.
We must take care that repeatability is not resolution : we can repeatabily read something wrong (or random)
To achieve a 12 bit resolution, I propose the following protocol :
- start with a variing voltage, truly controled with a more than 12 bits voltmeter.
- make small variations with the claimed resolution,
- compare reference voltage with measured voltages

To achieve 12 bit resolution, matching must be greater than random.

For example, with a reference voltage variing by mV from 4000mV,
we have to measure something like :
4005 mV (OK accuracy is 5mV : no problem)
4006 mV
4007 mV
4008 mV
etc...

If we measure :
4005 mV
4007 mV
4006 mV
4009 mV
etc...
12 bits are show, but 12 bits resolution is not achieved...

As measurements are good to 2mV, I will say that resolution is 11 bits in that case.

It may need the CALIBADC15 or CALIBADC16 versions to get to truly sub-mV resolution. :)
My voltmeter is "only" 40 000pts (15 bits ?) so I am not able to test that... :p
 
Last edited:

BESQUEUT

Senior Member
Not sure to understand your code, but made some measurements :
Code:
do
	gosub calibadc14
	sertxd ("w1=",#w1,13,10)
loop
I did wait for one hour before starting measurements for both Picaxe and Voltmeter to stabilize.
The voltage generator is adjusted to mV reading a Voltcraft VC 920

Here are my measurements :
Code:
Volts	w1 
3,9990	7516
3,9980	7513
3,9970	7513
3,9960	7513
3,9950	7509
3,9940	7506
3,9930	7506
3,9920	7503
3,9910	7496
3,9900	7496
3,9890	7496
3,9880	7496
3,9870	7496
3,9860	7493
3,9850	7493
3,9840	7489
3,9830	7486
3,9820	7483
3,9810	7483
3,9800	7483
To have full 12 bits resolution (IE 4000 points) we need to read differents values for each voltage reference.
To my mind, w1 is 11 bits
 

AllyCat

Senior Member
Hi,

Thanks for those tests. It appears that w1 has incorect values, so we need to find out why. The values should be around 4000, because CALIBADC10 has a value near to 256 (at 4 volts) so 16 effective reads should total around 4100. The "magic number" (calibration quotient) is nominally 2^24 (~16 million) so w1 should be also around 4000.

Maybe the silicon hardware wasn't setup correctly. Which PICaxe chip did you use? I always use M2s and normally head my code accordingly, but as there are no SFR accesses I omitted it this time. I do see that the ADCCONFIG command uses different parameters with X2s and M2s but the particular parameter (0) appears to be compatible. Also the DAC is not supported with many X2s, but the PE should report that.

Can you check whether the CALIBADC14 routine generates ~4000 in w3 at ~4 volts Vdd (or 16000 after the compensation stage) ?

However, I wouldn't be particularly surprised if CALIBADC14 is "only" giving 11 bits resolution. That's still 3 bits better than CALIBADC10 and I wouldn't expect to achieve the "theoretical" 4 bits improvement, because it's relying on a randomising trick using the available hardware. There have been similar discussions comparing the PICaxe RANDOM command with a "real" random sequence.

Cheers, Alan.
 

BESQUEUT

Senior Member
Test with 18M2

Same program, but 18M2 Picaxe.
For each ref voltage, there are many values (often 3 to 4). So I wrote min and max, and did a mean with Excel :
Code:
volts	w1 min	w1 max	mean
3,9990	4036	4040	4038
3,9980	4035	4038	4036,5
3,9970	4034	4037	4035,5
3,9960	4033	4036	4034,5
3,9950	4032	4034	4033
3,9940	4030	4033	4031,5
3,9930	4029	4032	4030,5
3,9920	4029	4031	4030
3,9910	4028	4031	4029,5
3,9900	4027	4030	4028,5
3,9890	4026	4029	4027,5
3,9880	4025	4028	4026,5
3,9870	4024	4027	4025,5
3,9860	4022	4026	4024
3,9850	4022	4025	4023,5
3,9840	4021	4025	4023
3,9830	4020	4024	4022
3,9820	4020	4021	4020,5
3,9810	4017	4020	4018,5
3,9800	4016	4020	4018
To my mind, this is excellent : true 12 bits resolution, but lot of noise...
So I made some code to mean w1 :
Code:
do
	w4=0
	for b20=1 to 10
	    gosub calibadc14
          w4=w4+w1
      next b20
	w4=w4/10
	sertxd (" ",#w4)
loop
With that, there are only one or two values, very similar to Excel mean.
 

mikeyBoo

Senior Member
Hi-Res DAC or ADC on Picaxe without the math

Something to consider:

The attached schematic gives 12-bit (or 16-bit if more expensive DAC) resolution on the Picaxe without doing any math. Each bit = 0.001v. If you use the 16.38v FS op-amp, just multiply the bit-value by 4. Voila! Voltage Output Calibrator.
Of course, with >12-bit resolution, temperature becomes a major issue, the DAC must have a stable internal reference (like the LTC2631 shown in drawing) & cheap carbon resistors won’t do.

The attached design is for a Picaxe-based calibrator with OLED display. However, just plug it in to a DVM & skip the display. The design worked fine on a breadboard, but never got around to making a circuit-board. A Picaxe-14M2 or Picaxe-20M2 would work just fine.
The same strategy works with ADCs. Change bit values to engineering units (temp, pres, etc.) via the linear conversion procedure I put in a post a day or so ago.
Have fun!
 

Attachments

AllyCat

Senior Member
Hi,

To my mind, this is excellent : true 12 bits resolution, but lot of noise...
Thanks, that's very encouraging. I don't have any "precision" equipment myself, so have been using a different method:

Basically, the test rig ramps the Vdd (down) slowly and uses the PICaxe to report its measurements via SERTXD, which are then transferred into Excel (or Libre Office to be precise). However, I have been more interested in the complete voltage range (5.5v down to below 2v) so my sweep rate was about 10 mV/second. But, having just rebuilt the (very simple ) test rig, I increased the capacitor to give around 1 mV/second, but was rather disappointed with the amount of noise in the system (more than I normally see).

To cut a long story short, I normally use a Vdd around 3 volts (a single LiFePO4 cell is my favourite) but was using 4 - 5 volts to be compatible with the above measurements. And the ADC noise seems to be much higher. :eek:

The attached graphs are at 4 volts and 2.5 volts and each shows 8 curves measured concurrently. The first is a basic "one decimal place" routine and the second is the best that can be obtained from a single CALIBADC10 reading (by maximising the quotient). The third takes two measurements with simple "randomising" and the fourth is the code exactly as posted by marks above. The final four are CALIBADC12 to CALIBADC15 as posted in my code snippet (except 15 which uses a FOR loop from 1 TO 31). Note that the absolute levels of the curves are not comparable because I have introduced minor offsets (tens of mV) in Excel to separate the overlaying lines.

The considerable "dither" on the "one decimal place" data shows how much noise is present and the curve for marks is unexpectedly good (because the degree of noise was unexpected). The four "extended CALIBADC" lines do improve progressively but it is not obvious whether this is due to the additional "randomisation" through the DAC or just to averaging more samples. The number of samples is 4, 8,16 and 32 respectively, compared with the 25 samples in the marks code, so I think the DAC is doing something "useful".

Thus, I was quite relieved when I processed the 2.5 volt data, which is much more as (I) expected. The noise is obviously much less and its omission clearly affects the marks code adversely. Note how CALIBADC13 (which is the only version to use FVR1024 to drive the DAC) is "wandering off" due to a bug in the PIC silicon hardware. I'll post code for an alternative version of CALIBADC13 and for CALIBADC15 in due course.

Calibadc15HV.png
Calibadc15LV.png

Cheers, Alan.

PS: Did you find out why the 40X2 gave a different result?
 

rmeldo

Senior Member
For what it's worth here is a log of the voltage on my gas consumption logger. Not sure whether it furthers the discussion but it is data.voltageHistory.PNG

I fitted new batteries on Monday and I am uploading the voltage readings (as well as the gas consumption) every half hour.
I saw this morning that 5.04 was missed altogether, with the reading oscillating between 5.06V and 5.02V. That probably means that in the last day or so the voltage was really ~5.04, but it wasn't displayed. It looks like I might be experiencing a lower resolution that 10 bits.

The code is still the original (stock Picaxe readadc10 plus some maths to convert to two digits voltage values). I during the implementation I dis calibrate the "magic number" for the specific silicon I am using. I haven't reworked the code yet as I am still in the discovery phase, not having gone through a full battery discharge cycle yet, but I sense (pardon the pun) that steps 0.04 V are a bit too large, especially in the flat part of the discharge curve. I can imagine having a go at predicting the date when the battery will need recharging (being away from home for several days) and being able to detect the slope of the discharge curve would be quite important.


Riccardo
 

AllyCat

Senior Member
Hi,

It looks like I might be experiencing a lower resolution that 10 bits.
Yes, even if the CALIBADC10 were creating "real" 10-bit data (which it doesn't with higher voltage rails) the normal PICaxe Basic division (required to calculate the Vdd) is generally limited to 8-bits resolution. The double-sized step is an artefact of the integer maths, I think I've even seen triple-sized steps on occasions.

Due to the amount of (unexpected) noise which seems to occur on CALIBADC measurements, the easiest solution appears to be to sum multiple measurements, perhaps even just two. So I plan to update my code snippet in due course.

And my apologies for rather hijacking your thread (but we have remained on topic). ;)

Cheers, Alan.
 

rmeldo

Senior Member
Not at all,

it is turning into quite a useful thread. In engineering issues have ramifications which need discussing to come up with a full understanding. If I was into quick solutions I wouldn't be making my own system but go and buy one.

Riccardo
 

hippy

Technical Support
Staff member
I did it in post#1.
Nothing to boast about however.
I imagine post #13 just got lost in the midst of other postings, but I expressed doubts on the suitability of the code in post #1. Can you explain how it is meant to work as I don't think it is doing what you expect it to.
 

BESQUEUT

Senior Member
I did it in post#1.
Nothing to boast about however.
Riccardo
Am I stupid, or this code is equivalent to :
Code:
getVoltage:
   calibadc10 voltUnit
   voltDec   = Vref*50/voltUnit *2
   voltUnit  = Vref/voltUnit
   voltUnit  = voltUnit * 100
   voltDec   = voltDec - voltUnit
   voltUnit  = voltUnit / 100
How do you write result ?
 

rmeldo

Senior Member
Yes it is equivalent. Now that you point it out there is no need to do the multiplication and division in two stages. (I was confused by the need to avoid overflow in the first line of code).

To answer Dippy's question, the idea is to calculate the whole part of the voltage by doing the voltage conversion BEFORE multiplying by 100

the code is:

voltUnit = Vref/voltUnit
voltUnit = voltUnit * 100

this should give as a result 0 or 100,200,300,400,500 depending on whether the voltage is 0.xx, 1.xx, 2.xx, 3.xx, 4.xx, 5.xx

the accurate voltage (times 100) is calculated with the following code:

voltDec = Vref*50/voltUnit *2

this, for instance would result in 453 if the voltage was 4.53V

Then subtracting voltUnit from voltDec I am left with 100 times the decimal part of the voltage (e.g. 53 in the case of 4.53V).

Will it work?

I can imagine there is a slicker way to do this without using more program space and it would still be interesting to know.

However I am quite drawn by the prospect of increasing the resolution using the methods discussed in this forum.


Answering Besquet's question, when sending a string through serout I then reconstitute it by using the command:

serout TCP_Send,T19200_16,("bat=",#voltUnit,".",#voltDec,13,10,13,10,13,10)

this serout will not work when, for instance the voltage is 4.08, because it will print out 4.8

I was using an if clause to print the intermediate zero when needed, but I had to take it out because I ran out of programming memory.

Instead now I deal with this on the webserver side, in the php script.


Many Thanks
Riccardo
 

hippy

Technical Support
Staff member
How did you calculate / determine your Vref constant to be 1058, and what is your actual Vref voltage ?
 

BESQUEUT

Senior Member
I saw this morning that 5.04 was missed altogether, with the reading oscillating between 5.06V and 5.02V. That probably means that in the last day or so the voltage was really ~5.04, but it wasn't displayed. It looks like I might be experiencing a lower resolution that 10 bits.
This is an effect off Integer math :
Suppose temp=ent(Vref/VolUnit)
Successives values are :
Code:
VoltUnit  Vref/VoltUnit  Temp  Temp*2
204       5,1863	259	518
205       5,1610	258	516
206       5,1359	256	512
207       5,1111	255	510
208       5,0865	254	508
209       5,0622	253	506
210       5,0381	251	502
211       5,0142	250	500
:cool: You are a good candidate to use floating point library...
 

rmeldo

Senior Member
Thanks to both.

I have verified the code with Matlab


Code:
Vref = 1058;
voltUnit = (204:211)';
voltage = Vref./voltUnit;


voltDec   = floor(Vref*50./voltUnit)*2;
voltUnit  = floor(Vref./voltUnit);
voltUnit  = voltUnit * 100;
voltDec   = voltDec - voltUnit;
voltUnit  = floor(voltUnit / 100);


temp = floor(Vref./voltUnit);

%       Voltunit Vref./voltUnit voltUnit voltDec 
toDisplay = [Voltunit voltage voltUnit voltDec ]

the result is as Besquet suggested.

voltUnit Vref./voltUnit voltUnit voltDec
initial value voltage for display for display
204.0000 5.1863 5.0000 18.0000
205.0000 5.1610 5.0000 16.0000
206.0000 5.1359 5.0000 12.0000
207.0000 5.1111 5.0000 10.0000
208.0000 5.0865 5.0000 8.0000
209.0000 5.0622 5.0000 6.0000
210.0000 5.0381 5.0000 2.0000
211.0000 5.0142 5.0000 0


Time to switch to AllyCat's code!

Riccardo
 
Top