Picaxe measurement accuracy

Jeremy Harris

Senior Member
In another thread discussing timing accuracy, hippy suggested starting a new thread on pulse width measurement accuracy:
It's possibly you are reading 8% lower than expected but that could be an issue beyond internal oscillator accuracy. That is well outside manufacturer tolerances for the oscillator and far from what we have experienced when testing. Might be worth creating a separate thread to discuss that issue.
which followed on from this observation I'd made:
Another vote for wide timing variability, even with seemingly similar code. I've been measuring this this morning, trying to get a bit of code to execute quickly, and even seemingly small changes have an appreciable effect on execution time. For example, running a simple FOR-NEXT loop to consecutively measure and store pulse widths indirectly into storage memory, with an ordinary variable as the loop counter, seems significantly slower than running the same loop with the byte pointer as the loop counter. The internal resonator and other internal function accuracy isn't great, either, my test programme reads around 8% low on a straightforward pulse width measurement, for example, and I suspect that would vary a bit from chip to chip.
I'm currently pushing the limits a bit by trying to sequentially measure the width of 32 low pulses with just a 275µS high period between each low pulse. I therefore need to execute the code to measure the pulse width, store it, increment the storage address and be ready to measure the next low pulse in less than 275µS. Currently I'm running this on an 08M2, clocked at 32MHz, for two reasons. It seems that this variant might execute instructions very slightly faster than some of the others, plus I'm after a compact solution.

The specification for the pulse train that I'm measuring is published and I've measured the pulse widths on my USB 'scope (a USB Instruments DS1M12 Stingray). My measured pulse widths match those of the spec very closely. For example, the inter-data packet low pulse should be 10mS and I make it exactly 10mS using the measurement cursors on the DS1M12. Similarly, the short data low pulse width (signifying a transmitted 0) should be 275µS and I've measured it at about 278µS and the long data low pulse width should be 1225µS and I make it 1260µS when measured with the 'scope.

A simple loop just measuring the 10mS pulse length with a 32MHz clock speed returns a value of around 7300, or about 9.125mS. I used this measurement to detect the start of a valid data packet and start measuring and storing the pulse widths for the next 32 low pulses, ignoring a 2.6mS low pulse that immediately follows the 10mS inter-packet gap. I get measured pulse lengths returned of around 193µS to 250µS for the pulses that are known to be about 275µS, yet the wider data pulses are measured as between 1218µS and 1258µS when they are known to be around 1260µS.

I suspect that the inaccuracy is more to do with other internal function behaviour rather than resonator accuracy, particularly given the non-linear error.

It's not causing me a problem, BTW, as I can easily work around it. This post is more a way of highlighting this behaviour.
 

hippy

Technical Support
Staff member
PULSIN measures in 10us units at 4MHz, 1.25us units at 32MHz, so a 275us pulse should deliver a result of 220, a 1260us pulse a result of 1008, the 10ms pulse a result of 8000.

The PULSIN timing is done under software control, and there may be slight timing errors in updating the msb rather than the lsb but they should not be significant, a couple of instruction cycles every 256 loops, about 1% perhaps. Your discrepancy seems a lot greater than that.

I'll look to see if we have any tests we can use to compare results or which may reveals what the issue could be.
 

hippy

Technical Support
Staff member
This seems to work for me, timing the high of a 1kHz PWM; "Raw=400, Time=500us" with the odd +/-1 which depends on where the sampling test and rising/falling edge occurs. No major discrepancy though. Pulse measured at just under 501us on the Logic analyser

Code:
#Picaxe 08M2
#No_Data
#Terminal 38400

' Link pin 2 to pin 1 ( leg 5 to leg 6 )

SetFreq M32

PwmOut PWMDIV64, 2, 124, 250 ; 1kHz / 500us high

Do
  PulsIn 1, 1, w0
  w1 = w0 * 125 / 100
  SerTxd( "Raw = ", #w0, 9, "Time = ", #w1, "us", CR, LF )
  Pause 1000
Loop
 

Jeremy Harris

Senior Member
Interesting, thanks for taking the time to look at this.

I wonder if measuring negative going pulses makes a difference?

My code first looks for the 10mS negative going pulse with this snippet:
Code:
detectpacket:
	pulsin c.2,0,pulselength			;look for the 10mS low pulse at start of data packet 
	IF pulselength > 7000 AND pulselength < 8400 THEN GOTO getpacket
	GOTO detectpacket				;detection window is set for a nominal 8,750µS to a nominal 10,500µS (but is really a bit lower)
I had to lower the bottom edge of the detection window to compensate for the apparent error, if I set it as I had it initially (which was for 9.5mS to 10.5mS, or 7600 to 8400) it failed to see the 10mS low pulse.

For the faster pulse sequence (where the errors seem to be lower) the code that's grabbing the pulses is:

Code:
getpacket:
	
	FOR bptr = 28 TO 89				;measure and store pulse widths for 32 consecutive low pulses as fast as possible (1.25µS resolution)
		pulsin c.2,0,pulselength		;note that this loop appears longer than it really is, because of the effective double bptr increment
		@BPTRINC = hibyte			;pulse length word is stored as two consecutive bytes, 64 bytes (32 words)
		@BPTR = lobyte 				;using bptr as the loop counter seems to increase speed (this loop has to execute in less than ~220µS)
	NEXT
 

Jeremy Harris

Senior Member
I've been digging around to try and see if I can get a feel for what's going on here, and have come up with something a bit interesting (and also a bit inexplicable!). I tried just grabbing 200 bytes (100 words) of pulse length data (using a 14M2 to get the additional storage memory), then looking at the 100 captured pulse length words. Rather peculiarly they seem to all be giving pretty accurate results. Here's a sample of the 33 words that represent the initial 10mS start of data packet pulse followed by a data packet, with the word values converted to µS:

Code:
Pulse		Data		Pulse
length		packet		length
count		value		(µS)

8041		START		10051µS
185		0		231µS
194		0		243µS
188		0		235µS
989		1		1236µS
192		0		240µS
991		1		1239µS
194		0		243µS
193		0		241µS
193		0		241µS
190		0		238µS
994		1		1243µS
198		0		248µS
996		1		1245µS
996		1		1245µS
196		0		245µS
198		0		248µS
195		0		244µS
992		1		1240µS
999		1		1249µS
194		0		243µS
989		1		1236µS
199		0		249µS
1000		1		1250µS
196		0		245µS
995		1		1244µS
193		0		241µS
196		0		245µS
1003		1		1254µS
1005		1		1256µS
197		0		246µS
1001		1		1251µS
205		0		256µS
It seems a bit odd that it measures and records pulse width fairly accurately (albeit with a little bit of an under-read on the shorter pulse lengths) but seems not to measure that accurately when I'm trying to detect the wide pulse at the start. Another slightly odd thing is that it's missing the 2.6mS pulse that follows the 10mS gap between data packets (see 'scope snapshot below).
Data packet.jpg

I can't see a logical reason for this pulse not being recorded when all the others are. The positive going pulse between the 10mS low and the 2.6mS low is the same duration as all the other positive pulses, around 275µS
 

Jeremy Harris

Senior Member
Shouldn't that FOR have a "STEP 2" ?

No; figured out what it's doing !
He He! That's why I added the comment, because I had a pretty good feeling that looking at this code again in a month or two I'd have forgotten about the extra increment that's sneaked in there!
 

AllyCat

Senior Member
Another slightly odd thing is that it's missing the 2.6mS pulse that follows the 10mS gap between data packets ...

I can't see a logical reason for this pulse not being recorded when all the others are. The positive going pulse between the 10mS low and the 2.6mS low is the same duration as all the other positive pulses, around 275µS
Hi Jeremy,

Are you just overlooking the fact that ALL PICaxe instructions take quite a significant time to execute? For example, I think

IF pulselength > 7000 AND pulselength < 8400 THEN GOTO getpacket

will probably take at least 200us to execute even with a 32 MHz clock (in particular, all basic jumps appear to take at least 1,000 PIC instruction cycles in M2 devices). Add to that the times taken to "fall out" from the PULSIN, setting up the FOR ... loop and entering the next PULSIN and I would expect the positive pulse to have already finished, so the instruction will have to wait for the next negative edge (after the next positive edge).

Have you seen this thread? Note that my measurements described in #12 show the M2 instructions (particularly GOTOs and RETURNs) to be significantly slower (for a given clock speed) than the previous devices described by westaust55.

Cheers, Alan.
 

Jeremy Harris

Senior Member
Yes, I'd read the compilation of executions times, thanks.

The odd thing with missing the 2.6mS pulse is that I wasn't using the "detect the start of the data packet" routine. For this test I just read 200 consecutive bytes (100 words) into memory using the earlier quoted bit of code:

Code:
FOR bptr = 28 TO 228				
	pulsin c.2,0,pulselength		
	@BPTRINC = hibyte			
	@BPTR = lobyte 	
NEXT
then looked at the stored values in "slow" time to read them out of the serial port.

AFAICS, the code above should measure all the low pulses at the input, and it certainly does always read the 10mS inter-packet low pulse and all of the 32 data bits accurately (every test I've done has given 100% accuracy on reading those bits). For whatever reason, this simple measure-and-store routine misses the 2.6mS low pulse that follows the 10mS low pulse every single time. I can't quite get my head around why that should be.
 

hippy

Technical Support
Staff member
For whatever reason, this simple measure-and-store routine misses the 2.6mS low pulse that follows the 10mS low pulse every single time. I can't quite get my head around why that should be.
If the 10ms is caught then the 2.6ms should be too. The only reason I can think is if the time between the end of 10ms and start of 2.6ms were too short to have got into the next PULSIN.

It could be that the gap is right on the cusp of not working right and the gap between 10ms and 2.6ms is just ever so slightly too short.

You could try unrolling the FOR-NEXT so it is a long sequence of PULSIN, @BPTRINC=, @BPTRINC=, PULSIN which will capture shorter gaps, and/or try a 20X2 which can use 64MHz.
 

AllyCat

Senior Member
Hi Jeremy,

Could it be something to do with that you appear to be storing the High byte first, whereas PICaxe basic uses Low byte first?

But I like hippy's idea, you only meed to unroll the first pass or two, to test the point.

Cheers, Alan.
 

Jeremy Harris

Senior Member
I think you're right, hippy, and it's been worrying me slightly, as it implies that the thing might be working right on the absolute edge of failing. I can't measure a difference on the 'scope between the first positive going pulse width (after the 10mS low pulse) and any of the others, but my USB 'scope doesn't have enough accuracy or resolution to detect really small differences.

I think tomorrow morning's exercise will be to do as you suggest and just create a long list of instructions to read in the pulse widths. It'll look clunky, but my guess is that it should be quite fast, as it seems that using the byte pointer to increment from one memory location to the next is about as fast an operation as a Picaxe can manage,
 

Jeremy Harris

Senior Member
Hi Jeremy,

Could it be something to do with that you appear to be storing the High byte first, whereas PICaxe basic uses Low byte first?

But I like hippy's idea, you only meed to unroll the first pass or two, to test the point.

Cheers, Alan.
I'll certainly give that a try, Alan. I think hippy's idea is also worth checking, though, as I don't like the idea of this working right on the edge of failure.

It's at times like this that it would be nice to be able to insert a short bit of assembly language into a Picaxe basic programme.
 

AllyCat

Senior Member
.. the thing might be working right on the absolute edge of failing...
Hi Jeremy,

If it's that marginal, you can try raising the clock frequency a little using calibfreq 31 to see if it fixes the issue (or lower the frequency to see if it makes matters worse).

I believe the PICaxe manual may still be out of date (and the base PIC datasheet doesn't appear to specify the OSCTUNE sensitivity). With M2 devices, the incremental steps appear to be about 0.1% each, but you can use values up to 31 (highest frequency) and down to -32 ($E0) within PICaxe Basic and probably still not wreck the serial comms.

Cheers, Alan.
 

Jeremy Harris

Senior Member
Well, I've decided to try out the "brute force" method hippy suggested this evening, as curiosity got the better of me. This works fine and collects every pulse width accurately, so that seems to indicate that it was, indeed, running right on the edge before. Removing the FOR-NEXT loops and just running a long list of pulsin - store instructions measures the pulse widths accurately and captures the missing 2.6mS second pulse every time. My guess is that the execution time of the original loop at 32MHz was very close to the 275µS available, close enough for it to fall over if the positive pulse length was just fractionally shorter.

I have plenty of spare programme memory, so I'll stick with the rather clunky method. I think I'll also test the robustness of this by seeing if it's affected by tweaking the clock speed or cooling the chip down (a squirt of cooling spray should shift the resonator frequency down a good few percent. I've checked the "clunky method" at 16MHz and the same error appears that I had with the FOR-NEXT loop, so I know that it's not massively robust................
 

Jeremy Harris

Senior Member
Just to close this by confirming that the "clunky" method (no loops, just a long repeated sequence of pulsin's and store direct to memory instructions) is robust. I now have the finished code reliably detecting the data packets from a HomeEasy dual wall switch, with no errors at all. I've tested the tolerance (rather crudely) by warming the Picaxe 14M2 up with a soldering iron held on its back and with a good squirt of freezer spray and it still seems to work reliably. The encoding scheme these switches use is pretty robust, as I have an outdoor weather transmitter that pings out data every 5 seconds as an unecoded low baud rate transmission and even that doesn't cause the slightest problem with the received signals from the HomeEasy transmitter that's working on the same frequency.

Thanks for the help in tracking down this rather interesting little problem.
 

hippy

Technical Support
Staff member
My guess is that the execution time of the original loop at 32MHz was very close to the 275µS available, close enough for it to fall over if the positive pulse length was just fractionally shorter.
That does seem to be the case. I rewrote my test code which approximately takes the same time as your original FOR, PULSIN, @BPTRINC=, @BPTR=, NEXT code ...

Code:
#Picaxe 08M2
SetFreq M32
PwmOut PWMDIV64, 2, 124, 250 ; 1kHz / 500us high
Do
  For b13 = 0 To 200
    PulsOut 0, 1
    PulsIn 1, 1, w0
    @bPtr = b0
  Next
  Pause 1000
Loop
Looking at the logic analyser trace, from the falling edge of the pulse to the pulse at the top is about 320us, so that's roughly how long it takes from one pulse to being ready for the next. There will be variations because it's different commands, placement in memory will be different but it's in that right ball park. It could be you were lucky you got anything at all !

Putting a PULSOUT after the PULSIN gives about 75us if you could have sequential PULSIN's, nothing else.

PULSIN, @BPTRINC=, @BPTRINC=, PULSOUT gives about 200us.

You could capture the 10ms, check it is in range, forego the 2.6ms pulse and capture all the rest sequentially - if that's not what you are doing already.
 

Attachments

Jeremy Harris

Senior Member
You could capture the 10ms, check it is in range, forego the 2.6ms pulse and capture all the rest sequentially - if that's not what you are doing already.
Great minds think alike, as that's what I'm now doing!

I added a small delay to make sure that the code that detects the 10mS low pulse always ends around the middle of the 2.6mS low pulse, which then makes for a nice and reliable way of doing this.
 

hippy

Technical Support
Staff member
Seems you've got it sorted then. Excellent work.

Did you ever get to the source of the apparent missing 10% in timing ? I suppose more importantly; are the times measured now similar to what they ought to be ? Not that it really matters so long as you can distinguish short from long.
 

Jeremy Harris

Senior Member
For some odd reason the timing accuracy error only seemed to occur with the original FOR-NEXT loop. Once I switched to just reading the long sequence of pulsin's as a bit of linear code the errors went away and the measured times now are pretty close (within 1 or 2%) of those measured with the 'scope. I can't for the life of me see why this should be, but can only assume that the original errors were a function of things running right on the very edge of failure.

Once I've tidied the code up (well, relatively speaking - it's a lot of rather clunky code to do a seemingly simple task!) I'll post it in the finished projects section. As these HomeEasy transmitters are cheap and readily available (even B&Q sell them) this may make for a useful way to implement fairly well encoded RF remote control.

Not really good enough for really secure tasks (it's only 32 bits, with 30 bits being used for unique device ID), but probably good enough for many remote control tasks. Having said that, I suspect even 30 bits is a fair bit better than many ordinary household mechanical locks by a fair margin.
 

DamonHD

Senior Member
Sounds good: just the sort of stuff that might be worth scooping up a copy of for my OpenTRV project, since I think interoperability with home automation systems is a common requirement.

Rgds

Damon
 
Top