CALIBADC10 Code examples

marks

Senior Member
#1
For good results using A/D Converters make sure you always include BY-PASS Capacitors,
without them power supply noise may well eliminate any chance of precision. Ideally they
should be found at the power supply and all devices you have populated your board with .
The smaller capacitors closest to the device pins and lead length kept as short as possible.
on the 18M2 Board I am using it has a 47uF Tantalum nearby with a 0.1uF at the picaxe
supply pins. I also have an 0.01uF capacitor which Microchip sometimes recommends
with the lowest values closest to the device.
Code:
 #picaxe 18m2  '  AXE132 8Bit marks
 #no_data
 #terminal 19200
 SETFREQ M16
 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 CALIB_ADC10 = W1
	SYMBOL Supply      = W2  :  SYMBOL Value = W2
        SYMBOL D0          = b7 
	SYMBOL D1          = b8 
	SYMBOL D2          = 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:                                                       ' Steps x Vref   / Vsup = CALIBABC10
     CALIB_ADC10 = 0                                        '  1023 x 1.024v /5.000 = (209)
       FOR avr = 1 TO 5                                     '  1023 x 1.024v /4.096 = (255)
      CALIBADC10 Value  : CALIB_ADC10 = CALIB_ADC10 +Value  '  1023 x 1.024v /3.300 = (317)
       NEXT avr                                             '  1023 x 1.024v /2.048 = (511)
       
 CALIB_ADC10 = CALIB_ADC10 /5                        ' just averages 5 readings which probably is not required 

  Supply = 20951 /  CALIB_ADC10 *5                   ' 1023 steps x 1024 mV = 1047552 / 50 = 20951
        
  Sertxd("ADC10 = ",#CALIB_ADC10)     

  BinToAscii Supply ,D2,D1,D0 : Sertxd("  LCD Supply  ",D2,".",D1,D0," V",cr,lf) ' 5.05 V resolution 05
  
DisplaySupplyLCD:
LOW RS  :senddata = 128 :   PULSOUT E,1
	HIGH RS
      FOR  index = 0 TO 15
 	    LOOKUP index,("LCD Supply ",D2,".",D1,D0,"V"),senddata 
 	                           pinsB = senddata : PULSOUT E,1 ' sending characters to line one 
      NEXT index
Pause 2000
    GOTO Main
 

marks

Senior Member
#2
Doubing our resolution to try achieve 0.01 decimal places

10bit Analogue Digital Converters are most often described as having 1024 definitive Steps.Which are 0 to 1023.
If we are using for example a Vref of 2048mV ,the width of all steps are identical so they will all be 2mV wide.
If we look at the last step which is 1023 it is between 2046mV and 2048mV.
So you can imagine if our input is 2046mV this value is between steps and so can fluctuate between 1022 and 1023.
This is known as the TRANSITION POINT. By taking many measurements we can try and determine this MidStep Point,
using it to try and double our resolution.
Using Calibadc10 automatically sets Vreference to 1.024v
It seems to work accurately between the tested supply voltages of 2 .00v to 5.35v .
At 1.91v it gives Up Like me on a Friday Night lol.
Code:
 #picaxe 18m2  '  AXE132 8Bit marks
 #no_data
 #terminal 19200
 SETFREQ M16
 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 CALIB_ADC10 = W1
	SYMBOL Supply      = W2  :  SYMBOL Value = w2 
	SYMBOL MStep       = b6
        SYMBOL D0            = b7 
	SYMBOL D1            = b8 
	SYMBOL D2            = 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:                                                       ' Steps x Vref   / Vsup = CALIBABC10
     CALIB_ADC10 = 0                                        '  1023 x 1.024v /5.000 = (209.5)*2 = 419 
       FOR avr = 1 TO 25                                    '  1023 x 1.024v /4.096 = (255.7)*2 = 512
      CALIBADC10 Value  : CALIB_ADC10 = CALIB_ADC10 +Value  '  1023 x 1.024v /3.300 = (317.4)*2 = 635  	
       NEXT avr                                             '  1023 x 1.024v /2.048 = (511.5)*2 = 1023   
       
MStep = CALIB_ADC10 //25 : IF MStep <>0 THEN LET MStep =1 : ENDIF : CALIB_ADC10 = CALIB_ADC10 /25  

  Supply = 65472 // CALIB_ADC10 *8 /CALIB_ADC10  ' 1023 steps x 1024 mV = 1047552 / 16 = 65472
  Supply = 65472 /  CALIB_ADC10 *8 +supply
 
   CALIB_ADC10 = CALIB_ADC10 +MStep         ' double resolution by attemping to detect intermediate steps.
  Supply = 65472 // CALIB_ADC10 *8 /CALIB_ADC10 +supply
  Supply = 65472 /  CALIB_ADC10 *8 +supply              
         
 CALIB_ADC10 = CALIB_ADC10 *2 -MStep : Sertxd("ADC11 = ",#CALIB_ADC10,"  ",#Supply," mV  ")     

  supply = supply + 7 /10 ' I used 7mV to round up
  BinToAscii Supply ,D2,D1,D0 : Sertxd("LCD Supply  ",D2,".",D1,D0," V",cr,lf) ' 5.01 V resolution 01
  
DisplaySupplyLCD:
LOW RS  :senddata = 128 :   PULSOUT E,1
	HIGH RS
      FOR  index = 0 TO 15
 	    LOOKUP index,("LCD Supply ",D2,".",D1,D0,"V"),senddata 
 	                           pinsB = senddata : PULSOUT E,1 ' sending characters to line one 
      NEXT index
Pause 2000
    GOTO Main
 

AllyCat

Senior Member
#3
Hi,

Code:
Main:                                                     
........
MStep = CALIB_ADC10 //25 : IF MStep <>0 THEN LET MStep =1 : ENDIF : CALIB_ADC10 = CALIB_ADC10 /25  

  Supply = 65472 // CALIB_ADC10 *8 /CALIB_ADC10  ' 1023 steps x 1024 mV = 1047552 / 16 = 65472
  Supply = 65472 /  CALIB_ADC10 *8 +supply
 
   CALIB_ADC10 = CALIB_ADC10 +MStep         ' double resolution by attemping to detect intermediate steps.
  Supply = 65472 // CALIB_ADC10 *8 /CALIB_ADC10 +supply
  Supply = 65472 /  CALIB_ADC10 *8 +supply              
         
 CALIB_ADC10 = CALIB_ADC10 *2 -MStep : Sertxd("ADC11 = ",#CALIB_ADC10,"  ",#Supply," mV  ")     

  supply = supply + 7 /10 ' I used 7mV to round up
  BinToAscii Supply ,D2,D1,D0 : Sertxd("LCD Supply  ",D2,".",D1,D0," V",cr,lf) ' 5.01 V resolution 01
Are you sure that works (as intended)?

First a general comment: It's arguable (but irrelevant) whether there are 1023 or 1024 steps, but it may well be necessary to scale up "65472" by up to a few percent (the Microchip data sheet specifies 6%) to correct for any error in the FVR value. Therefore in my rather similar code I used scaling factors of 9 (18 overall) instead of 8 (16) to give some "headroom".

Certainly there does seem to be sufficient noise within the PICaxe (at least at higher VDDs) to justify averaging multiple ADC conversions, but 25 seems a little excessive (to possibly improve the resolution by a factor of 2). Of course if there is no significant noise, then it's pointless because the ADC would always return the same value.

Code:
Main:                                                       ' Steps x Vref   / Vsup = CALIBABC10
.....     
MStep = CALIB_ADC10 //25 : IF MStep <>0 THEN LET MStep =1 : ENDIF
It appears that MStep will take the value of 1 for 96% of all the CALIB_ADC10 values. Is this correct?

Code:
CALIB_ADC10 = CALIB_ADC10 /25  
  Supply = 65472 // CALIB_ADC10 *8 /CALIB_ADC10          ' 1023 steps x 1024 mV = 1047552 / 16 = 65472
  Supply = 65472 /  CALIB_ADC10 *8 +supply
The first line appears to throw away any improved resolution that might have been obtained by the multiple READADC10s, Then the other two lines "round up" a result which might be written as: Supply = CALIB_ADC10 / 2 + 65472 // CALIB_ADC10 * 8 (at least if "65472" were a little smaller) ?

Code:
   CALIB_ADC10 = CALIB_ADC10 +MStep         ' double resolution by attemping to detect intermediate steps.
  Supply = 65472 // CALIB_ADC10 *8 /CALIB_ADC10 +supply
  Supply = 65472 /  CALIB_ADC10 *8 +supply                    
........    
  supply = supply + 7 /10           ' I used 7mV to round up
Seems to do much the same as before, with a value 1 larger for 96% of all input values. But I'm not sure about the carry forward of "supply" from the previous stage, nor why: +7 / 10 is used to "round up" and not the usual +5 / 10 ?

I must admit that when I actually tested my similar snippet in a spreadsheet, I was disappointed that there still appeared to be some "two-unit" jumps in the results where they were not expected. It seems that it may not be easy to "cheat" the limited resolution of PICaxe Basic's integer division. Therefore, I now always use a version of my more elaborate subroutine with true double-word division, which can achieve not just a genuine two deciimal places of resolution, but three!

Cheers, Alan.
 

marks

Senior Member
#4
A few years ago I did read a good micro book I think it had a K in micro ,it did influence me a little.
I dare not mention the year. In short it suggested to first use 1023!
Step 1 was described as code 0 and could be as high as 1 mV
Step 1024 was described as code 1023 and could be as high as 1024 mV

In a perfect world all steps are equal and the slope is a straight line ,however that was not his view.
He described the most accurate to be the mid to upper region is where we should design our use.
There could be slight inaccuracies at the extreme lowest and highest values and some individual steps along the slope
with offset and many other mentioned errors. With higher precision A / D Convertors code 0 actually only being half of a step.
After reading much of this he almost had me believing it had been calibrated towards 1023 . showing Fullscale = 2n-1
on top of this there is also a gain error which increases the slope again.lol

I dont pretend to have any precision equipment just a Certified Fluke187 which reads 5.0000 V
Using a 18M2 for testing annoyingly have concluded that at 5v this is a Mid Transition Point,which can be calculated 52378 /209.5 x20 =5000 mV.
When using CALIBADC10 result of 209 we can calculate its value to be 5012 mV but because of our 16bit maths our result is only 5000 mV.
52378 //CALIBADC10 *20 /CALIBRADC10 +supply may look a bit clumsy but its our 16bit remainder of 0012 mV which we add back to our result.
Code:
 #picaxe 18m2  '  AXE132 8Bit marks
 #no_data
 #terminal 19200
 SETFREQ M16
 dirsB = %11111111
 dirsC = %11001111 
	SYMBOL CALIB_ADC10 = W1
	SYMBOL Supply      = W2  :  SYMBOL Value = W2
     		
Main:                                           ' Value x Vref   / CALIBABC10 = Supply
                                                '  1023 x 1.024v /209   = (5.012v)
                                                '  1023 x 1.024v /209.5 = (5.000v)
      CALIBADC10 Value  :  CALIB_ADC10 = Value  '  1023 x 1.024v /210   = (4.988v)
                                                '  1023 x 1.024v /511   = (2.050v)
                                                '  1023 x 1.024v /511.5 = (2.048v)
                                                '  1023 x 1.024v /512   = (2.046v)
      Supply = 52378 // CALIB_ADC10 *20 /CALIB_ADC10 '  1023 x 1024 mV = 1047552 / 20 = 52377.6
    Supply = 52378 /  CALIB_ADC10 *20   +supply     
   Sertxd("CALIBADC10 = ",#CALIB_ADC10,"    Supply ",#Supply," mV",CR,LF)     
 
  Pause 2000
    GOTO Main
If we run our code and set our supply as close we can to 5.012V we should see a value of 209 and 5012 mV.
and if we set our supply to 4.988V we should get a value of 210 and 4988 mV these precision points are called centre of code.
if our results vary by more than 1 step it is likely better decoupling is needed.The Mid Transition point will be found between these values
mine was at about 5.0040V ,it can be quite narrow we should see this change back and fourth from 209 too 210.
 
Last edited:

marks

Senior Member
#5
In the past I have used averaging with 25 being the magic number and 20 giving good results too.!
I have rewritten the code slightly from the above post#2 but is doing the same.
I was generous using 25 readings but believe it can be difficult to detect these transition points
maybe my supply is too clean but I do prefer this when using the A / D converters.
IF we /25 it usually selects the lower value so 1 or more at 209 =209
+24/25 selects the higher value so if 1 or more at 210 =210
In both cases if all 25 values are the same 209 we will get 209 in both cases.

If we use 65472 yes we get a slightly more accurate result multiplying by 8 is just shifting our value
where as when we multiply by 10 there is a chance of slight rounding errors also,but the biggest differrence is
adding in the remainder.

It is worth noting at 2v we are getting 2mV resolution ,but at the higher value 5v it is 12mV .
and at 5.34v it is about 14mv this is why I added the 7mV to the result, this also
seems to provide good accuracy from 2.00 to 5.35 V using 2 decimal places which is enough for me !
Code:
#picaxe 18m2  '  AXE132 8Bit marks
 #no_data
 #terminal 19200
 SETFREQ M16
 dirsB = %11111111
 dirsC = %11001111 

      SYMBOL avr         = b0
	SYMBOL CALIB_ADC10 = W1
	SYMBOL Supply      = W2  :  SYMBOL Value = W2 
	SYMBOL MStep       = W3     
Main:                                                       ' Value x Vref   / CALIBABC10 = Supply
     CALIB_ADC10 = 0                                        '  1023 x 1.024v /209   = (5.012v) 
       FOR avr = 1 TO 25                                    '  1023 x 1.024v /209.5 = (5.000v)
      CALIBADC10 Value  : CALIB_ADC10 = CALIB_ADC10 +Value  '  1023 x 1.024v /210   = (4.988v)  	
       NEXT avr                                             '  1023 x 1.024v /511   = (2.050v)
                                                            '  1023 x 1.024v /512   = (2.046v)       
MStep = CALIB_ADC10 +24/25  
CALIB_ADC10 = CALIB_ADC10 /25  

  'Supply = 52378 // CALIB_ADC10 *10 /CALIB_ADC10        ' 1023 steps x 1024 mV = 1047552 / 20 = 52377.6
  'Supply = 52378 /  CALIB_ADC10 *10 +supply 
  'Supply = 52378 // MStep *10 /MStep +supply        ' double resolution by attemping to detect intermediate steps.
  'Supply = 52378 /  MStep *10 +supply  
  
  Supply = 65472 // CALIB_ADC10 *8 /CALIB_ADC10        ' 1023 steps x 1024 mV = 1047552 / 16 = 65472
  Supply = 65472 /  CALIB_ADC10 *8 +supply 
  Supply = 65472 // MStep *8 /MStep +supply        ' double resolution by attemping to detect intermediate steps.
  Supply = 65472 /  MStep *8 +supply  
           
CALIB_ADC10 =CALIB_ADC10+MStep/2 : Mstep =Mstep-CALIB_ADC10*5 : Sertxd("ADC10 = ",#CALIB_ADC10,".",#Mstep,"  ",#Supply," mV  ",CR,LF)     

Pause 2000
    GOTO Main
 

hippy

Technical Support
Staff member
#6
I am not sure why you are using 25 readings when that's an odd number so will never give an even chance of being read higher and lower if at a transition point. I would have used a power of two, and you can go up to 64 samples before overflowing.

The theory is sound; if 0V-1V reads %00, 1V-2V reads %01, if one reads %00 more often than %01 one can say it's more likely between 0.5V-1.0V than between 1.0V-1.5V. That effectively gains an extra bit of resolution in the ADC/CALIBADC reading. Read 64 samples, don't divide or average, and one instantly adds 6 extra bits of resolution.

I am not sure this actually works in practice, that inherent noise is enough to get a more accurate reading or higher resolution, but let's assume that it does.

Then it's just a case of changing from the 10-bit Nref calculation of -

Vpsu = 1024mV * 1023 / Nref10

to one using 16 bits and 64 samples -

Vpsu = 1024mV * 1023 * 64 / Nref16

As before, the challenge is in trying to divide a larger than 16-bit number by a 16-bit number.

That cannot be done with a simple PICAXE "/" division, not without having to divide down top and bottom, which brings in rounding errors and is what loses accuracy. Doing the division some other way should get it as accurate and with highest resolution as possible.

Of course, all this assumes the 1.024V reference is accurate, when it could have +/-6% error.
 

AllyCat

Senior Member
#7
Hi,

....In short it suggested to first use 1023!
Thanks for your replies. The Microchip data sheet indicates that the ADC has a "half step" at the start and "one and a half" at the end, but it's not exactly a precise diagram since the "centre line" doesn't pass through the origin. :confused:

ADCtransfercharacteristic.png

He described the most accurate to be the mid to upper region is where we should design our use.
So it's rather unfortunate that PICaxe uses only FVR1024, or 20% of VDD = 5 volts. I have written code to use FVR2048, but it uses rather a lot of SFR POKEs and PEEKs.

However, as I said in #3, the exact number of levels is largely (or completely? ) irrelevant because the PIC(axe)'s FVR (and perhaps the ADC itself) just isn't accurate enough for it to matter. The FVR parameters from the same data sheet are quite revealing; in addition to the 6% tolerance, the tempco is more than 5% of the tempco of the "temperature sensing" diodes! and the Line Regulation units of "%/V" are interesting because that implies a square law (i.e. non-linear) component which amounts to more than 10 mV at 5 volts.

FVRcharacteristtics.png

So I'm really not expecting great precision, but I would like the changes in the voltage to be reported with no "missing levels", i.e. in single "10mV steps". However, that's not possible at 5 volts, when the CALIBADC10 steps are as large as 1/210 or ~0.5%. IMHO that requires enhancements to both the CALIBADC10 value (e.g. using a higher FVR level and/or accumulating a "noisy" signal) and to the PICaxe's 16-bit (numerator) division calculation. That can be done easily with my more elaborate CALIBADCxx and double-word divison subroutines, but I'm still looking for an optimum "two decimal places" method.

Yes hippy, as discussed from about #11 in this recent thread, the internal noise can increase the resolution a little (perhaps by 1 or 2 bits) at higher VDDs, but reduces considerably at lower voltages. I'm in the process of documenting my "test rig" for the finished projects section.

Cheers, Alan.
 

marks

Senior Member
#8
I was helping a friend sort a problem today, he had a Brymen BM202 Multimeter
when using it noticed the next scale over 2.5v only has two decimal places @ 5.00V
I told him a little PICAXE-18M2 can do a better job and when comparing to my fluke
his was out by 0.01 and quite often hunt between values.
This made me think perhaps not everyone can easily verify their own results,we have talked
of accuracy and missing decimal positions and I guess I should table some of my own results
so members can at least make a quick comparison ,as I've found it can also be tedious lol.

I,m familiar with the code in post #2 and previous testing did prove I could obtain all TransitionPoints
down to 1.91 V So I'll reavalidate some of that, between 5.36v and 4.75v
below 4.75 v the the resolution is 10mV we should not loose any places.
The additional 0.01 uF was removed as I dont think any one will bother with that,I did notice
the detection of Transition Points were better at 5v noise had doubled, when checking the lower values
I noticed at about 2.05v the value was ment to report 1024 but was hunting between 1023 1024 1025 which
doesnt matter as the resolution there is 2mV and wont worry our two decimal places.

I should point out the raw code values never change ,the supply is adjusted down until one is steady
and record the result on the Fluke( I dont actually bother to find centre of code value that would be to tedious)
I sure any one who's done this knows it not as easy as it sounds as voltage adjustment can jump when changing.

Maybe try hippy's suggestion of averaging at a latertime for the reasons he mentioned I vered away from that.
I have already thought of a few ways to pickup those mising places, it just a matter of finding the time lol
even these results have taken me all night.
 

Attachments

Top