what's your best pasm code for a gray code quadrature encoder?
Alex.Stanfield
Posts: 198
Has anyone a compact pasm code to sense direction and step on a 2 bit gray code encoder?
Is there anything better than converting to binary and substracting fron the last value?
Thanks in advance
Alex
Is there anything better than converting to binary and substracting fron the last value?
Thanks in advance
Alex
Comments
For those speed demons, I've thought that a small PLD can clock two counters, and the difference is the location.
I think that needs 3 channels, two as counters, and one to generate the PLD state engine clock.
Edit : The generic design would need 3 Counter, but I think the Prop allows this to shrink to 2 Counters (1 COG) if you use
CTRMODE %01011 POSEDGE detector w/ feedback INC@[A1 & !A2] BPIN: !A1
- those BPIN sigs then drive the PLD as ack-style handshakes, which can create a ack-clk, to advance the pld quad phase lock.
That uses 4 pins per Quad Counter : CLK_U, CLK_D, QFBn_U, QFBn_D
It may be possible to drop that to 3, if we flip to
CTRMODE %01111 NEGEDGE detector w/ feedback INC @[!A1 & A2] BPIN: !A1
- and map both BPIN to the same IO, and use the OR feature ?
Pins are now : CLK_U, CLK_D, PINB == (QFBn_U OR QFBn_D)
I'm guessing here that two CTRs can drive (OR) a single pin in FB mode ?
if CLKin is mostly HI, FB out will be mostly low, allowing OR to work.
Here, 2 Quad 'support' channels can fit into a SPLD, and you need 1 COG Counter pair, per HW Quad supported.
The PLD does the edge conditioning, and Phase-follow, and the Prop does the 32 bit dual (difference) counting. Maximum Quad Speed should be >20MHz-40MHz region.
Spinning Up Fun With Encoders
Duane J
Thanks for the idea, anyway the counters are used in the cog I'll use.
Alex
Thanks Duane, I bowsed the doc however I found it too verbose and inefficient.
I found on another site the clue I was looking for:
- After the usual step detection your direction is given by (pcode):
If New(A) == Old(B) then going down
else going up
The following example (untested) should do. Whenever I find a little time to hook up the test I'll try it out.
It's so simple I just can't imagine something better. :cool:
If you'd like to say that you are going UP instead of DOWN for New(A) == Old(B) then just swap the test pattern on the test instructions.
Alex
The classic Quad system uses 2 old values, and 2 new values to give 16 choices, 4 are hold, 4 are illegal, and 4 each for Up and Dn.
In Change Pseudo code that is usually along the lines of
Your code is asymmetrical. The first conditional is also true if both change; the second, only if B changes and not A. You have to make a decision whether two changes entail a double jump due to an inadequate sampling rate or constitute an illegal condition.
-Phil
Correct, it is merely the counting code, not the error handling needed if you cannot guarantee you polling is faster than the edge rate.
In a fully working system, it is good practice to include code that catches and sticky-flags an illegal condition.
That way you know if you are not having illegal events. I've added that as an expanded example.
You should always catch +/-1step, but If the mechanical system's speed is near the speed limit of you SW loop, you could catch a double jump with: Anyway you shouldn't treat that as a double jump since you can't know for sure in which direction that was (if 2 bits changed then you are exactly at the opposite end of the table not knowing through which path you got there)
Alex
If you want all 4 edges to count properly then I think you you do need more tests, but if you only want one count per 4 edges, then one ==, <> is ok.
Basically you can feed a Quad pair into a CLK.DIRN counter for one count per 4 edges.
There's no need for that once you know the inputs changed (with XOR between new_sample and old_sample)
With a single " IF new(A) == old(B) " you will count correctly on every edge. See the attached spreadsheet.
encoder.xls
Alex
Interesting.
If this was really 100% coverage, my PLD optimizer should give the same reduction.
Here is what the PLD optimizer says, spaced for clarity, note Qfb is old(B), Qia is new(A)
It does not reduce to match your example, and the clue as to why more qualifiers are needed, is in the Illegal case.
Notice two of those illegal branches, pass your single test of " IF new(A) == old(B) "
So the correct coverage is given by first excluding the Illegal case, in a second test
If (Inputs_Changed AND Inputs_Legal) THEN
IF new(A) == old(B) THEN INC() ELSE DEC()
In the case of Prop+PASM, you may be able to use the unique feature that CY is Parity result, as No Change and Illegal are both even parity, and one-only-changed are odd-parity ?
http://forums.parallax.com/showthread.php?89954-quadrature-encoders/page2
That's a brilliant piece of code! -- and from 2007, too, when we were all still learning the ins and outs of the Prop!
-Phil
Graham
-Phil
Exactly! That's what I meant with " once you know the inputs changed (with XOR between new_sample and old_sample)"
Result of current_input XOR previous_input
00
Nothing changed => loop and keep reading
01
1 step detected => PROCESS
10
1 step detected => PROCESS
11
2 steps detected => ILLEGAL
Results of 00 and 11 don't get processed
Results 01 and 10 signal that we received a single step, now we need only to determine it's direction. You go through the single "IF new(A)==old(B) THEN..." for both cases and you get the right direction. (only this last step is modeled in the excel file above)
Alex
It may be what you meant, but it not what you actually said, or what your code does.
You need to re-word that test to be
once you know the inputs legally changed
but the 11 (both changed) really does need to be caught, before you apply the "IF new(A)==old(B) THEN" test.
I'm not sure I'd call that "does not get processed" ?
If you fail to test/catch that case, your INC/DEC coverage is different from a classic Quadrature design, in that you will (wrongly) change on an illegal case.
I think in PASM, a IF_Z_OR_NC test is able to exit on (NoChange OR Illegal )
Alex
Alex
YES! Thanks for reminding me of NR, I use it so little I always forget it's there. So now we shaved another long. :cool:
On the sum[?] I'm not so sure. The key is to add (or substract) when one bit on the new sample is the same as the other bit of the previous sample. It doesn't matter if it's 0 or 1, just that it be the same. How can we do that with sum[?] ?
Alex
Bit shifting and masking, for me at least, can be hard to follow. I know that this is not native PASM but the PropBASIC compiler that I'm currently evaluating produces PASM and at any time, there should be no
more than approximately 10 instructions executing.
At this point, I didn't bother with the incrementing/decrementing of the counter variable but would like opinions as to whether this looks feasible.
Edit: Just added the counter Inc/Dec stuff.
Cheers!
Mickster
Absolutely COOL :cool:. hand tested modifying the excel above shows it should work. I'll try it tonight.
We keep shaving longs...
I'll post the final tests tonight (I hope)
Alex
If you want to push down in size, a JMP to another JMP never 'looks good', and I like to speed the fastest path, and then use that time for optional 'other stuff'.
eg It is nice to have an (optional?) line that can somehow flag illegal cases, (either toggle a pin, or a sticky pin ) as that can indicate the SW is not keeping up.
This should be possible without increasing the longest path time ?
Roughly sketched :
SW pace issues can sneak up as users add more code to the loop (Chips time sliced COG is going to be great...)
- or they might splice on multiple Quad channels.
Here's an encoder simulator with the PASM code under test and the results seen
Encoder 1 - Alex.spin
Have Fun!
Alex