Question about cnt
Sapphire
Posts: 496
in Propeller 1
I have a need to accurately measure a period of time around 60 second. I know at 80MHz cnt rolls over just past 53 seconds, so I thought I'd try running at 40MHz in hopes that I would have up to 107 seconds, which would be more than enough. But it isn't working. If the period of the input signal is more that 53 seconds, I get incorrect time measurements. Interestingly, at 40MHz I tried a simple waitcnt(cnt) and indeed the cog stopped for 107 seconds. But in my test code below where I wait for 60 seconds, the time calculation is not correct. After the first iteration, I always get 47372ms. If I change the last line to wait for less than 53 seconds, it gives the correct result. Can someone explain what's happening?
CON _clkfreq = 40_000_000 ' clock frequency MHz _clkmode = xtal1 + pll8x ' standard clock mode mSec = _clkfreq/1_000 ' mSec VAR long now,past OBJ fds : "FullDuplexSerial" PUB main fds.Start(31,30,%0000,115200) waitcnt(clkfreq*2 + cnt) ' two second pause fds.tx(16) ' Clear screen repeat past := now now := cnt fds.dec(||(now-past)/mSec) fds.tx(13) waitcnt(clkfreq*60 + cnt) ' 60 second pause
Comments
BTW, you can't do an inline delay of more than ~53 seconds at 80MHz -- you run into the same 32-bit resolution of the cnt register.
Note: By using the differential between two points you can measure multiple events simultaneously using the strategy illustrated above (you'll need a variable for each event). If you only have one event you can start the timer by setting teh global variable millis to zero, then reading it at the end of your event.
Thanks for the code sample. I think posx is what I overlooked. At 20MHz, I can measure a 60 second period.
Pub Ticks(Pin) : Microseconds | cnt1, cnt2
Dira [Pin]~
waitpne(0, |< Pin,0)
Cnt1 :=cnt
waitpne (0, |< Pin,0)
Cnt2 := cnt
Microseconds :=(||(cnt1-cnt2) / (clkfreq / 1_000_000))>>1
Pub Time(Pin) : Dis
Dis := Milt(Pin)/10
The above is my ping2 Object
Then my in my main object
Var
Long range,time
Obj
LCD : "debug_lcd"
Ping : "ping2"
Pub
Repeat
Range :=ping.time(ping_pin)
Range:=range-555
LCD.decf(range/10,3)
LCD.putc(".")
LCD.decx(range//10,1)
waitcnt(clkfreq/10+cnt)
I left out the details about the LCD , con clkmode +pll16x xinfreq 5_000_000
So in your case (in a nutshell...), you can measure the offset (in 80MHz tics) between the 1pps and the start of your period, and again against the 1pps and the stop of your period. Even periods of hours or days can be measure this way and with great accuracy.
Even better accuracy can be obtained with a bit more software sophistication and across more time. Pretty soon you've created your own Trimble Thunderbolt.
Wait one second, sixty times
Why not take the delay time in seconds, divide by 32 (shift right if an integer),
and wait units of 32 seconds, then wait for the remainder. The error is only
per 32 seconds.
You can also correct for the error by trial and error (say wait repeatedly for 1ms,
toggling a pin and measure the pin frequency error (should be 500Hz), while changing
a fiddle factor that is subtracted from the counter target value.
Actually you run into trouble at half this time. Since the Propeller treats the 32-bit value as a signed long, the now - before calculation will give you bad results once half the roll over time has elapsed.
You could use the full roll over time if you treated the numbers as unsigned longs. I believe Phil has an object which allows unsigned math.
You could also watch for the negative value of the now - before calculation and use a different method to calculate the time from the two values.
Of course it's easy to count seconds or milliseconds as others have suggested.
-Phil
I did the same thing for my example, but in milliseconds so I could validate the resolution of the timer (it worked).
I don't think this needs a resolution of 25ns, so you can shift the CNT value so that it fit into the positive range of a 32bit variable.
This should work (still 50ns resolution!):
Andy
If you're not using the counters and you have a spare pin you can create a free-running timer without using a cog (or funky cnt register math).
The current time (ms) will be in phsa. If you're just timing one event, you can write 0 to phsa to reset the timer. If you want to have multimple running timers, do this:
Since the events are successive, and the end of one is the beginning of the next, I can use:
to read it and reset it at the same instant (even though I know you don't like ~). It works in your first example, which I have running now.
I have to read up on how you got the oscillator frequency. Why $8000_0000 and later << 1? The clkfreq/1000 part I get.
1) They are not obvious to coders new to Spin
2) They are slower than direct assignments -- which are obvious to anyone
Yes, I have run a test to prove point 2.
phsa~ takes 416 ticks
phsa := 0 takes 368 ticks (faster and clearer to other coders)
Here's the code (the - 544 is to remove the Spin overhead for the assignment operators)
To do the calculation for frqx you need 2^32 which Spin sees as 0. By expressing 2^32 as $8000_0000 << 1 and rearranging the order of things, the problem is averted.
For the record, I didn't come up with this solution (I think it was Beau Schwabe), but jumped right on it when it was presented.
elapsed := phsa
phsa :=0
does take longer and leaves a bigger gap between reading and restarting than
elapsed := phsa~
Do you have a trick for that?
There will be exceptional cases, and this is one. In general, however, I think it's wisest to use code that is obvious. But then, I'm just an actor who writes code to keep a roof over his head until stardom calls.
I find the post ~ and ~~ confusing too, but thought in this case reading and clearing was the way to minimize lost time. I try to avoid them and it's interesting to know they take longer. It would seem that they should compile into a simpler instruction, but I guess not!
Post ~ and ~~ certainly do compile into simpler instructions - in fact, PNut has special versions of its assignment operator just for them. I don't believe that they're slower. Tracy Allen says they're faster.
(Sorry if I misunderstood your post.)
I was commenting on Jon's code that showed phsa := 0 is faster than phsa~. You're saying phsa~ should be faster?
In my application I want to read and clear as fast as possible, so using is necessary.
elapsed := phsa := 0
do the same?
Mike
-Phil