In theory this method of quadrature decoding should work perfectly,
unless I'm forgetting something. But for some reason which I'm afraid
I don't understand this is not synthesizable. I liked the idea of
using this method for quadrature decoding as it didn't require me to
deal with storing the previous state - the use of the falling_edge()
and rising_edge() functions did that for me. Xilinx ISE help brought
me to this page: http://www.xilinx.com/support/answers/14047.htm.
However, I don't have any embedded 'event statements, or any 'event
statements at all, for that matter (unless again I'm missing
something).
What exactly am I doing wrong, and is there a way to fix it? 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 enccnt : unsigned (7 downto 0);
begin
process(clk)
begin
if rising_edge(clk) then
cnt <= cnt + 1;
encval <= "000000" & enc_b & enc_a;
end if;
end process;
process (enc_a, enc_b)
begin
if (rising_edge(enc_a) and enc_b = '1') then
enccnt <= enccnt - 1;
elsif (rising_edge(enc_a) and enc_b = '0') then
enccnt <= enccnt + 1;
elsif (falling_edge(enc_a) and enc_b = '1') then
enccnt <= enccnt + 1;
elsif (falling_edge(enc_a) and enc_b = '0') then
enccnt <= enccnt - 1;
elsif (rising_edge(enc_b) and enc_a = '1') then
enccnt <= enccnt + 1;
elsif (rising_edge(enc_b) and enc_a = '0') then
enccnt <= enccnt - 1;
elsif (falling_edge(enc_b) and enc_a = '1') then
enccnt <= enccnt - 1;
elsif (falling_edge(enc_b) and enc_a = '0') then
enccnt <= enccnt + 1;
end if;
end process;
led <= std_logic_vector(cnt(30 downto 23)) when switches(0)='0' else
std_logic_vector(encval);
end rtl;
> What exactly am I doing wrong
1. Using inputs as clocks.
2. Using two clocks in a process.
and is there a way to fix it?
Declare as many registers as you need,
but put everything in your first process.
Have a look at my examples.
-- Mike Treseler
> In theory this method of quadrature decoding should work perfectly,
> unless I'm forgetting something.
The quadrature encoder on the Spartan 3E Starter Kit is mechanical, you
should implement some debouncing. Maybe some simple holdoff is sufficient,
but if there are fast crosstalk glitches, a simple low pass filter (in
VHDL) would be a good idea, too.
--
Frank Buss, f...@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
So rising_edge() and falling_edge() can only be used with clocks?
> and is there a way to fix it?
>
> Declare as many registers as you need,
> but put everything in your first process.
Why does everything have to be in one process? Is there a reason it's
objectionable to have one process that is sensitive to only my clock
and one process that's only sensitive to a couple inputs? And so
you're suggesting I go with a state based approach? Or something else?
What are these extra registers for?
> Have a look at my examples.
>
> -- Mike Treseler
What examples are you referring to?
Thanks!
-Michael
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.
-Michael
http://forums.xilinx.com/xlnx/blog/article?message.uid=9394
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ß
Peter Alfke
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 CE, DIRN, and ERR,
as well as 2 bits for Next state.
-jg
> 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.
As long as the bounce isn't faster than the counter can count, yes.
I like the clocked design that Peter A. has in another post.
I believe that one works as long as the clock is faster than
the fastest possible real count. Bounces might be missed, but
the count will be right.
Also, I believe one should reset the counter on the first index
pulse and not on subsequent ones.
-- glen
Bounces should (or must) be missed..
That's the whole purpose of the circuit .:-)
Peter..
> So rising_edge() and falling_edge() can only be used with clocks?
For synchronous designs, yes.
> Why does everything have to be in one process?
It doesn't. Just a suggestion.
Peter has the problem solved for you.
Post a question to comp.lang.vhdl if you need more help.
-- Mike Treseler
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 = '1' and B = '1') then
Q1 <= '1';
elsif (A = '0' and B = '0') then
Q1 <= '0';
elsif (A = '0' and B = '1') then
Q2 <= '1';
elsif (A = '1' and B = '0') then
Q2 <= '0';
end if;
D1 <= (A and B) or ((A or B) and Q1);
D2 <= ((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 = A, Q2 = B, D1 = A, and D2
= 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
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
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
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"=>
enc_cnt <= enc_cnt + 1;
when "01"=>
enc_cnt <= enc_cnt - 1;
when "10"=>
enc_cnt <= enc_cnt - 1;
when "11"=>
enc_cnt <= enc_cnt + 1;
when others =>
enc_cnt <= enc_cnt;
end case;
else
case prevstate is
when "00"=>
enc_cnt <= enc_cnt - 1;
when "01"=>
enc_cnt <= enc_cnt + 1;
when "10"=>
enc_cnt <= enc_cnt + 1;
when "11"=>
enc_cnt <= enc_cnt - 1;
when others =>
enc_cnt <= enc_cnt;
end case;
end if;
prevstate <= 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
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=Maddsub_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 <= cnt + 1;
encval <= "000000" & enc_b & enc_a;
end if;
end process;
process(enc_a, enc_b)
begin
if prevstate(0) /= enc_a then
prevstate(0) <= enc_a;
prevstate(1) <= enc_b;
end process;
led <= std_logic_vector(cnt(30 downto 23)) when switches(0)='0' else
std_logic_vector(enc_cnt);
end rtl;
<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,
> 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.
you only have 2 state bits, once you get 4, you can do it on a faster
clock edge,
So if you consider a quad-state/johnson counter, as a first pass.
[easier to follow than binary], and what you are coding is a phase
lock, or
tracking system counter (TSC) that spins that counter to match the
Quad IP.
It CAN spin at any speed, normally it will do one phase click, and
then
many,many idle states until the next phase click is needed
IP.TSC
00.00
01.01
11.11
10.10 are all HOLD or do nothing states, MOST clocks will be here
00.01
01.11
11.10
10.00 are all DEC(TSC) by one, and output DEC_EN to the long counter
00.10
01.00
11.01
10.11 are all INC(TSC) by one, and output INC_EN to the long counter
which leaves 4 other states
00.11
01.10
11.00
10.01 as different by TWO clocks, so these are illegal
and should never happen in a correct margin design.
Take them to a sticky-led, perhaps ?.
Once you have that working, you can compact the two TSC bits into the
Long counter, two LSBs, by change to binary coding, if you really want
to
save two FFs :)
In a FPGA tho, why bother ?
( In a small CPLD, you might take the effort. )
Also, on read/compare of the long counter, if the system WORD size
is less than the counter size, you need to capture ALL bits
in one clock, in case the counter rolls over between byte/word reads.
Jim Granville,
You almost have what you want but you will still have troubles because
mechanical chatter can matter in a binary implementation. One way you
CAN achieve your goals is to use Gray code counters, at least for the
LSbits.
I previously designed a 3-bit Gray code counter for a first stage of a
very fast counter such that I only have 1 LUT of delay for each bit and
was able to count at a clock frequency limited by the delay of a single
LUT. My design produced the same combinatorial loops warning but for a
stable combinatorial quadrature counter, those are expected warnings
and should still produce a programming file. Even here, I used this
first stage as a divide-by-8 prescaler rather than using the count
directly because getting a 3-bit Gray counter to sync up with a
multi-bit Gray or binary counter isn't trivial though it is doable with
a little care.
The key to getting a quadrature counter to work with a Gray code counter
is to realize that when you are at a specific Gray count, only two bits
can change: one for the forward direction, one for reverse. Effectively
one channel of the quadrature counter can be slaved to the LSbit while
the count determines which upper bit (and at what polarity) the other
quadrature channel controls. When you transition between two upper bits
being controlled by the one quadrature channel, the bits are both safe
because the quadrature channel that controls those upper bits is in the
middle, stable part of its cycle while the LSbit channel is
transitioning the count, affecting which upper bit is controlled.
When the Gray count is read, the value is (typically) registered in the
clock domain twice such that if one bit is caught mid-transition then
that value will be settled upon by the second register - only one
register - as a defined value. If instead there are multiple registers
fed by that one first-stage value, it's possible two paths could "see"
different values for this asynchronously changing signal.
If you can figure how to count in Gray and then how to change one bit
(at a defined polarity) at a time for a given count, you will have an
every-transition counter for full resolution and be
asynchronous-friendly in your design. And you will get plenty of
combinatorial-loop warnings for your design but you will be able to
produce a programming file; they are just warnings, after all.
- John_H
There is a test bench and simulation script.
As mentioned : don't forget to register A & B before hooking to the
module.
Bertrand
I am working on a design that uses 4 LUTs and 4 flip-flops,
takes in raw A and B inputs plus a clock
and outputs synchronously decoded CE and U/D signals to control a
binary counter.
There are 3 configuration options: 1, 2, or 4 counts per 360 degree
contact cycle.
And bounce is suppressed, and kept away from the counter :-)
"Everything you always wanted to have in a quadrature detector'"
Peter
John - unless I'm missing something here... I think just watching
edges is good enough to deal with any sort of debouncing problems, as
long as only A or B (not both) are bouncing at any given time.
But either way - right now I really just want to figure out why I
can't get the stupid thing to map properly - and then I will worry
more about dealing with the small things. I just posted the full code,
constraints, and console output on the FPGA4Fun forum:
http://www.fpga4fun.com/forum/viewtopic.php?t=1281
Can somebody please look at this? I just am at a complete loss as to
what is wrong - but to you more experienced people the problem will
probably jump right out.
Thanks!
-Michael
Can you guarantee that the edge will transition fully and stay
transitioned for a minimum length of time?
While most debounce problems are a true mechanical bounce issue where
hard contacts make it difficult to engage and hold a contact
instantly, I'm not comfortable enough to say that a bounce will be a
hard transition from one logic level to another with microsecond-scale
valid levels between bounces. I've always considered the mechanical
transition to be undefined enough to make undebounced (I like this
word) switches extremely poor for things like clocks.
In your case, do you even use those edges as clocks? I thought you
had non-clocked combinatorial loops where you had binary counts
affected by the edge. If that edge gets undefined, some bits may go
one way while other bits go another way in that binary word unless you
can guarantee the debounced behavior of those edges.
The Gray code based quadrature counter doesn't have problems with
bounce. At all.
I'd like to make some time to look at your code but I natively code
Verilog and I don't have a synthesizer at home. I'll see what I can
do.
- John_H
What does your trim report say about the trimmed signals?
From your mapper output you posted on FPGA4Fun, "See the trim report
for details about why the input signal will become undriven."
- John_H
Hi John - I couldn't figure out how to look at the trim report. There
wasn't a single file with trim in it in the project directory. I
GREPed the directory for trim and didn't turn up anything except for
places where it says to look at the trim report. I googled and
couldn't find anything, and I checked the Xilinx website for
information about how to look at the trim report and couldn't find
anything. Any suggestion as to where I can find this report?
Thanks!
-Michael
Hmmm,
Can you explain how bounce can be suppressed in a 4 count design ?
There, you cannot distinguish between a change in direction, and
a contact bounce, as they have the same signature ?.
Does it also catch false/illegal states in the 4 count mode ?
I have heard of very high apparent edge rates in mechanical systems,
caused by shock propogations (certainly enough to stress a Software
Solution) - so a 50MHz+ clock is not as silly as it may sound :)
- Jim Granville,
>On Apr 21, 7:35 pm, John_H <newsgr...@johnhandwork.com> wrote:
>> Michael wrote:
>>
>> What does your trim report say about the trimmed signals?
>>
>> From your mapper output you posted on FPGA4Fun, "See the trim report
>> for details about why the input signal will become undriven."
>>
>> - John_H
>
>Hi John - I couldn't figure out how to look at the trim report. There
>wasn't a single file with trim in it in the project directory.
It's part of the mapper report file, *.mrp (often mydesign.mrp or map.mrp)
- Brian
> I have heard of very high apparent edge rates in mechanical systems,
> caused by shock propogations (certainly enough to stress a Software
> Solution) - so a 50MHz+ clock is not as silly as it may sound :)
The clocked decoder latches the inputs on each clock cycle and
updates the counter appropriately. It works as long as the clock
is faster than the time between actual edges (including any bounce).
If the signals can bounce long enough for the encoder to get
to the next edge, it is not possible to uniquely decode the
signal. If the encode sits right on an edge, continually
bouncing, the counter may miss some back transitions, but it
also won't count the matching forward transition.
If you worry about metastability in the latch, add a second
latch.
-- glen
The design is done, we are finishing documentation, and Ken Chapman
has graciously agreed to give it a thorough hardware test net week:
4 LUTs, 4 flip-flops, insensitive to contact bounce or any type of
erratic mechanical movement.
1, 2, or 4 counts per encoder cycle (i.e. 90, 180 or 360 degrees
between counts, as a config option)
"The end of all discussions about quadrature encoders, mechanical or
optical."
Peter Alfke
Then it must be VERY clever, even Clairvoyant, if it can separate
'any type of erratic mechanical movement', from a genuine
mechanical movement! ;)
> 1, 2, or 4 counts per encoder cycle (i.e. 90, 180 or 360 degrees
> between counts, as a config option)
> "The end of all discussions about quadrature encoders, mechanical or
> optical."
No mention of catching illegal states ? (clock too slow) ?
-jg
I'll run the clock between 10 and 100 MHz, so it will never be too
slow.
There is no way to move a whole quadrant in one such clock period.
Peter
Peter Alfke wrote:
Hi Peter,
Hmm, history is littered with such brave. sweeping statements!.
In the example I came across, the designer had also asserted the
clock would never be to slow, :), but he did the maths based on
average MASS velocities, and forgot about shock/whiplash.
In a practical installation, he needed a faster clocked PLD
for reliable operation. The real world can do that to
designers.
There is also a drive these days to save power, and use only
as many MHz as will suffice.
So, given the ease with which designers can get this wrong, and
the ease with which catching this can be added,
I would suggest that a good reference design should include this
optional feature.
Feel free to blame me for the inclusion :)
Jim Granville
Peter Alfke wrote:
Hi Peter,
Your second description does not fully follow your first; the point
I was making, was that in a full quadrant design, it is impossible
to discriminate between "erratic mechanical movement" and a genuine
movement
and the counter must track Up/Dn/Up/Dn/Up until the contact settles.
"insensitive to contact bounce" is thus impossible: the system should
follow
the bounce. (Which I think is what your second description is
actually saying?)
-jg
Let's discuss this when the design is completely tested and described.
My design does not follow the bouncing contact, it just ignores it and
waits for the other contact to make a change...
Peter
The system does not count edges, it counts states. This means that
bounces will not matter as long as only one input changes at a time.
If you miss and even number of edges, you are at the same state you
started at and the result is correct. If you miss an odd number, you
are in the next state and the result is correct at the next clock.
This is not the case if you use the edges for clocks. That is a very
bad thing to do and will give you lots of problems.
You do care if both inputs bounce. That is tough to follow.
This can become somewhat semantic, but there is no bounce in a
full 4 quadrant encoder. It cannot know what bounce is, and it
MUST follow all edges that it is able to see.
To see this in a nice diagram, see figure 12
http://literature.agilent.com/litweb/pdf/5965-5894E.pdf
Here, you will see a direction reversal, being tracked.
It might look like a bounce to some, but the chip cannot know that,
it has to react to all edges it sees.
If you are working in LESS than 4 quadrant mode, then yes,
you can start some hand-over logic, but that will add some
back-lash - which may be fine for a hand-operated knob
application.
> This is not the case if you use the edges for clocks. That is a very
> bad thing to do and will give you lots of problems.
>
> You do care if both inputs bounce. That is tough to follow.
Which is why I was suggesting adding logic to show you have
'lost sync'.
Then, you can lower the clock, to save power, and know when
you have gone too far.
-jg
Below is the vhdl file, courtesy of my friend Ken Chapman:
----------------------------------------------------------------------------------------------------------------------------------
-- Shaft Encoder
----------------------------------------------------------------------------------------------------------------------------------
--
-- Interface to rotary encoder.
-- Detection of movement and direction.
--
-- The rotary switch contacts are filtered using their offset (one-
hot) style to
-- clean them. Circuit concept by Peter Alfke.
-- Note that the clock rate is fast compared with the switch rate.
--
rotary_filter: process(clk)
begin
if clk'event and clk='1' then
--Synchronise inputs to clock domain using flip-flops in input/
output blocks.
rotary_a_in <= rotary_a;
rotary_b_in <= rotary_b;
--concatinate rotary input signals to form vector for case
construct.
rotary_in <= rotary_b_in & rotary_a_in;
case rotary_in is
when "00" => rotary_q1 <= '0';
rotary_q2 <= rotary_q2;
when "01" => rotary_q1 <= rotary_q1;
rotary_q2 <= '0';
when "10" => rotary_q1 <= rotary_q1;
rotary_q2 <= '1';
when "11" => rotary_q1 <= '1';
rotary_q2 <= rotary_q2;
when others => rotary_q1 <= rotary_q1;
rotary_q2 <= rotary_q2;
end case;
end if;
end process rotary_filter;
--
-- Pipeline the valies of q1 and q2 so that we can detect ANY changes
and determine what changed.
--
-- The detection of any change is considered a rotation event.
--
-- Then the direction of rotation in one direction is indicated
by.....
-- q1 changing from Low to High when q2 is Low
-- or
-- q1 changing from High to Low when q2 is High
-- or
-- q2 changing from Low to High when q1 is High
-- or
-- q2 changing from High to Low when q1 is Low
--
-- Clearly if neither of the above are true then the rotation was in
the opposite direction.
--
shaft_direction: process(clk)
begin
if clk'event and clk='1' then
delay_rotary_q1 <= rotary_q1;
delay_rotary_q2 <= rotary_q2;
rotary_event <= (rotary_q1 xor delay_rotary_q1) or (rotary_q2 xor
delay_rotary_q2);
rotary_right <= ( (not delay_rotary_q1) and rotary_q1 and
(not rotary_q2) )
or ( delay_rotary_q1 and (not rotary_q1)
and rotary_q2 )
or ( (not delay_rotary_q2) and rotary_q2
and rotary_q1 )
or ( delay_rotary_q2 and (not rotary_q2) and
(not rotary_q1) );
end if;
end process shaft_direction;
--
--
-- A simple binary counter is used to record directional change
-- LEFT decrements counter.
-- RIGHT increments counter.
--
position_counter: process(clk)
begin
if clk'event and clk='1' then
if rotary_event = '1' then
if rotary_right = '1' then
rotate_counter <= rotate_counter + 1;
else
rotate_counter <= rotate_counter - 1;
end if;
end if;
end if;
end process position_counter;
--
----------------------------------------------------------------------------------------------------------------------------------
--
--
end Behavioral;
------------------------------------------------------------------------------------------------------------------------------------
--
-- END OF FILE shaft_encoder.vhd
Peter Alfke wrote:
> The quadrature encoder has been tested and proven to work ( thank you,
> Ken Chapman), detecting every transition as a count pulse,
It also seems to bundle all illegal transistions into 'rotary_left' bucket ?
ie Missing is :
Illegal_event <= (rotary_q1 xor delay_rotary_q1) and (rotary_q2 xor
delay_rotary_q2);
> never an
> accumulated error. The only flaw is a one-pulse backlash.
That could be a quite serious drawback in a closed loop system ?
eg a DC servo system with a relatively coarse quadrature encoder,
should be able to seek any edge, and 'dither-lock' there.
> That means,
> it does not recognize the first change after a reversal of direction.
> You could call it hysteresis, analogous to a +/- 1 count ambiguity,
> known to exist in many conversions.
Do you have device report files ?
This seems to use quite few flip-flops.
Tolerable in a FPGA, less desirable in a CPLD.
What is the latency, and the max count speed, in clk terms ?
-jg
You are right, it uses few flip-flops: four to be exact, plus 4 LUTs.
Call that four Logic cells.
Obviously fits also into PALs and CPLDs. Latency is 2 clock ticks.
I have explained the "electronic backlash". It's inherent to the
design. You either can tolerate it or you cannot.
For crying out loud: This was a simple, almost trivial, cute and
clever design idea.
Let's not make a big issue out of it....It works, and I explained the
limitation.
We built hundreds of instruments, and it is nice (but not earth-
shakingly so) that we can trust this trivial circuit to never give us
any bad surprise.
Peter Alfke
Peter Alfke
Peter Alfke
Peter Alfke
Do you have a link to a Compilable File ? (or set of files)
-jg
>>I like the clocked design that Peter A. has in another post.
>>I believe that one works as long as the clock is faster than
>>the fastest possible real count. Bounces might be missed, but
>>the count will be right.
> Bounces should (or must) be missed..
> That's the whole purpose of the circuit .:-)
If it bounces long enough, up to the next clock pulse,
it won't be missed. As long as the extra counts come
in matched (up and down) pairs, the count is right.
-- glen
Correct. Peter has published a partial VHDL source, and
the design he is talking about uses what I'd call an
anti-Chatter-Filter, which is a back-lash state
engine that uses hand-over edges.
That could be a good idea on a user-interface,
(less flicker effects) but could be a bad idea
on a closed loop control system driven from a rotary encoder.
-jg
Agreed. The circuit was invented for a manually operated frequency-
control application. I like its simplicity and lack of any analog
components. But there is backlash which never bothered us in the
intended application.
What's the best solution for servo-applications where the backlash is
bothersome? Counting all contact bounce might be risky...
Peter Alfke
W
If the count choices are 347 and 348 on either side of the bounce and
only those two counts, what risk is there?
- John_H
It could ba a good thing, in a manual system, as it would avoid flicker.
The eye does not like flicker.
I've seen some hand-rotary encoders where the detent aligns with
one of the edges, which would seem to be asking for chatter-by-design :)
> What's the best solution for servo-applications where the backlash is
> bothersome? Counting all contact bounce might be risky...
The Sampled-clock nature gives a natural LPF, so the only risk I can
see, is if the logic somehow 'missed' balancing the INC and DECs.
A well coded design should not do that. It should just pack
INC/DEC/INC with possible idle clocks between them.
The code Peter posted maps the illegal states onto rotary_left,
so there is risk there of overrange conditions creeping left.
Lowest power rotary encoder would use SPDT contacts, but I have not
seen those commercially ?
-jg
If you count on both edge polarities, and you never miss any edges,
no problem. Ensuring that you never miss an edge is problematic.
If you get a runt pulse, you might only count on the leading edge but
not the trailing edge of the pulse.
If you can guarantee some minimum pulse width, then there should be
no problem. But how do you guarantee that for bounce?
>John_H <news...@johnhandwork.com> writes:
>> If the count choices are 347 and 348 on either side of the bounce and
>> only those two counts, what risk is there?
>
>If you count on both edge polarities,
You should not count edges you should sample states. It doesn't matter if
you miss bounce states as long as you don't miss true encoder change
states. FPGA implementations can sample at ridiculously fast rates compared
to state changes from physical encoders.
Below is verilog for a decoder with position counter. It counts all states
(that is 400 counts for one revolution of a 100 line encoder). If you don't
like the LSB jittering with bounce just ignore it?
module qecounter(
// Outputs
count,
// Inputs
a,
b,
arst,
srst,
clk
);
parameter counter_width = 16;
output [counter_width - 1 : 0] count;
input a;
input b;
input arst;
input srst;
input clk;
reg [counter_width - 1 : 0] count;
reg la;
reg lb;
reg lla;
reg llb;
always @(posedge clk or posedge arst) begin
if(arst) begin
la <= 0;
lb <= 0;
lla <= 0;
llb <= 0;
count <= 0;
end else begin
la <= a;
lb <= b;
lla <= la;
llb <= lb;
if(srst) begin
count <= 0;
end else begin
case({lb, la, llb, lla})
// increment count states
4'b0001,
4'b0111,
4'b1000,
4'b1110 : count <= count + 1;
// decrement count states
4'b0010,
4'b0100,
4'b1011,
4'b1101 : count <= count - 1;
// nop states
// 4'b0000,
// 4'b0101,
// 4'b1010,
// 4'b1111 :
// invalid states
// 4'b0011,
// 4'b0110,
// 4'b1001,
// 4'b1100 :
endcase
end
end
end
endmodule
--
So let's talk about solutions:
I offered a solution that never makes a cumulative mistake, resolves a
single quadrant, but has a one-quadrant hysteresis.
It also happens to be very simple, and has no analog components.
Who offers something better, i.e. without the hysteresis, but with the
same reliability?
Peter Alfke
(unless you hit it will illegal states, then it creeps left ;)
[easily fixed, with one x ]
rotary_event <= (rotary_q1 xor delay_rotary_q1) xor (rotary_q2
xor delay_rotary_q2);
> resolves a
> single quadrant, but has a one-quadrant hysteresis.
That is a separate 2 bit state-filter, which can be added to any Quad
Encoder system.
As such, it is a useful option for manual systems, but I would
call it sub-optimal for a control system
> It also happens to be very simple, and has no analog components.
> Who offers something better, i.e. without the hysteresis, but with the
> same reliability?
If a system catches a runt pulse LOW it must capture a following HI
and so do the correct +/-1. Provided a system can accept adjacent
Inc/dec commands, it will not get lost.
A good test should verify that.
-jg
Is there a way to get away from "if" and "provided".
I am looking for a watertight solution, no ifs or buts...
Assumption:
Horrible undefined contact bounce, but only on one of the two inputs
at any one time.
Keep track of rotational angle with one-quadrant resolution, never an
accumulated error.
Avoid the hysteresis of my suggested solution
Peter
> Assumption:
> Horrible undefined contact bounce, but only on one of the two inputs
> at any one time.
> Keep track of rotational angle with one-quadrant resolution, never an
> accumulated error.
> Avoid the hysteresis of my suggested solution
I think this is impossible without some "if"s. If the contact bounces, you
have to add some holdoff, which doesn't work for fast movements. If you
have one-quadrant resolution without debouncing or hysteresis, the angle
output flickers one position for each bounce, which may be a problem for
the next stage.
But maybe I'm wrong. Sounds like you have already a better solution in mind
:-)
--
Frank Buss, f...@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
The 'if' was only to explain that a runt pulse cannot 'sneak past',
as a low spike must settle high. The system IS clock sampled.
The 'provided' is a simple design condition, and I have not seen
a design that does NOT accept adjacent Inc/Dec, but I am sure
one could be designed (!), so that is why I stated it as a test.
I do not think any of the designs offered have problems here, but
it is a boundary condition, that should be verified by test.
> Assumption:
> Horrible undefined contact bounce, but only on one of the two inputs
> at any one time.
> Keep track of rotational angle with one-quadrant resolution, never an
> accumulated error.
Yes, a system can do that. The real question is if you WANT the +/-1
on adjacent clocks, (@same apparent physical position) or not ?.
An operator would probably answer NO, and machine control system, would
answer yes, remove the backlash.
> Avoid the hysteresis of my suggested solution
but that separate 2 register hyst. block might be a positive addition :)
If you are serious about accumulated errors, then you should NOT
count on the illegal states.
In a new system, my preference would be to catch, and flag those illegal
states, as they indicate an out-or-range condition (or a sensor problem)
It allows you to lower the clock, as far as practical, for power savings.
The optimal Encoder uses the smallest number of registers/cells, and you
can design one where the state-follower forms 2 LSBs of the counter.
This also has the lowest latency.
-jg
That's certainly how I've always done it.
> If you don't like the LSB jittering with bounce just ignore it?
That was my point. Someone complained about jitter. No matter how you
do it, you're going to give up something. You can't really eliminate
the jitter without causing other potential problems.
My apologies that I couldn't rattle this off in the original poster's
VHDL preference but the Verilog should be easy to understand even to the
untrained eye.
module
whatCouldGoWrong
( input clk
, input [1:0] quad
, output reg [7:0] count
);
reg [1:0] rQuad;
// used for an asynchronous boundary crossing
wire [1:0] binQuad = {rQuad[1],^rQuad[1:0]};
// change the quadrature input to binary
wire dir = (binQuad - count[1:0]) >> 1;
// +1 is 0, -1 is 1, +/-2 is init only
always @(posedge clk)
begin
rQuad <= quad;
if( binQuad != count[1:0] )
count <= count + (dir ? -8'h1 : +8'h1);
end
endmodule
If there is ONLY one contact bouncing, the quadrant defined by the count
LSbits will track the quadrature input LSbits. If the quadrature inputs
are vacillating between two quadrants, the binary value will also
vacillate between those same two quadrants. There is no uncertainty.
No ifs or buts.
When the system starts there is no guarantee that the quadrature inputs
and the counter will correspond to the same quadrant so the count will
adjust - even if there is an "illegal" state by being 180% out of phase
at init - by initializing to a value between -2 and +1, inclusive.
There will NEVER be an illegal state after this if only one contact
bounces; the changes will always be +/-1.
There is no hysteresis and no problems as long as the clock is faster
than the change rate plus bounce of the quadrature encoder phases.
We've taken the situation far out of where it needs to be since
quadrature encoders give us the flexibility to be so stable. If the
user wants a flag for an illegal state, just include an initial mask to
keep any initialization movement from flagging a non-problem and any
difference of +/-2 after that can flag the error.
It's possible that I've missed something fundamental but I don't believe
so. The quadrature input is keeping the same quadrant information as
the LSbits of the synchronous counter. I originally viewed the problem
as more difficult than needed because of my previous Gray counter work
for very fast prescaling which required some combinatorial tricks. No
tricks here.
- John_H
Interesting code, wins a prize for fewest-lines!! :)
(to the point of terse...)
Quite Similar to my design, but I had separated out the 2 LSBs, as a
separate State-follow (ie more lines of code), in a more
divide-and-conquer code style.
Your code also compiles first-paste, but the report does not look quite
right.
It Created Logic on Count (above the 2 LSB's) BOOLEAN EQNS as
count<2>.T := rQuad<1> * !count<1> + !count<0>
# !rQuad<1> + !rQuad<0> + count<1> * count<0>;
count<2>.CLK = clk; // GCK
(etc) for all higher bits
That means your code has synthesised to use
DOWN as rQuad<1>
and UP as
!rQuad<1> + !rQuad<0>
- which is not quite right, something might need a tweak ?
Up and down should clearly BOTH contain both Quad lines.
My code created this for 3rd bit in the counter.
SF_counter<0>.T := rotary_b_in * !rotary_a_in * !Follow<0> * !Follow<1>
# !rotary_b_in * !rotary_a_in * Follow<0> * Follow<1>;
SF_counter<0>.CLK = clk; // GCK
seems your version has a + !rQuad<0> is missing somehow ?
Lower 2 bits have more differences, but more is happening there.
-jg
remind me again, please:
I thought the # was the OR operator but you have a mix of + and * mixed
in there as well.
Does your synth produce an enable for each register, too? Forcing an
enable (count[1:0]!=quad) might clarify the operation a little from a
human-readable standpoint.
- John_H
>
> Does your synth produce an enable for each register, too? Forcing an
> enable (count[1:0]!=quad) might clarify the operation a little from a
> human-readable standpoint.
No, strange thing is ONE direction seems plausible, but the other dirn
seems wrong.... ?
(This was Xilinx webpack)
What does your report say ?
-jg
Typos corrected, "+" changed to "*" where "*" is and while "#" is or.
For the upper bits, the down count will happen when the rQuad is
behind the count by 1 or 2 phases and the count[1:0] is 00 and the up
count will happen when the rQuad is in front of the count by 1 phase
and the count[1:0] is 11. Because the logic you're using is a toggle,
the upper bits will only change when the count[1:0] is 00 and the
rQuad is 10 or 11 (down) or the count[1:0] is 11 and the rQuad is 00
(up).
This matches your (de-typoed) logic above.
Since the count is used to judge the retained quadrant rather than a
registered version of the quadrature inputs, the up/down toggle points
work in nicely with the quadrant detection.
- John_H
This is a general comment:
It was said that you can just "ignore the counter LSBs" if they bother
you. That is not true. Imagine the counter oscillating between 99 and
100 (if it were decimal) or between FFF and 1000. That would be pretty
upsetting.
Wow, we got the thread-count up to 63. Isn't it amazing. Never has so
much been argued (in a friendly way) about so little...
Peter
Jim Granville wrote:
> John_H wrote:
>> Does your synth produce an enable for each register, too? Forcing an
>> enable (count[1:0]!=quad) might clarify the operation a little from a
>> human-readable standpoint.
>
>
> No, strange thing is ONE direction seems plausible, but the other dirn
> seems wrong.... ?
> (This was Xilinx webpack)
> What does your report say ?
Looks like a Xilinx tool bug ?
If I create a node for either dir, or dir and CEnable, then it
changes, and Webpack seems to cope better.
[ perhaps the code is too terse for it ;) ]
It creates a (bizarely named) pair of clock enables, that are
now symmetric, and look plausible.
rQuad<1> := quad<1>;
rQuad<0> := quad<0>;
dir = count<1>
$ count<0> * !rQuad<0>
# !count<0> * rQuad<1>;
--Created node, seems to be CE_DOWN
rQuad<0>.COMB = !count<0> * !count<1> * rQuad<1> * dir
# !count<0> * !count<1> * rQuad<0> * dir;
--Created node, seems to be CE_UP
rQuad<1>.COMB = count<0> * count<1> * !rQuad<1> * !dir
# count<0> * count<1> * rQuad<0> * !dir;
count<3>.T := rQuad<0>.COMB * !count<2>
# rQuad<1>.COMB * count<2>;
Given rQuad<1> is a Quad registered input, seeing rQuad<1>.COMB
used as something tool-created, is strange....
Wonder what a brand new Xilinx tool chain does here....?
-jg
> Wow, we got the thread-count up to 63. Isn't it amazing. Never has so
> much been argued (in a friendly way) about so little...
Not only that: the 'so little' is reducing even more, as the
register count is falling ;)
-jg
> Jim Granville wrote:
> <snip>
>
>>It Created Logic on Count (above the 2 LSB's) BOOLEAN EQNS as
>>
>>count<2>.T := rQuad<1> * !count<1> * !count<0>
>> # !rQuad<1> * !rQuad<0> * count<1> * count<0>;
>>count<2>.CLK = clk; // GCK
>>
>>(etc) for all higher bits
>
> <snip>
>
> Typos corrected, "+" changed to "*" where "*" is and while "#" is or.
>
> For the upper bits, the down count will happen when the rQuad is
> behind the count by 1 or 2 phases and the count[1:0] is 00 and the up
> count will happen when the rQuad is in front of the count by 1 phase
> and the count[1:0] is 11.
Ah, yes - I missed the 2-phase instance. In my code, only
one-quadrant is used for count, and a 2 quadrant delta is ignored
by MSBs and can be flagged as an error.
LSBs lock on the next clock.
It could be the 2 quadrant instance is also contained in here, just not
so visible ?
dir = count<1>
$ count<0> * !rQuad<0>
# !count<0> * rQuad<1>;
rQuad<1>.COMB = count<0> * count<1> * !rQuad<1> * !dir
# count<0> * count<1> * rQuad<0> * !dir;
rQuad<0>.COMB = !count<0> * !count<1> * rQuad<1> * dir
# !count<0> * !count<1> * rQuad<0> * dir;
Which brings the question: How would you recode your version to
Trap a 2 Quadrant error and not propogate a MSB count on it ?
-jg
> Looks like a Xilinx tool bug ?
No. John has pointed out the 2-quad delta in his code causes a clock,
so the eqn result is not as symmetric as I was expecting. PBKAC.
> If I create a node for either dir, or dir and CEnable, then it
> changes, and Webpack seems to cope better.
>
> It creates a (bizarely named) pair of clock enables, that are
> now symmetric, and look plausible.
Likely behaves the same, just the 2-quad case is less visible.. ?
-jg
I hope you agree that the original upper bits were performing
properly. The LSbits can carry through from a similar argument, I
imagine. Just figure the four count[1:0] quadrants in order are 00,
01, 10, and 11 while the corresponding rQuad quadrants are 00, 01, 11,
and 10. If these were LUTs and not counters, the 2 LSbits would be
entirely dependent on rQuad and count[1:0] in one level of 4-input LUT
logic.
There should be NO illegal codes in a properly working system after
initialization. I use the starting quadrature input phase to move the
starting count to a range of -2 to +1, inclusive. During this init,
the "illegal" phase will help move a 180 degree out-of-phase starting
code to a count of -2. After this init, any instance of 180 degrees
out of phase (((rQuad-count[1:0])&2'h3)==2'h2) is an illegal code and
CANNOT be properly interpreted. The illegal code could set a flag but
only after the first few clocks of the initialization have fed through
the system. It might be the best way to gate the signal is to use the
first time the count is outside the -2 to +1 initialization range.
I don't know the proper way to apply a "preserve" attribute in XST to
guarantee that these flops don't get optimized to high steady states
independent of the init state with the opposite "reg name=1'b0;"
Synplify may not yet support this form of register initialization
though XST tries to do the right thing.
reg gate = 1'b0;
reg err = 1'b0;
always @(posedge clk)
begin
if( count>8'h1 & count < 8'hfe ) gate <= 1'b1;
if( gate & ( ((rQuad-count[1:0])&2'h3)==2'h2 ) ) err <= 1'b1;
end
The err does not reset in this code snippet.
- John_H
> Jim Granville wrote:
> <snip>
>
>>Which brings the question: How would you recode your version to
>>Trap a 2 Quadrant error and not propogate a MSB count on it ?
>>
>>-jg
>
>
> I hope you agree that the original upper bits were performing
> properly. The LSbits can carry through from a similar argument, I
> imagine. Just figure the four count[1:0] quadrants in order are 00,
> 01, 10, and 11 while the corresponding rQuad quadrants are 00, 01, 11,
> and 10. If these were LUTs and not counters, the 2 LSbits would be
> entirely dependent on rQuad and count[1:0] in one level of 4-input LUT
> logic.
Correct, but it seems to vary
- My Code uses just 1 and 2 Product terms, because I split the 2 LSB out
and effectively code binQuad = {rQuad[1],^rQuad[1:0]} in registers.
( which has an implicit next-clock-lock )
- but my code is more lines, and 'splits' the counter, which is
not as 'visually appealing' :)
- Your code compiles to either 4.TFF & 6.TFF PT, with a DIRN node or
4.TFF & 3.DFF PT on no Dirn node.
- which I think is the +2 complicating things ?
I think the problem is fundamental : with one counter line,
if you code to remove the +2 case, you do not sync, but leaving it
in, means you propogate an error.... :(
I suppose you could rule that it WILL sync on the next quad click,
and so that could be tolerable, to allow just +/-1 ?
(effectively this is a hold-on-illegal ?)
>
> There should be NO illegal codes in a properly working system after
> initialization. I use the starting quadrature input phase to move the
> starting count to a range of -2 to +1, inclusive. During this init,
> the "illegal" phase will help move a 180 degree out-of-phase starting
> code to a count of -2. After this init, any instance of 180 degrees
> out of phase (((rQuad-count[1:0])&2'h3)==2'h2) is an illegal code and
> CANNOT be properly interpreted.
It could be a sensor error, or a rate error (clk too slow), which
is why I am keen to catch it.
-jg
Using this rule, I re-coded as
wire OutByOne = ( count[1:0]-binQuad == 2'b01 )
| ( binQuad-count[1:0] == 2'b01 );
always @(posedge clk)
begin
rQuad <= quad;
if ( OutByOne )
count <= count + (dir ? -8'h1 : +8'h1);
end
and this compiles to identical MSBs, and
LSBs of Count0 = 2PT.DFF and Count1 = 4PT.TFF
and this code I think can only ever INC or DEC the
counter by one.
So we have achieved one counter line, and no count at all on illegal
(an improvement over my Auto-sync which would snap the 2 LSBs on error)
- minor point, as Error state is rare.
The slight hike in resource, is probably worth it ?
-jg
There is NO protection from illegal states. If you get an illegal state
because BOTH input channels have flipped polarity, the first input that
comes back to correct polarity might not be the correct one giving you a
count of -1 instead of +1 that should occur; the bouncing continues,
possibly coming back through the original phase back to where it SHOULD
have been with the +1 count but with a -3 instead. There CANNOT be
protection from the illegal state, only an indicator that it happened.
If the 180 degree phase difference is removed from the init in my code,
the 180 degree difference will stay until a change in the encoder
finally happens. I'd recommend keeping the "illegal code" treatment as
a negative step of 1 for init purposes. That is the ONLY time it SHOULD
occur.
- John
Correct (I said similar earlier) which is why I prefer to flag it
as well, - the hold-on-illegal suggests you _missed_ an edge, but have
no way of knowing if it was CW or CCW.
A novice might not realise there even ARE illegal states.
In a FPGA with trucklods of resource, you could run an Error counter,
and read it periodically as a sensor integrity check.
> If the 180 degree phase difference is removed from the init in my code,
> the 180 degree difference will stay until a change in the encoder
> finally happens. I'd recommend keeping the "illegal code" treatment as
> a negative step of 1 for init purposes. That is the ONLY time it SHOULD
> occur.
It's a philosphy thing I guess, I prefer to keep any error away from
the counter, and think the one-click to lock at power up, is no
real problem - a Quad system always has power up problems anyway.
It gets moot, because a system with frequent errors needs remedy
either way!.
A user might complain more about a double-step (which they will see)
than a late-step
(which they will hardly notice a few degrees on a knob?
Peter's Anti-Jitter block can bolt on the front of any of these, if
you want to avoid flicker effects.
-jg
There ARE NO illegal states unless the system is very hosed such as the
extreme shock problems mentioned earlier. If the system is being jarred
to that degree, the count will make no sense especially if the bounce is
many 10s (100s?) of bogus transitions on both channels.
There is no missing an edge because it isn't edges we're tracking, it's
quadrants. As long as the encoder visits each quadrant there will be NO
missed counts and therefor no post-init illegal states.
I allow the phase difference between the quad inputs and the count of
180 degrees on startup to transition to the count of -2 in a stable manner.
- John_H
> Jim Granville wrote:
> <snip>
>
>>
>> Correct (I said similar earlier) which is why I prefer to flag it
>> as well, - the hold-on-illegal suggests you _missed_ an edge, but have
>> no way of knowing if it was CW or CCW.
>> A novice might not realise there even ARE illegal states.
>
> <snip>
>
> There ARE NO illegal states unless the system is very hosed such as the
> extreme shock problems mentioned earlier.
Correct: A correctly operating, Quadrature encoder with good clk margin,
should have no illegal states.
> If the system is being jarred
> to that degree, the count will make no sense especially if the bounce is
> many 10s (100s?) of bogus transitions on both channels.
Also, if your sensor is damaged/worn/dirty, then this will be one way to
sense that.
>
> There is no missing an edge because it isn't edges we're tracking, it's
> quadrants. As long as the encoder visits each quadrant there will be NO
> missed counts and therefor no post-init illegal states.
Suppose the CLK is too slow, relative to the Quad signals ?
Now, you WILL have a 'missing edge', and as CLK freq is
a design choice, knowing you have made an error is a good
thing :)
This is a real-word interface, and the real-world can throw
some failure modes into the mix....
I'm looking for an encoder that uses SPCO contacts,
(allows micro-power encoder), but that seems to be unobtanium.
-jg
Consider a shaft running at an outrageous 100k rpm and a 1024 phase per
revolution encoder. That's about 1.6 MHz to guarantee operation. Of
someone shooses to use a slower clock, they sure aren't doing their job.
An error indicator isn't going to help much in that situation. It's a
little like having a warning system in your shoe that says "your foot is
on fire." Is it really useful? Sure it could happen, but come on....
> This is a real-word interface, and the real-world can throw
> some failure modes into the mix....
>
> I'm looking for an encoder that uses SPCO contacts,
> (allows micro-power encoder), but that seems to be unobtanium.
>
> -jg
Rather than exotic metal contacts, how about using an optical encoder?
It's hard to bounce 2 channels of light at the same time.
- John_H
The problem with optical encoders is power consumption.
You can pulse them with low duty cycles to save power, but
suddenly that clock speed issue becomes important...
(as does the power of the oscillator, used to derive the
pulses.. )
-jg
At the other extreme are high-speed optical quadrature encoders in
robotics, where they might control an arm that moves up to a few
meters per second with a resolution of 1 micrometer, which means a few
MHz. There is no detent, and highest resolution without hysteresis is
desirable. The computer can suppress counter changes due to mechanical
oscillations. John H. presented a synchronous circuit that covers that
application, and is also very simple.
It has been an interesting, albeit lengthy discussion.
Peter Alfke
> At the other extreme are high-speed optical quadrature encoders in
> robotics, where they might control an arm that moves up to a few
> meters per second with a resolution of 1 micrometer, which means a few
> MHz.
Just another thought, what happens for slow moving systems
and an optical encoder where it might generate a voltage
in between '1' and '0'?
-- glen
Normally hysteresis is employed to handle that.
-jg
With the asynchronous crossing, the registered quadrature encoder
signals in the synchronous domain will come to a conclusion as to what
that intermediate voltage should be: high or low. This will often
result in chatter between two quadrants in much the same way as switch
bounce because the threshold voltage may be modulated by noise giving
a different full-scale registered result on various consecutive
cycles.
The techniques is still completely stable. That's why quadrature
encoders are so great.
- John_H
With the proviso that such slow edges should NOT be fed into a FPGA.
(they have tr/tf specs)
If you have a cpld with hysteresis pin option,
(Atmel ATF15xxBE, Xilinx XC2Cxx, Lattice 40xxZE ) then you
are ok.
-jg
You can feed any FPGA with excessive transition-time signals, as long
as you understand the consequences:
The signal will pick up noise and thus create digital glitches as it
moves through the threshold, and there will be some cross-current
through the inverter, adding some power consumption, but never causing
any damage. A slow-transitioning clock is an invitation to disaster,
but a logic input usually causes no harm. And you can convert any 2
pins into a one-bit Schmitt input, just with 2 external resistors.
Peter Alfke
Rotary (or linear) quadrature shaft encoder.
After a lengthy exchange of ideas, we found that there are only two
winning circuits.
Both are equally simple and inexpensive, and both can be used from
zero speed to multi-MHz speed. And none has cumulative errors.
Circuit A was presented by Peter Alfke, and it guarantees the
suppression of all contact bounce and erratic contact behavior,
completely isolating the position-indicating counter from such
disturbances. The design pays for this with an angular or linear
hysteresis (a.k.a. electronic backlash), that limits its resolution.
Circuit B was presented by John_H, and it guarantees the best possible
resolution. This design pays for this with a possibly erratic foward/
backward oscillating content in the counter that indicates the shaft
position.
Either design does the best possible job, and thus represents a
perfect engineering compromise.
Peter Alfke
Peter Alfke wrote:
> Let me try a conclusion after more than 70 postings:
>
> Rotary (or linear) quadrature shaft encoder.
> After a lengthy exchange of ideas, we found that there are only two
> winning circuits.
> Both are equally simple and inexpensive,
Not if you count registers ;)
With Xilinx tools, targeting a Xilinx CPLD, this used 8 more registers
than the possible minimum.
> and both can be used from
> zero speed to multi-MHz speed. And none has cumulative errors.
...when used with a clean Quadrature signal.
>
> Circuit A was presented by Peter Alfke, and it guarantees the
> suppression of all contact bounce and erratic contact behavior,
> completely isolating the position-indicating counter from such
> disturbances. The design pays for this with an angular or linear
> hysteresis (a.k.a. electronic backlash), that limits its resolution.
This is actually a pre-filter, so it can be used with any 'standard'
quadrature encoder.
If you want Circuit A to not count on illegal states, use this line:
rotary_event <= (rotary_q1 xor delay_rotary_q1)
xor (rotary_q2 xor delay_rotary_q2);
-and optionally- (for those who think errors need reporting )
rotary_error <= (rotary_q1 xor delay_rotary_q1)
and (rotary_q2 xor delay_rotary_q2);
> Circuit B was presented by John_H, and it guarantees the best possible
> resolution. This design pays for this with a possibly erratic foward/
> backward oscillating content in the counter that indicates the shaft
> position.
This is a more classic version, but with one packing twist :
It uses the two counter LSBs as the follow-nodes, whilst
that section of Circuit A uses two more registers.
Circuit A also consumes registers for Event and Direction,
and has more latency.
(in a fpga the higher register cost of Circuit A might not
bother anyone)
There was another 'Circuit C', submitted by nospam,
a more common 4 register vesion, but with illegal states
properly handled. It is very easy to follow, as it
tabulated the 16 states (8 as comments).
[Cost is 2 more registers, than B.]
Below is a version of Circuit B, extended to
a) Catch illegal states (for those who think that matters )
b) Does not count on illegal states. Instead, waits one click on POR
You can, of course, optionally add the Sticky-state filter of Circuit A
to the front of B or C, if you want to reduce possible visual
flicker.
Which I think covers all the bases, for everyone... :)
-jg
module
whatCouldGoWrong
( input clk
, input [1:0] quad
, output reg [7:0] count
, output OutByTwoErr
);
// used for an asynchronous boundary crossing
reg [1:0] rQuad;
// change the quadrature input to binary
wire [1:0] binQuad = {rQuad[1],^rQuad[1:0]};
// +1 is 0, -1 is 1, ą2 is init only
wire dir = (binQuad - count[1:0]) >> 1;
// Sense if out by Plus/Minus ONE - IF equal, or out by 2, ignore
wire OutByOne = ( count[1:0]-binQuad == 2'b01 )
| ( binQuad-count[1:0] == 2'b01 );
// If out by 2 frequently, your CLK, and/or sensor need attention!
// Can feed into an Error counter ( see some CAN Bus controllers )
assign OutByTwoErr = ( count[1:0]-binQuad == 2'b10 )
| ( binQuad-count[1:0] == 2'b10 );
always @(posedge clk)
begin
rQuad <= quad;
if ( OutByOne )
count <= count + (dir ? -8'h1 : +8'h1);
end
endmodule
> Do you have a link to a Compilable File ? (or set of files)
Here's one way to hook up the processes:
http://mysite.verizon.net/miketreseler/shaft_encoder.vhd
http://mysite.verizon.net/miketreseler/shaft_encoder.pdf
-- Mike Treseler