Problem writing quadrature decoder

The quadrature encoder has been tested and proven to work ( thank you, Ken Chapman), detecting every transition as a count pulse, never an accumulated error. The only flaw is a one-pulse backlash. 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. Peter Alfke

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

Reply to
Peter Alfke
Loading thread data ...

It also seems to bundle all illegal transistions into 'rotary_left' bucket ?

ie Missing is : Illegal_event never an

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.

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

Reply to
Jim Granville

?

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

Reply to
Peter Alfke

Do you have a link to a Compilable File ? (or set of files)

-jg

Reply to
Jim Granville

Peter Alfke wrote: (snip, I wrote)

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

Reply to
glen herrmannsfeldt

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

Reply to
Jim Granville

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

Reply to
Peter Alfke

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

Reply to
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 :)

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

Reply to
Jim Granville

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?

Reply to
Eric Smith

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

Reply to
nospam

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

Reply to
Peter Alfke

(unless you hit it will illegal states, then it creeps left ;)

[easily fixed, with one x ]

rotary_event resolves a

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

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

Reply to
Jim Granville

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

Reply to
Peter Alfke

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, fb@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
Reply to
Frank Buss

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.

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.

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

Reply to
Jim Granville

That's certainly how I've always done 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.

Reply to
Eric Smith

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

Reply to
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.T := rQuad * !count + !count # !rQuad + !rQuad + count * count; count.CLK = clk; // GCK

(etc) for all higher bits

That means your code has synthesised to use DOWN as rQuad and UP as !rQuad + !rQuad

- 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.T := rotary_b_in * !rotary_a_in * !Follow * !Follow # !rotary_b_in * !rotary_a_in * Follow * Follow; SF_counter.CLK = clk; // GCK

seems your version has a + !rQuad is missing somehow ? Lower 2 bits have more differences, but more is happening there.

-jg

Reply to
Jim Granville

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

Reply to
John_H

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.