Let's talk pin jitter with the counters ...
turbosupra
Posts: 1,088
So I've been trying to read a frequency in PASM and I've tried three different ways. The first way does not give any pin jitter, and by that I mean stray cycles that show up that are far less than what the frequency should be, usually less than 100. The second and third way both give pin jitter and will sometimes output < 100 cycles during a loop. I tried a pull down resistor to make sure it wasn't hardware related (although I'm seeing the values on both highs and lows), so I'm pretty confident it isn't stray signals based on that.
I've also tried throwing a waitcnt after the program verifies that the value stored in the phsX is not 0, that did not help. Now the jitter reads waitcnt + <100, which is very confusing.
Another potential cause could be a read/write issue between PASM and the pst/spin and that I've been chasing my tail for hours on this. Maybe I need some sort of lock?
Or maybe this is just something that cannot be fixed and has to be filtered via software. Can anyone elaborate? I'm looking for lessons from those who have more experience than I, which is probably just about everyone here
Thanks for reading!
[edit]
While writing this post, I had an idea, I altered the 2nd code block to what the 4th code block looks like and it appears to be working. In short, I filter any value under 200, clear it and ignore it. Is this the right way to go about this? Or a wise way to hand it?
[edit]Working version
I've also tried throwing a waitcnt after the program verifies that the value stored in the phsX is not 0, that did not help. Now the jitter reads waitcnt + <100, which is very confusing.
Another potential cause could be a read/write issue between PASM and the pst/spin and that I've been chasing my tail for hours on this. Maybe I need some sort of lock?
Or maybe this is just something that cannot be fixed and has to be filtered via software. Can anyone elaborate? I'm looking for lessons from those who have more experience than I, which is probably just about everyone here
Thanks for reading!
[edit]
While writing this post, I had an idea, I altered the 2nd code block to what the 4th code block looks like and it appears to be working. In short, I filter any value under 200, clear it and ignore it. Is this the right way to go about this? Or a wise way to hand it?
' looks for equality between pinMask and pin state to copy phsb to lowCycles waitpeq pinMask, pinMask ' compares pinMask to ina condition of pinMask mov lowCycles, phsb ' copy low cycles counter to lowCycles mov phsb, #0 ' clear low cycles counter wrlong zero, pstPtr13 ' write to pointer to show pin state oscillation wrlong lowCycles, pstPtr12 ' write lowCycles to pointer
' test for high, if high c = 1, then copy low cycles test pinMask, ina WC, WZ ' test for high, and if high if_nc {if not high} jmp #end ' if not high jump to end if_c {if high} mov phsbT, phsb wz ' write 1 to z if value being copied is a 0, copy low cycles if_z {if value = 0} jmp #end ' if it is a 0, jump if_nz mov cntT, fiveThouCycles if_nz add cntT, cnt if_nz waitcnt cntT, #0 if_nz {if value != 0} mov lowCycles, phsb ' copy low cycles counter to lowCycles if_nz {if value != 0} mov phsb, #0 ' clear low cycles counter if_nz {if value != 0} wrlong zero, pstPtr13 ' write to pointer to show pin state oscillation if_nz {if value != 0} wrlong lowCycles, pstPtr12 ' write lowCycles to pointer
mov ina, ina and ina, pinMask wz ' if z=1, pin is low if_nz {if not low} jmp #end ' if not low jump to high if_z {if low} mov phsbaT, phsb wz ' write 1 to z if value being copied is a 0, copy high cycles if_z {if value = 0} jmp #end ' if it is a 0, jump if_nz mov cntT, twoHunCycles if_nz add cntT, cnt if_nz waitcnt cntT, #0 if_nz {if value != 0} mov lowCycles, phsa ' copy high cycles counter to highCycles if_nz {if value != 0} mov phsb, #0 ' clear high cycles counter if_nz {if value != 0} wrlong one, pstPtr13 ' write to pointer to show pin state oscillation if_nz {if value != 0} wrlong lowCycles, pstPtr11 ' write highCycles to pointer
[edit]Working version
' test for high, if high c = 1, then copy low cycles test pinMask, ina WC, WZ ' test for high, and if high if_nc {if not high} jmp #end ' if not high jump to end if_c {if high} mov phsbT, phsb wz ' write 1 to z if value being copied is a 0, copy low cycles if_z {if value = 0} jmp #end ' jump out of loop to end [b]if_nz {if value != 0} cmp phsbT, #200 WC, WZ ' compare phsX value to 200 if_be {if <= 200 jmp} mov phsb, #0 ' clear and ignore value if less than 201 if_be {if <= 200 jmp} jmp #end ' if it is below 201, jump out of loop to end[/b] if_a {if value != 0} mov lowCycles, phsb ' copy low cycles counter to lowCycles if_a {if value != 0} mov phsb, #0 ' clear low cycles counter if_a {if value != 0} wrlong zero, pstPtr13 ' write to pointer to show pin state oscillation if_a {if value != 0} wrlong lowCycles, pstPtr12 ' write lowCycles to pointer
Comments
I'm not following this - do you mean 100 counts low, or a value of 100 ?
It is not clear what your update rate is, or how many digits of precision you want ?
There are three basic methods to measure frequencies (Fu) :
a) Measure Fu Period, and invert. (You can expand to choose how many cycles).
b) Use a gate fixed time and count Fu cycles - counter value is direct cycles/gate time
c) Build a reciprocal counter, where you have a nominal gate/update time, but 'snap' that to an exact Fu edge, and then run the reciprocal maths
Fu = (Whole_Cycles)/(Time_Taken)
With low frequencies like you have here, a) or c) usually have the best precision.
c) can auto-scale, and has the highest Digits/Second ability.
Where this fails, though, is when the frequency is very low w.r.t. the amount of time you can allocate for counting edges. When that happens, you have to measure the time between edges and invert to compute a frequency. This method is fraught with issues, however, if the input pulse train is jittery, as many mechanically-derived pulse trains tend to be.
-Phil
Do you have a logic analyzer that you could run against your signal source? That way we can see how clean the source is.
...
or, being that you say the source is a propeller, could you post the code that the source propeller is using to generate your signal?
I would just like the cycles at this point, I have math in my code that can calculate what I need from the number of cycles. My code was actually working well with waitpeq and waitpne, I just cannot use code that pauses the cog anymore.
Hi,
Here is the frequency/counter setup code
Hi,
I have to objects that can generate these frequencies, one that I wrote and one that someone was kind enough to write for me. The waitpeq/waitpne functions work fine if that helps narrow down anything?
Here is the loop from my own code
Do you mean Fu cycles per (undefined) sample time, or Clock Cycles per Fu period ?
Why not ? - you will need some means to lock to edges (aka wait) no matter what you write ?
If you need to 'do other stuff' such that you cannot be certain you have got back in time for the NEXT edge, then drive the Fu into a counter. and check & use that, after a WAITxx. Doing that means you do not lose any information. It will usually be 1 on single-cycle grabs, but if it reads 2 or more, you still know exactly how many cycles have occurred.
This is now getting close to option c) I described above.
If you can tolerate using two pins, you should also be able to use WAITxx to get a true edge
If you count Fu as above, just configure CTRx as POSEDGE detector w/ feedback, and now you have Fu and a 1 SysClk delayed Fu PinB, which will give a Bit-pair result of 00/11 on stable L/H and 01/10 on each edge.
I'm not quite sure what you are asking and you are talking in terms I'm not familiar with, to me the clock cycles define the period, since I have a given of clkfreq. I need to know whether the pin was high for 200000 clock cycles and then low for 200000 clock cycles (200hz) or high for 5714 clock cycles and low for 5714 clock cycles (7000hz), as well as anything in between that range. All I need is the clock cycle count for pin state high and the clock cycle count for pin state low. I only want to update once after the pin is in the opposing state, for example I want to count high clk cycles when the pin transitions to low and count low clk cycles when the pin transitions to high. In my code, I check for pin state, verify the phsX is not 0 and then copy the cycles stored on phsX for the state that is opposite of the current pin state.
I cannot pause the cog as I do need to do other stuff and I need to have a timeout.
I just searched around for information on the POSEDGE with feedback, I'm unfamiliar with that. I didn't find anything so I will keep searching.
Edit, I found this, I don't understand yet what the feedback buys me?
http://www.parallaxsemiconductor.com/sites/default/files/appnotes/AN008-SigmaDeltaADC-v1.0_0.pdf
OK, so you are measuring Period, on a per-Fu-cycle basis. ( note as PhiPi has mentioned, this is jitter prone)
It buys you a true edge wait (by testing two bits), as opposed to a Level only wait (which can fire early, it is not a true edge)
Then the above does not help, as it uses a WAIT.
If you really want to run wait-free, then you have to accept lower precision.
The best you can then do, is configure the Fu to clock a Counter ( just POSEDGE, POSEDGE with feedback not needed) and poll the counter for a change. when it is <> last reading, you are as close to your edge as your loop timing allows.
With true edge precision, (SysCLK & Wait) you capture full cycles as 80e6/11428 = 7000.350Hz, and next is 80e6/11427 = 7000.962Hz etc (.0087%)
Now let's suppose you have a 2us loop - you now only know the edge to 2us granularity, so now your possible frequency readings are
500k/71 = 7042.253Hz, then 500k/72 = 6944.44Hz (etc) (declined to 1.4%)
The faster your polling can be, the better this is, or you may decide you do not need a reading every ~142us, and decide to update at the lowest rate (or some lower rate) if you check every ~5ms, or > 1 count, now your frequency steps are improved (and you have reduced the jitter by averaging at least the higher frequencies)
eg if you read 35 Fu counts, in 2500 2us loops, 35*500k/(2500) = 7000
next is 35 Fu counts in 2499 2us loops, 35*500k/(2499) = 7002.80 & likewise 35*500k/(2498) = 7005.604
At 400Hz, you may get 2 Fu counts, for 2*500k/(2498) = 400.320, then 2*500k/(2499) = 400.16006
(improved to .04%)
Of course, if the Prop had a Capture feature in the Counters, no compromise would be needed.
Hopefully on Prop 2 ?
Thanks for the reply. So are you saying that the way I'm doing it with POSEDGE and the filter that removes anything less than XXX is the best way I can do it?
I don't mind getting the value a few cycles late (lower precision), as long as I get the value before it is overwritten. If my understanding of the counters are correct, I can wait to poll the counter at any point that it is in an opposing state and get the number of cycles from the previous state. If it goes high to low every 10k cycles and then low to high every 10k cycles, I can wait for it to transition from high to low and get the high cycles (while it is low) at [transition +1] cycles or [transition +9999] cycles and it doesn't really matter as the count is stored until the next time that same pin state is reached and the value is overwritten, correct?
My code ends up averaging the last 8 highs or the last 8 lows.
I'm not sure why you do HI/LO - I think you are using
%01000 POS detector mode
when I would instead use
%01010 POSEDGE detector
That halves the work, and guarantees to work on a full cycle, as it is edge based, and gives twice the time-tolerance.
It changes CTR to read Fu cycles, ( as opposed to Gated SysClks), but you can still get SysClk counts from CNT.
You then poll the CTR value, and after it changes, (or after it is +8, as you already average, so let the HW do it for you)
you immediately grab Time (from CNT), and you have 8 cycles in that time.
If you use the actual value >8, then it even tolerates a polling loop so slow, it misses and edge or two.
Because edges are counted in hardware, that does not matter.
What is your polling loop time, and it is precisely paced ?
-Phil
Let me ask you, if I do use POSEDGE, how does that work with giving me the total number of cycles the pin is high? Based on the name, I figured it would only alert as to the moment the rising edge surpassed 1.65v. Is that how it works? Would I then need to detect NEGEDGE and then subtract POSEDGE from NEGEDGE to get the amount of cycles it was high?
All of my math is based on clock cycles. Do you mind giving me a simple example to show me what I should see, I'm having a hard time conceptualizing this.
and
This is the key :
It changes CTR to read Fu cycles, ( as opposed to Gated SysClks), but you can still get SysClk counts from CNT.
Yes, you still need to get SysClks (elapsed time), which you do by reading CNT (once), not your gated CTR pair. (twice)
The CNT you read, is now the time for a WHOLE number of cycles (not half cycles as before)
When coding, you should write with purpose and know -- I mean really know -- exactly what each statement does. Throwing stuff in because it "seems to be working" does nothing to promote your end result and serves only to obfuscate your original intent.
-Phil
Unfortunately for me, I need them separate in this particular project and I do comparisons against the low and high times separately, which are not split equal at 50% high/50% low either.
By the way, the whole "Fu" thing is really throwing me. Is that short for Frequency units or something so I can read it as a word instead of "FU" ?
Anyway, I'd like to really know (I've read the prop manual on ina already), so I'm all ears.
I got that function from someone else's code while searching the forums earlier today at work, unfortunately I do not remember the link or the context now. I only remember that they wrote to the shadow register for a reason, but I forget why.
Thanks,
-Phil
I do have a compilable counter program in reference to this particular thread, as I have a PASM template that I've created and use. I have no problem attaching it to the thread, but am not sure how useful it'll be because it requires that other piece of code on the other prop that generates the frequency.
I believe that ina/inb was left over from a previous snippet of code that I had removed after trying it, and I had just forgotten to remove those lines as well, unfortunately they seemed to have bit me this time and became a point of focus. I don't know how others test code/technique, but with my template it can be used as a child object or a stand alone object and write to the pst and debug. Maybe this is poor form and not granular enough, I'm pretty new so that would not surprise me to hear from someone with much more experience then I.
Yes, if those are captures of SysClks from CNT.
Ah, you did not actually SAY that tho, you asked to "read a frequency"....
If you want to read Low and High times, ( half periods) you will need more gymnastics.
Since this is polled anyway, you may still be able to use hardware to help, (over multiple cycles), if you configure one timer as =\_ edge, and one as gate on High, and also poll-capture CNT (as above) then you get a highly accurate total edge count, and the CTR value Gate on High is also accurate, and the PeriodSysClk-HiSysClks will give you a LoSysClk value (poll accurate).
This still lets you SW capture after some number of edge counts, (8,16, 35 etc) before actually reading the registers, so it gives your code more slack time to do other things.
A true edge count should also make your missing input timeout easier, as no change in edge cont, means there is no frequency input.
Fu = Frequency Unknown - that which is being measured (or Fin, if you prefer?)
As for terminology, rising edge is also gate on high and falling edge is gate on low?
-Phil
I believe I have it already setup for gating then, although I did not know the term before today. The code below is set up to accumulate system clocks to frqa each time the pin is high and frqb each time the pin is low.
When the pin is high, the code checks that the low clock count is not 0 as well as higher than 200 (to eliminate jitter) and copies the low clock count stored on phsb, then finally clears the phsb low count.
When the pin is low, the code checks that the high clock count is not 0 as well as higher than 200 (to eliminate jitter) and copies the high clock count stored on phsa, then finally clears the phsa high count.
Is my logic solid? And my code in line with my logic? I can post the entire code block based on our prior conversation if you would like that instead.
Isn't 0 included in the only take > 200 test ?
If that's the worst part of my code, that's quite a compliment.
On a side note, I did get the timeout and rest of code to work this afternoon