Fred said:
My outputs are only approximately in quadrature, because I have
synchronized all inputs and outputs to the clk frequency. This clk signal
is intended to be, say, a 10 MHz clock.
I don't think this would cause any problems. Afterall, the period of a
10 MHz clock is only 100 nS, and a delay of a few hundred nanoseconds
shouldn't cause any problem for an encoder running in the KHz range.
Here is the basic idea:
__ __ __ __ __ __ __ __ __ __ _
a_in _| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__|
__ __ __ __ __ __ __ __ __ __
b_in __| |__| |__| |__| |__| |__| |__| |__| |__| |__| |__
cnt 0 1 2 3 0 1 2 3 0 1 2
__________ __________ __________
a_out |____________| |____________| |_____
___________ ___________ __________
b_out ____| |___________| |___________|
If the encoder is rotating in the other direction, everything is the
same,
but swapped left to right, which is exactly what we want.
The rule for calculating a_out is: a_out is high when count is 0 or 1,
and
low otherwise.
The rule for calculating b_out is: b_out is high when count is 1 or 2,
and
low otherwise.
The rule for calculating cnt is a little bit tricky, so stick with me.
There are four input encoder states:
state | a_in | b_in
======|======|=====
i | 0 | 0
ii | 1 | 0
iii | 1 | 1
iv | 0 | 1
As the states progress from 'i' to 'iv' as shown above, the shaft is
rotating (let's just say) clockwise. To calculate count, what we want to
do is pick one of those transitions and define it as the key transition.
For example, we could say that whenever we go from state 'i' to state
'ii', we should increment cnt. Likewise, when we go from state 'ii' to
state 'i' we should decrement cnt. Then, if the shaft goes back and forth
at the i/ii boundary, count will also go back and forth, and a_out and
b_out will behave correctly (that is, as if they were real encoder
signals).
So, assuming, once again, that we have some fast clock lying around (10
MHz would probably work fine), I would just sample a_in and b_in every
clock period, and look for the transitions mentioned above. These would
cause cnt to increase and decrease in the desired way, and a_out and
b_out would then track cnt, thus giving the correct output.
Probably there is another way to do this which doesn't involve using a
separate clock, but it isn't coming to me immediately, and since this
will
probably work on a small PLD anyway, why bother trying to screw with it?
Here is another untested verilog module. This one differs from the
earlier
one because I use a much more sophisticated (and correct) way of
calculating cnt, and I changed the outputs so they are no longer
clocked, but just depend combinatorially on cnt. Note that cnt is still
synchronous to clk.
module quad_div(a_in, b_in, clk, rst, a_out, b_out);
input a_in, b_in, clk, rst;
output a_out, b_out;
reg [1:0] cnt;
reg a_in_r, b_in_r, a_in_d, b_in_d; // r for register, d for delayed.
assign a_out = ((cnt == 0) || (cnt == 1));
assign b_out = ((cnt == 1) || (cnt == 2));
always @(posedge clk or posedge rst)
begin
if (rst) begin
cnt <= 0;
a_in_r <= 1;
b_in_r <= 1;
a_in_d <= 1;
b_in_d <= 1;
end
else begin
a_in_r <= a_in;
b_in_r <= b_in;
a_in_d <= a_in_r;
b_in_d <= b_in_r;
if (({a_in_d, b_in_d} == 0) && ({a_in_r, b_in_r} == 2))
cnt <= cnt + 1;
else if (({a_in_d, b_in_d} == 2) && ({a_in_r, b_in_r} == 0))
cnt <= cnt - 1;
end
end
Your algorithm also accumulates error and your mistake is to fix the AB
condition at which you make an increment/decrement decision. I have
included what I believe to be 'a' Verilog specification of a working
machine that expands upon your original idea- but one never knows when
using online Verilog "manuals" written by !@#$%^& imbeciles who wouldn't
know precision of expression if it blew up in their face. What is it
with this weak language that it has no multidimensional conditional
assignments?- can't believe that. To make a long story short: you must
expand the 'cnt' register to a 4-bit, the a/b_out variables are now a
function of the msb two bits per your original formulation, the lsb two
bits bidirectionally count transitions on the AB state with increments
on CW rotation and decrements on CCW rotations. The lsb two bits
naturally over/under-flow into the two msb bits of cnt modulo 4,
everything stays modulo 4, all motion is accumulated modulo 4 by cnt at
each encoder boundary crossing. At any instant cnt[3,2] contains the
modulo-16 sum of the net total of CW - CCW encoder increments off its
state when quad-div was 'rst', cnt[1,0] is the modulo-4 sum of this
total, and cnt[3,2] is the modulo-4 sum of the cnt[1,0] over/underflows.
So that mathematically you will have CW-CCW= q*4 + r where r=0,1,2, or 3
, and q=q'*4+r' where r'=0,1,2, or 3. Then CW-CCW=q'*16+r'*4+r so that
CW-CCW MOD 16= r'*4+r. The content of cnt[1,0] is obviously r, and the
total number of overflows into cnt[3,2] from cnt[1,0] is obviously q, so
that cnt[3,2] contains q MOD 4 or r' making cnt[3,0] binary value r'*4+r
or CW-CCW MOD 16 as required. If CW-CCW is negative, CW-CCW=q''*16+r''
where r''=0,1,2,...,15 and q''*16<=CW-CCW so that this is a composite
motion of q'' groups of 16 CCW encoder transitions, which land cnt[3,0]
at state 0, followed by r'' CW encoder transitions. Then r''=q*4 +r as
before, q>=0 and r=0,1,2,3, with q=q'*4+r', r'=0,...,3, with cnt[3,2]=r'
and cnt[1,0]=r.
In summary then, the instantaneous state of cnt[3,0] is exactly the net
number of signed encoder increments off its arbitrary state at quad_div
rst, cnt[1,0] is always the net number of encoder cell signed
transitions off the initial state, and cnt[3,2] is the net number of
signed encoder cell groups of 4 or the DIV 4 output sought. This one
*never* slips- *never* accumulates error-*never* delays, and is easily
extended to any DIV number except the quadrature output transitions
become impractical to compute in the PLD, will have to make that an
external register entry of some kind- at any rate you can run this one
at arbitrarily high speeds without limitation. If you want direction
outputs then set CW=1 whenever you do a cnt<=cnt+1, so make this
variable CW/CCW I suppose.
View in a fixed-width font such as Courier.
module quad_div(a_in, b_in, clk, rst, a_out, b_out);
input a_in, b_in, clk, rst;
output a_out, b_out;
reg [3:0] cnt;
reg abr[1,0], abd[1,0]; // r for register, d for delayed.
assign a_out = ((cnt[3,2]== 0) || (cnt[3,2]== 1));
assign b_out = ((cnt[3,2]== 1) || (cnt[3,2]== 2));
always @(posedge clk or posedge rst)
begin
if (rst)
begin
cnt <=0;
abr<=0;
abd<=0;
end
else
begin
abr<= {a_in,b_in};
abd<= abr;
if ( abd != abr )
case abd
0: case abr
1: cnt<=cnt-1;
2: cnt<=cnt+1;
default: cnt<=cnt;
endcase
1: case abr
3: cnt<=cnt-1;
0: cnt<=cnt+1;
default: cnt<=cnt;
endcase
2: case abr
0: cnt<=cnt-1;
3: cnt<=cnt+1;
default: cnt<=cnt;
endcase
3: case abr
2: cnt<=cnt-1;
1: cnt<=cnt+1;
default: cnt<=cnt;
endcase
endcase
end
end
Let's take this one step further and duplicate the performance of that
DigitalControls divide by user selected N for N=1 to 4096 or a 12-bit
divisor, with quadrature outputs at "exactly" 50% each. The PLD running
off of a high speed external clock makes this very easy. The way to
achieve 50% duty at arbitrary divide ratios is to note that the actual
divide of encoder resolution cell transitions is 4xN which happens to be
Nx4, so that by counting these transitions in 4 groups of N, you
naturally end up with your processed encode output using the rule that
a_out is high during the 1st and 2nd N-group count, and b_out is high
during the second and 3rd N-group count, both a_out and b_out low during
4th N-group count. The simplest conceptual implementation of this would
be a bidirectional counter that counts between states 1 and N
inclusively where overflow out of state N is to state 1, and underflow
from state 1 is to state N. The accumulator functions as before totaling
out to the net CW-CCW resolution cell travel off the reset zero point. A
simple state machine in a_out, b_out transitioning on these counter
over/under-flows then produces the required outputs. I am assuming the
use of these so-called "non-blocking" state assignments in Verilog, and
this works out nicely even in the usually degenerate case of N=1 which
is no problem in a machine working from a much higher frequency external
clock. The state machine logic on a_out,b_out is then simply
a_out<=~b_out and b_out<=a_out on N-count overflow, and a_out<=b_out and
b_out<=~a_out on N-count underflow, initial state a_out=1, b_out=0 on
'rst'. The original Verilog routine is now easily upgraded since it
already contains the encoder transition logic- pasting this thing
together-(may or may not be totally correct):
View in a fixed-width font such as Courier.
module quad_div(a_in, b_in, clk, rst, a_out, b_out,n11,...,n0,cw,ccw)
input a_in, b_in, clk, rst, n11,n10,n9,n8,n7,n6,n5,n4,n3,n2,n1,n0
// n11,n10,...,n0 division ratio
output a_out, b_out, cw, ccw; //cw , ccw direction outputs
reg [11:0] cnt;
reg [1:0] abr;// r for register
reg [1:0] abd;// d for delayed
reg [1:0] abo;// o for divided a,b outputs
reg [1:0] dir;// direction output dir[1]=cw dir[0]=ccw
assign a_out=abo[1];
assign b_out=abo[0];
assign cw=dir[1];
assign ccw=dir[0];
always @(posedge clk or posedge rst)
begin
if (rst)
begin
cnt <=0;
abr<=0;
abd<=0;
abo<=2;
dir<=0;
end
else
begin
abr<= {a_in,b_in};
abd<= abr;
if ( abd != abr )
case abd
0: case abr
1:begin //cnt<=cnt-1
dir<=1;
if(cnt==1)
cnt<={n11,n10,n9,n8,n7,n6,n5,n4,n3,n2,n1,n0};
abo<={abo[0],~abo[1]};
else
cnt<=cnt-1;
end
2:begin //cnt<=cnt+1
dir<=2;
if (cnt=={n11,n10,n9,n8,n7,n6,n5,n4,n3,n2,n1,n0})
cnt<=1;
abo<={~abo[0],abo[1]};
else
cnt<=cnt+1;
end
default: cnt<=cnt;
endcase
1: case abr
3: begin //cnt<=cnt-1
dir<=1
if(cnt==1)
cnt<={n11,n10,n9,n8,n7,n6,n5,n4,n3,n2,n1,n0};
abo<={abo[0],~abo[1]};
else
cnt<=cnt-1;
end
0: begin //cnt<=cnt+1
dir<=2;
if (cnt=={n11,n10,n9,n8,n7,n6,n5,n4,n3,n2,n1,n0})
cnt<=1;
abo<={~abo[0],abo[1]};
else
cnt<=cnt+1;
end
default: cnt<=cnt;
endcase
2: case abr
0:begin //cnt<=cnt-1
dir<=1;
if(cnt==1)
cnt<={n11,n10,n9,n8,n7,n6,n5,n4,n3,n2,n1,n0};
abo<={abo[0],~abo[1]};
else
cnt<=cnt-1;
end
3:begin //cnt<=cnt+1
dir<=2;
if (cnt=={n11,n10,n9,n8,n7,n6,n5,n4,n3,n2,n1,n0})
cnt<=1;
abo<={~abo[0],abo[1]};
else
cnt<=cnt+1;
end
default: cnt<=cnt;
endcase
3: case abr
2:begin //cnt<=cnt-1
dir<=1;
if(cnt==1)
cnt<={n11,n10,n9,n8,n7,n6,n5,n4,n3,n2,n1,n0};
abo<={abo[0],~abo[1]};
else
cnt<=cnt-1;
end
1:begin //cnt<=cnt+1
dir<=2;
if (cnt=={n11,n10,n9,n8,n7,n6,n5,n4,n3,n2,n1,n0})
cnt<=1;
abo<={~abo[0],abo[1]};
else
cnt<=cnt+1;
end
default: cnt<=cnt;
endcase
endcase
end
end