FPGARelated.com
Forums

Problem writing quadrature decoder

Started by Michael April 20, 2008
On Apr 20, 7:12=A0pm, Peter Alfke <al...@sbcglobal.net> wrote:
> Michael, you might prefer my much simpler solution, as described in a > recent blog on the Xilinx forums website. > > http://forums.xilinx.com/xlnx/blog/article?message.uid=3D9394 > > I designed this a few years ago, and we use it in our programmable > frequency gererator, that I have mentioned here a few times. > Ken Chapman then took the exact same shaft encoder for the Spartan > eval board. > The design is absolutely bounce-proof, no Mickey Mouse low-pass > filters or other analog nonsense. > I hope the explanation is sufficient. > > Viel Spa=DF > Peter Alfke
Hi Peter - I didn't see any code attached to the post. Does this look right? signal Q1, Q2 : std_logic; process (A, B) begin if (A =3D '1' and B =3D '1') then Q1 <=3D '1'; elsif (A =3D '0' and B =3D '0') then Q1 <=3D '0'; elsif (A =3D '0' and B =3D '1') then Q2 <=3D '1'; elsif (A =3D '1' and B =3D '0') then Q2 <=3D '0'; end if; D1 <=3D (A and B) or ((A or B) and Q1); D2 <=3D ((not A) and B) or (((not A) or B) and Q2); end process; So then I would take say D1 and call it the clock, and call D2 the direction, and then increment the count on every edge of D1 when D2 was high and decrement it for every D1 edge when D2 was low? At least, that was my understanding from your post. However, I just simulated it by hand (on a piece of paper- haven't figured out how to use a simulator just yet) - and I set it up to have B 90 degrees behind A, and then I found Q1 =3D A, Q2 =3D B, D1 =3D A, and D2= =3D B. So I suspect I'm misunderstanding something... -Michael
On Apr 20, 8:09=A0pm, Michael <nleah...@gmail.com> wrote:
> On Apr 20, 7:12=A0pm, Peter Alfke <al...@sbcglobal.net> wrote: > > > Michael, you might prefer my much simpler solution, as described in a > > recent blog on the Xilinx forums website. > > >http://forums.xilinx.com/xlnx/blog/article?message.uid=3D9394 > > > I designed this a few years ago, and we use it in our programmable > > frequency gererator, that I have mentioned here a few times. > > Ken Chapman then took the exact same shaft encoder for the Spartan > > eval board. > > The design is absolutely bounce-proof, no Mickey Mouse low-pass > > filters or other analog nonsense. > > I hope the explanation is sufficient. > > > Viel Spa=DF > > Peter Alfke > > Hi Peter - I didn't see any code attached to the post. Does this look > right? > > signal Q1, Q2 : std_logic; > process (A, B) > begin > =A0 =A0 =A0 =A0 if (A =3D '1' and B =3D '1') then > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q1 <=3D '1'; > =A0 =A0 =A0 =A0 elsif (A =3D '0' and B =3D '0') then > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q1 <=3D '0'; > =A0 =A0 =A0 =A0 elsif (A =3D '0' and B =3D '1') then > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q2 <=3D '1'; > =A0 =A0 =A0 =A0 elsif (A =3D '1' and B =3D '0') then > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q2 <=3D '0'; > =A0 =A0 =A0 =A0 end if; > > =A0 =A0 =A0 =A0 D1 <=3D (A and B) or ((A or B) and Q1); > =A0 =A0 =A0 =A0 D2 <=3D ((not A) and B) or (((not A) or B) and Q2); > end process; > > So then I would take say D1 and call it the clock, and call D2 the > direction, and then increment the count on every edge of D1 when D2 > was high and decrement it for every D1 edge when D2 was low? At least, > that was my understanding from your post. > > However, I just simulated it by hand (on a piece of paper- haven't > figured out how to use a simulator just yet) - and I set it up to have > B 90 degrees behind A, and then I found Q1 =3D A, Q2 =3D B, D1 =3D A, and =
D2
> =3D B. So I suspect I'm misunderstanding something... > > -Michael
Check it with pencil and paper. Yes, A and B are 90 degree offset, that's why it's called a quadrature decoder. Then check if for a monotonic "friendly" rotation, and you see how it works. The trickery is that it also works for any non-monotonic move. Peter
On Apr 20, 8:14=A0pm, Peter Alfke <al...@sbcglobal.net> wrote:
> On Apr 20, 8:09=A0pm, Michael <nleah...@gmail.com> wrote: > > > > > On Apr 20, 7:12=A0pm, Peter Alfke <al...@sbcglobal.net> wrote: > > > > Michael, you might prefer my much simpler solution, as described in a > > > recent blog on the Xilinx forums website. > > > >http://forums.xilinx.com/xlnx/blog/article?message.uid=3D9394 > > > > I designed this a few years ago, and we use it in our programmable > > > frequency gererator, that I have mentioned here a few times. > > > Ken Chapman then took the exact same shaft encoder for the Spartan > > > eval board. > > > The design is absolutely bounce-proof, no Mickey Mouse low-pass > > > filters or other analog nonsense. > > > I hope the explanation is sufficient. > > > > Viel Spa=DF > > > Peter Alfke > > > Hi Peter - I didn't see any code attached to the post. Does this look > > right? > > > signal Q1, Q2 : std_logic; > > process (A, B) > > begin > > =A0 =A0 =A0 =A0 if (A =3D '1' and B =3D '1') then > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q1 <=3D '1'; > > =A0 =A0 =A0 =A0 elsif (A =3D '0' and B =3D '0') then > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q1 <=3D '0'; > > =A0 =A0 =A0 =A0 elsif (A =3D '0' and B =3D '1') then > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q2 <=3D '1'; > > =A0 =A0 =A0 =A0 elsif (A =3D '1' and B =3D '0') then > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q2 <=3D '0'; > > =A0 =A0 =A0 =A0 end if; > > > =A0 =A0 =A0 =A0 D1 <=3D (A and B) or ((A or B) and Q1); > > =A0 =A0 =A0 =A0 D2 <=3D ((not A) and B) or (((not A) or B) and Q2); > > end process; > > > So then I would take say D1 and call it the clock, and call D2 the > > direction, and then increment the count on every edge of D1 when D2 > > was high and decrement it for every D1 edge when D2 was low? At least, > > that was my understanding from your post. > > > However, I just simulated it by hand (on a piece of paper- haven't > > figured out how to use a simulator just yet) - and I set it up to have > > B 90 degrees behind A, and then I found Q1 =3D A, Q2 =3D B, D1 =3D A, an=
d D2
> > =3D B. So I suspect I'm misunderstanding something... > > > -Michael > > Check it with pencil and paper. > Yes, A and B are 90 degree offset, that's why it's called a quadrature > decoder. > Then check if for a monotonic "friendly" rotation, and you see how it > works. The trickery is that it also works for any non-monotonic move. > Peter
Michael, you start simply by ignoring the clock, just assume that it is always there, and is very fast, so each look-up table output is immediately registered. Then you move A and B, realizing that they are 90 degree offset. Then you just inspect the two Qs, and you see that you can consider one of them (either one) as the outgoing clock, and the other as the direction. Seems trivial, but the beauty is that it also works for any and every kind of irregular operation. It's not "rocket science" but it is simple and clever, if i may say so. Peter
On Apr 20, 11:39=A0pm, Peter Alfke <al...@sbcglobal.net> wrote:
> On Apr 20, 8:14=A0pm, Peter Alfke <al...@sbcglobal.net> wrote: > > > > > > > On Apr 20, 8:09=A0pm, Michael <nleah...@gmail.com> wrote: > > > > On Apr 20, 7:12=A0pm, Peter Alfke <al...@sbcglobal.net> wrote: > > > > > Michael, you might prefer my much simpler solution, as described in =
a
> > > > recent blog on the Xilinx forums website. > > > > >http://forums.xilinx.com/xlnx/blog/article?message.uid=3D9394 > > > > > I designed this a few years ago, and we use it in our programmable > > > > frequency gererator, that I have mentioned here a few times. > > > > Ken Chapman then took the exact same shaft encoder for the Spartan > > > > eval board. > > > > The design is absolutely bounce-proof, no Mickey Mouse low-pass > > > > filters or other analog nonsense. > > > > I hope the explanation is sufficient. > > > > > Viel Spa=DF > > > > Peter Alfke > > > > Hi Peter - I didn't see any code attached to the post. Does this look > > > right? > > > > signal Q1, Q2 : std_logic; > > > process (A, B) > > > begin > > > =A0 =A0 =A0 =A0 if (A =3D '1' and B =3D '1') then > > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q1 <=3D '1'; > > > =A0 =A0 =A0 =A0 elsif (A =3D '0' and B =3D '0') then > > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q1 <=3D '0'; > > > =A0 =A0 =A0 =A0 elsif (A =3D '0' and B =3D '1') then > > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q2 <=3D '1'; > > > =A0 =A0 =A0 =A0 elsif (A =3D '1' and B =3D '0') then > > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q2 <=3D '0'; > > > =A0 =A0 =A0 =A0 end if; > > > > =A0 =A0 =A0 =A0 D1 <=3D (A and B) or ((A or B) and Q1); > > > =A0 =A0 =A0 =A0 D2 <=3D ((not A) and B) or (((not A) or B) and Q2); > > > end process; > > > > So then I would take say D1 and call it the clock, and call D2 the > > > direction, and then increment the count on every edge of D1 when D2 > > > was high and decrement it for every D1 edge when D2 was low? At least,=
> > > that was my understanding from your post. > > > > However, I just simulated it by hand (on a piece of paper- haven't > > > figured out how to use a simulator just yet) - and I set it up to have=
> > > B 90 degrees behind A, and then I found Q1 =3D A, Q2 =3D B, D1 =3D A, =
and D2
> > > =3D B. So I suspect I'm misunderstanding something... > > > > -Michael > > > Check it with pencil and paper. > > Yes, A and B are 90 degree offset, that's why it's called a quadrature > > decoder. > > Then check if for a monotonic "friendly" rotation, and you see how it > > works. The trickery is that it also works for any non-monotonic move. > > Peter > > Michael, > you start simply by ignoring the clock, just assume that it is always > there, and is very fast, so each look-up table output is immediately > registered. > Then you move A and B, realizing that they are 90 degree offset. Then > you just inspect the two Qs, and you see that you can consider one of > them (either one) as the outgoing clock, and the other as the > direction. Seems trivial, but the beauty is that it also works for any > and every kind of irregular operation. It's not "rocket science" but > it is simple and clever, if i may say so. > Peter- Hide quoted text - > > - Show quoted text -
So this is what I got (for the two different directions): A _--__--__- B --__--__-- Q1 _--__--__- Q2 --__--__-- D1 _--__--__- D2 --__--__-- A _--__--__- B __--__--__ Q1 __--__--__ Q2 -__--__--_ D1 __--__--__ D2 _--__--__- I want to be able to count every edge. I think this method will give me half (or a quarter?) of the resolution that I want, unless I'm missing something? Either way - what exactly is the idea here... I really don't understand the post you linked to. Is it saying that one of these signals can be used as a direction and one as the clock. However - if one of these was a direction signal - I'd expect it to be constant as my quadrature signals are just going in one direction... -Michael
On Apr 20, 9:27=A0pm, Michael <nleah...@gmail.com> wrote:
> On Apr 20, 11:39=A0pm, Peter Alfke <al...@sbcglobal.net> wrote: > > > > > On Apr 20, 8:14=A0pm, Peter Alfke <al...@sbcglobal.net> wrote: > > > > On Apr 20, 8:09=A0pm, Michael <nleah...@gmail.com> wrote: > > > > > On Apr 20, 7:12=A0pm, Peter Alfke <al...@sbcglobal.net> wrote: > > > > > > Michael, you might prefer my much simpler solution, as described i=
n a
> > > > > recent blog on the Xilinx forums website. > > > > > >http://forums.xilinx.com/xlnx/blog/article?message.uid=3D9394 > > > > > > I designed this a few years ago, and we use it in our programmable=
> > > > > frequency gererator, that I have mentioned here a few times. > > > > > Ken Chapman then took the exact same shaft encoder for the Spartan=
> > > > > eval board. > > > > > The design is absolutely bounce-proof, no Mickey Mouse low-pass > > > > > filters or other analog nonsense. > > > > > I hope the explanation is sufficient. > > > > > > Viel Spa=DF > > > > > Peter Alfke > > > > > Hi Peter - I didn't see any code attached to the post. Does this loo=
k
> > > > right? > > > > > signal Q1, Q2 : std_logic; > > > > process (A, B) > > > > begin > > > > =A0 =A0 =A0 =A0 if (A =3D '1' and B =3D '1') then > > > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q1 <=3D '1'; > > > > =A0 =A0 =A0 =A0 elsif (A =3D '0' and B =3D '0') then > > > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q1 <=3D '0'; > > > > =A0 =A0 =A0 =A0 elsif (A =3D '0' and B =3D '1') then > > > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q2 <=3D '1'; > > > > =A0 =A0 =A0 =A0 elsif (A =3D '1' and B =3D '0') then > > > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 Q2 <=3D '0'; > > > > =A0 =A0 =A0 =A0 end if; > > > > > =A0 =A0 =A0 =A0 D1 <=3D (A and B) or ((A or B) and Q1); > > > > =A0 =A0 =A0 =A0 D2 <=3D ((not A) and B) or (((not A) or B) and Q2); > > > > end process; > > > > > So then I would take say D1 and call it the clock, and call D2 the > > > > direction, and then increment the count on every edge of D1 when D2 > > > > was high and decrement it for every D1 edge when D2 was low? At leas=
t,
> > > > that was my understanding from your post. > > > > > However, I just simulated it by hand (on a piece of paper- haven't > > > > figured out how to use a simulator just yet) - and I set it up to ha=
ve
> > > > B 90 degrees behind A, and then I found Q1 =3D A, Q2 =3D B, D1 =3D A=
, and D2
> > > > =3D B. So I suspect I'm misunderstanding something... > > > > > -Michael > > > > Check it with pencil and paper. > > > Yes, A and B are 90 degree offset, that's why it's called a quadrature=
> > > decoder. > > > Then check if for a monotonic "friendly" rotation, and you see how it > > > works. The trickery is that it also works for any non-monotonic move. > > > Peter > > > Michael, > > you start simply by ignoring the clock, just assume that it is always > > there, and is very fast, so each look-up table output is immediately > > registered. > > Then you move A and B, realizing that they are 90 degree offset. Then > > you just inspect the two Qs, and you see that you can consider one of > > them (either one) as the outgoing clock, and the other as the > > direction. Seems trivial, but the beauty is that it also works for any > > and every kind of irregular operation. It's not "rocket science" but > > it is simple and clever, if i may say so. > > Peter- Hide quoted text - > > > - Show quoted text - > > So this is what I got (for the two different directions): > > A =A0_--__--__- > B =A0--__--__-- > Q1 _--__--__- > Q2 --__--__-- > D1 _--__--__- > D2 --__--__-- > > A =A0_--__--__- > B =A0__--__--__ > Q1 __--__--__ > Q2 -__--__--_ > D1 __--__--__ > D2 _--__--__- > > I want to be able to count every edge. I think this method will give > me half (or a quarter?) of the resolution that I want, unless I'm > missing something? > > Either way - what exactly is the idea here... I really don't > understand the post you linked to. Is it saying that one of these > signals can be used as a direction and one as the clock. However - if > one of these was a direction signal - I'd expect it to be constant as > my quadrature signals are just going in one direction... > > -Michael
Michael, you get one increment or decrement for every complete cycle, not for every edge. Since the direction polarity only matters exactly at the rising edge of Q1 (assuming you use Q1 as the clock) there is no need for Q2 to be stable at any other time. It just indicaates direction when it is needed. You can look at Q1 and Q2 just as cleaned up versions of A and B, without any bounce, but also insensitive to small changes in direction. There may be a way to double the resolution, by running Q1 through a frequency doubler while conditionally inverting Q2. That would double the total LUT count, but would still be quite simple. I had no need for the increased resolution in my application, but I'll take a look at it. The fundamental issue is to ignore the unavoidable contact bounce, and also to count correctly (up and down) even when the shaft encoder moves in any crazy way. You have to solve both these different issues: contact bounce and strange reversal of direction at any time. Otherwise any solution is worthless and just gives you a headache. And a digital designer wants to stay away from capacitors and filters and other analog stuff... Peter
On Apr 20, 8:02=A0pm, -jg <Jim.Granvi...@gmail.com> wrote:
> > Hi Frank - I thought about debouncing and - unless I'm being dumb - I > > think as long as only one input is changing at a time, bounce won't > > affect this approach in the steady state. I mean that if I turn it 4 > > counts, it might count something like 0 1 2 3 2 3 4 3 4. But the final > > value will be correct. > > There are different classes of Quad encoder. > The Simplest feed one phase into a CLK and the > other into DIRN, but that counts only once per whole cycle. > > The best designs can count on every edge, and can tolerate > a chattering edge. You might also want to catch > illegal state jumps (missed states), as that indicates > something is amiss in your design. > One easy to understand way to code this, is to create > a internal 2 bit phase engine, and lock it to the > external sampled edges. That design makes illegal > state jumps easy to catch. > You have a simple state engine, with 2 IP bits, > 2 Present bits, [16 combinations] and output =A0CE, DIRN, and ERR, > as well as 2 bits for Next state. > > -jg
I'm looking to count every single edge. I had another idea about how to do it: process(enc_a, enc_b) begin if enc_a'event then case prevstate is when "00"=3D> enc_cnt <=3D enc_cnt + 1; when "01"=3D> enc_cnt <=3D enc_cnt - 1; when "10"=3D> enc_cnt <=3D enc_cnt - 1; when "11"=3D> enc_cnt <=3D enc_cnt + 1; when others =3D> enc_cnt <=3D enc_cnt; end case; else case prevstate is when "00"=3D> enc_cnt <=3D enc_cnt - 1; when "01"=3D> enc_cnt <=3D enc_cnt + 1; when "10"=3D> enc_cnt <=3D enc_cnt + 1; when "11"=3D> enc_cnt <=3D enc_cnt - 1; when others =3D> enc_cnt <=3D enc_cnt; end case; end if; prevstate <=3D enc_a & enc_b; end process; Unfortunately - apparently the "if enc_a'event then" line is not synthesizable - you can't trigger on both edges. So once again I'm stuck. Any suggestions? Thanks! -Michael
On Apr 20, 10:10=A0pm, Michael <nleah...@gmail.com> wrote:
> On Apr 20, 8:02=A0pm, -jg <Jim.Granvi...@gmail.com> wrote: > > > > > > Hi Frank - I thought about debouncing and - unless I'm being dumb - I > > > think as long as only one input is changing at a time, bounce won't > > > affect this approach in the steady state. I mean that if I turn it 4 > > > counts, it might count something like 0 1 2 3 2 3 4 3 4. But the final=
> > > value will be correct. > > > There are different classes of Quad encoder. > > The Simplest feed one phase into a CLK and the > > other into DIRN, but that counts only once per whole cycle. > > > The best designs can count on every edge, and can tolerate > > a chattering edge. You might also want to catch > > illegal state jumps (missed states), as that indicates > > something is amiss in your design. > > One easy to understand way to code this, is to create > > a internal 2 bit phase engine, and lock it to the > > external sampled edges. That design makes illegal > > state jumps easy to catch. > > You have a simple state engine, with 2 IP bits, > > 2 Present bits, [16 combinations] and output =A0CE, DIRN, and ERR, > > as well as 2 bits for Next state. > > > -jg > > I'm looking to count every single edge. I had another idea about how > to do it: > > process(enc_a, enc_b) > begin > =A0 =A0 =A0 =A0 if enc_a'event then > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 case prevstate is > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "00"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt + 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "01"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt - 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "10"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt - 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "11"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt + 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when others =3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 end case; > =A0 =A0 =A0 =A0 else > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 case prevstate is > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "00"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt - 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "01"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt + 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "10"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt + 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "11"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt - 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when others =3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 end case; > =A0 =A0 =A0 =A0 end if; > > =A0 =A0 =A0 =A0 prevstate <=3D enc_a & enc_b; > end process; > > Unfortunately - apparently the "if enc_a'event then" line is not > synthesizable - you can't trigger on both edges. So once again I'm > stuck. > > Any suggestions? > > Thanks! > > -Michael
Michael, when you say: count every single edge", do you mean every edge of A, or every edge of either A or B ? One is 2 edges per 360 deegrees, the other one is 4. Mine is 1. Incidentally, why is this important? For manual control, you have too many edges to start with... Peter
On Apr 21, 1:49=A0am, Peter Alfke <al...@sbcglobal.net> wrote:
> On Apr 20, 10:10=A0pm, Michael <nleah...@gmail.com> wrote: > > > > > > > On Apr 20, 8:02=A0pm, -jg <Jim.Granvi...@gmail.com> wrote: > > > > > Hi Frank - I thought about debouncing and - unless I'm being dumb - =
I
> > > > think as long as only one input is changing at a time, bounce won't > > > > affect this approach in the steady state. I mean that if I turn it 4=
> > > > counts, it might count something like 0 1 2 3 2 3 4 3 4. But the fin=
al
> > > > value will be correct. > > > > There are different classes of Quad encoder. > > > The Simplest feed one phase into a CLK and the > > > other into DIRN, but that counts only once per whole cycle. > > > > The best designs can count on every edge, and can tolerate > > > a chattering edge. You might also want to catch > > > illegal state jumps (missed states), as that indicates > > > something is amiss in your design. > > > One easy to understand way to code this, is to create > > > a internal 2 bit phase engine, and lock it to the > > > external sampled edges. That design makes illegal > > > state jumps easy to catch. > > > You have a simple state engine, with 2 IP bits, > > > 2 Present bits, [16 combinations] and output =A0CE, DIRN, and ERR, > > > as well as 2 bits for Next state. > > > > -jg > > > I'm looking to count every single edge. I had another idea about how > > to do it: > > > process(enc_a, enc_b) > > begin > > =A0 =A0 =A0 =A0 if enc_a'event then > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 case prevstate is > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "00"=3D> > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt =
<=3D enc_cnt + 1;
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "01"=3D> > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt =
<=3D enc_cnt - 1;
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "10"=3D> > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt =
<=3D enc_cnt - 1;
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "11"=3D> > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt =
<=3D enc_cnt + 1;
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when others =3D> > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt =
<=3D enc_cnt;
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 end case; > > =A0 =A0 =A0 =A0 else > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 case prevstate is > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "00"=3D> > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt =
<=3D enc_cnt - 1;
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "01"=3D> > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt =
<=3D enc_cnt + 1;
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "10"=3D> > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt =
<=3D enc_cnt + 1;
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "11"=3D> > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt =
<=3D enc_cnt - 1;
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when others =3D> > > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt =
<=3D enc_cnt;
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 end case; > > =A0 =A0 =A0 =A0 end if; > > > =A0 =A0 =A0 =A0 prevstate <=3D enc_a & enc_b; > > end process; > > > Unfortunately - apparently the "if enc_a'event then" line is not > > synthesizable - you can't trigger on both edges. So once again I'm > > stuck. > > > Any suggestions? > > > Thanks! > > > -Michael > > Michael, when you say: count every single edge", do you mean every > edge of A, or every edge of either A or B ? > One is 2 edges per 360 deegrees, the other one is 4. Mine is 1. > Incidentally, why is this important? For manual control, you have too > many edges to start with... > Peter
I'm looking for 4 counts/360 degrees. My final use for quadrature decoding will be with motor control - so increasing the resolution of my encoder counting is very beneficial. -Michael
On Apr 21, 1:10=A0am, Michael <nleah...@gmail.com> wrote:
> On Apr 20, 8:02=A0pm, -jg <Jim.Granvi...@gmail.com> wrote: > > > > > > > > Hi Frank - I thought about debouncing and - unless I'm being dumb - I > > > think as long as only one input is changing at a time, bounce won't > > > affect this approach in the steady state. I mean that if I turn it 4 > > > counts, it might count something like 0 1 2 3 2 3 4 3 4. But the final=
> > > value will be correct. > > > There are different classes of Quad encoder. > > The Simplest feed one phase into a CLK and the > > other into DIRN, but that counts only once per whole cycle. > > > The best designs can count on every edge, and can tolerate > > a chattering edge. You might also want to catch > > illegal state jumps (missed states), as that indicates > > something is amiss in your design. > > One easy to understand way to code this, is to create > > a internal 2 bit phase engine, and lock it to the > > external sampled edges. That design makes illegal > > state jumps easy to catch. > > You have a simple state engine, with 2 IP bits, > > 2 Present bits, [16 combinations] and output =A0CE, DIRN, and ERR, > > as well as 2 bits for Next state. > > > -jg > > I'm looking to count every single edge. I had another idea about how > to do it: > > process(enc_a, enc_b) > begin > =A0 =A0 =A0 =A0 if enc_a'event then > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 case prevstate is > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "00"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt + 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "01"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt - 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "10"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt - 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "11"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt + 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when others =3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 end case; > =A0 =A0 =A0 =A0 else > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 case prevstate is > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "00"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt - 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "01"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt + 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "10"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt + 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when "11"=3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt - 1;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 when others =3D> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 enc_cnt <=
=3D enc_cnt;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 end case; > =A0 =A0 =A0 =A0 end if; > > =A0 =A0 =A0 =A0 prevstate <=3D enc_a & enc_b; > end process; > > Unfortunately - apparently the "if enc_a'event then" line is not > synthesizable - you can't trigger on both edges. So once again I'm > stuck. > > Any suggestions? > > Thanks! > > -Michael
A-ha! I realized I could get rid of the edge checking and just compare with my saved previous state. It synthesizes! Unfortunately, it gives me some warnings during synthesization, and then (I believe due to the warnings) it won't give me a programming file. The warnings are: the following signal(s) form a combinatorial loop: led<7>, enc_cnt<7>, Maddsub_enc_cnt14. (for led<7> - led<0>) Then it gives me a bunch of errors during mapping, like this one: ERROR:MapLib:661 - LUT4 symbol "Maddsub_enc_cnt41" (output signal=3DMaddsub_enc_cnt4) has input signal "Maddsub_enc_cnt" which will be trimmed. See the t I'm so close now (I think!) Can anybody tell me what is wrong with my code? I've never had a problem with mapping before - so this is very odd for me. I've posted the full code below. Thanks so much! -Michael library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.numeric_std.all; entity hello_world is port ( clk, enc_a, enc_b : in std_logic; switches : in std_logic_vector (3 downto 0); led : out std_logic_vector (7 downto 0) ); end hello_world; architecture rtl of hello_world is signal cnt : unsigned (30 downto 0); signal encval : unsigned (7 downto 0); signal enc_cnt : unsigned (7 downto 0); signal prevstate : std_logic_vector (1 downto 0); begin process(clk) begin if rising_edge(clk) then cnt <=3D cnt + 1; encval <=3D "000000" & enc_b & enc_a; end if; end process; process(enc_a, enc_b) begin if prevstate(0) /=3D enc_a then case prevstate is when "00"=3D> enc_cnt <=3D enc_cnt + 1; when "01"=3D> enc_cnt <=3D enc_cnt - 1; when "10"=3D> enc_cnt <=3D enc_cnt - 1; when "11"=3D> enc_cnt <=3D enc_cnt + 1; when others =3D> enc_cnt <=3D enc_cnt; end case; else case prevstate is when "00"=3D> enc_cnt <=3D enc_cnt - 1; when "01"=3D> enc_cnt <=3D enc_cnt + 1; when "10"=3D> enc_cnt <=3D enc_cnt + 1; when "11"=3D> enc_cnt <=3D enc_cnt - 1; when others =3D> enc_cnt <=3D enc_cnt; end case; end if; prevstate(0) <=3D enc_a; prevstate(1) <=3D enc_b; end process; led <=3D std_logic_vector(cnt(30 downto 23)) when switches(0)=3D'0' else std_logic_vector(enc_cnt); end rtl;
Peter Alfke wrote:

<snip>
> The fundamental issue is to ignore the unavoidable contact bounce, and > also to count correctly (up and down) even when the shaft encoder > moves in any crazy way. You have to solve both these different issues: > contact bounce and strange reversal of direction at any time.
In a full precision (one count per edge) you cannot ignore bounce, you have to accept it, and 'count with it, so the counter goes +1,-1 +1 as it follows the bounce. Similarly with edge jitter (also possible in control systems) you need to follow. That's why it is a good idea to sample the IPs, and the CLK speed needs to be no more than the total counter speed (normally well over 10MHz) Did you look at the links I gave before on the HCTL-2001 etc. ? Jim Granville,