URL
https://opencores.org/ocsvn/q_rotary_enc/q_rotary_enc/trunk
Subversion Repositories q_rotary_enc
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 1 to Rev 2
- ↔ Reverse comparison
Rev 1 → Rev 2
/q_rotary_enc/Capture.gif
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
q_rotary_enc/Capture.gif
Property changes :
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: q_rotary_enc/lpf_cap.sv
===================================================================
--- q_rotary_enc/lpf_cap.sv (nonexistent)
+++ q_rotary_enc/lpf_cap.sv (revision 2)
@@ -0,0 +1,46 @@
+/*
+Simple low-pass filter. Digital capacity
+*/
+
+`ifndef _lpf_cap_
+`define _lpf_cap_
+
+module lpf_cap #(parameter FILTER_WIDTH = 7)( // about half bit
+ input clock, sclr,
+ input in,
+ output out,
+ output init
+);
+
+reg [FILTER_WIDTH-1:0] cnt = {1'b1, {(FILTER_WIDTH-2){1'b0}}};
+always_ff @(posedge clock)
+ if (sclr)
+ cnt <= {1'b1, {(FILTER_WIDTH-2){1'b0}}}; // middle value
+ else if (in == 1'b1 && cnt != '1)
+ cnt <= cnt + 1'b1; // charging
+ else if (in == 1'b0 && cnt != '0)
+ cnt <= cnt - 1'b1; // discharging
+
+reg out_reg = 1'b0;
+always_ff @(posedge clock)
+ if (sclr)
+ out_reg <= 1'b0;
+ else if (cnt == '1) // full
+ out_reg <= 1'b1;
+ else if (cnt == '0) // empty
+ out_reg <= 1'b0;
+
+assign out = out_reg;
+
+reg init_reg = 1'b1;
+always_ff @(posedge clock)
+ if (sclr)
+ init_reg <= 1'b1;
+ else if (cnt == '1 || cnt == '0)
+ init_reg <= 1'b0;
+
+assign init = init_reg;
+
+endmodule :lpf_cap
+
+`endif
Index: q_rotary_enc/q_rotary_enc.sv
===================================================================
--- q_rotary_enc/q_rotary_enc.sv (nonexistent)
+++ q_rotary_enc/q_rotary_enc.sv (revision 2)
@@ -0,0 +1,45 @@
+/*
+ Counter for quadrature rotary encoder.
+ Module with low-pass filter for A, B signals is rot_enc_flt.sv
+*/
+
+`ifndef _q_rotary_enc_
+`define _q_rotary_enc_
+
+module q_rotary_enc(
+ input clock, sclr, ena,
+ input dir, // main count direction. 0 - direct count, 1 - reverse
+ input A, B, // signals from sensor. Signals /A, /B/, Z, /Z don't use. A, /A, B, /B more good connect to optocouplers
+ output reg signed [31:0] bidir_counter, // current relative position
+ output reg error // A, B error. For to clear error use sclr signal
+);
+
+ localparam bit[1:0] grey_inc2[4] = '{2'b01, 2'b11, 2'b00, 2'b10};
+ localparam bit[1:0] grey_dec2[4] = '{2'b10, 2'b00, 2'b11, 2'b01};
+
+ wire [1:0] cur_code;
+ reg [1:0] old_code = '0;
+
+ assign cur_code = (dir) ? {A, B} : {B, A};
+
+ always_ff @(posedge clock)
+ old_code <= cur_code;
+
+ wire cnt_ena = old_code != cur_code;
+ wire inc = cnt_ena && cur_code == grey_inc2[old_code];
+ wire dec = cnt_ena && cur_code == grey_dec2[old_code];
+ wire err = cnt_ena && !(inc || dec);
+
+ always_ff @(posedge clock)
+ if (sclr) bidir_counter <= 'sh0;
+ else if (ena)
+ if (inc) bidir_counter <= bidir_counter + 1'b1;
+ else if (dec) bidir_counter <= bidir_counter - 1'b1;
+
+ always_ff @(posedge clock)
+ if (sclr) error <= 1'b0;
+ else if (ena && err) error <= 1'b1;
+
+endmodule :q_rotary_enc
+
+`endif
Index: q_rotary_enc/rot_enc_flt.sv
===================================================================
--- q_rotary_enc/rot_enc_flt.sv (nonexistent)
+++ q_rotary_enc/rot_enc_flt.sv (revision 2)
@@ -0,0 +1,54 @@
+/*
+ Counter for quadrature rotary encoder with low-pass filter
+*/
+
+`ifndef _rot_enc_flt_
+`define _rot_enc_flt_
+`include "lpf_cap.sv"
+`include "q_rotary_enc.sv"
+
+module rot_enc_flt(
+ input clock,
+ input sclr,
+ input dir, // main direction
+ input A, B,
+ output signed [31:0] bidir_counter,
+ output error,
+ output ready // counter enabled
+);
+ reg A_reg = 1'b0, B_reg = 1'b0;
+ wire A_flt, B_flt;
+ reg [1:0] dir_reg = '0;
+ wire dir_changed;
+ wire [1:0] init;
+
+ // to trigger A, B signals on input pins
+ always_ff @(posedge clock) begin
+ A_reg <= A;
+ B_reg <= B;
+ dir_reg <= {dir_reg[0], dir};
+ end
+
+ assign dir_changed = dir_reg[1] ^ dir_reg[0];
+
+ // debouncing
+ lpf_cap #(7) // 2^7 - it's about 1/8 of min encoder period
+ f0(.clock, .sclr(dir_changed), .in(A_reg), .out(A_flt), .init(init[0])),
+ f1(.clock, .sclr(dir_changed), .in(B_reg), .out(B_flt), .init(init[1]));
+
+ reg ready_reg = 1'b0;
+ always_ff @(posedge clock)
+ ready_reg <= (dir_changed) ? 1'b0 : init == '0;
+
+ assign ready = ready_reg;
+
+ q_rotary_enc enc_inst(
+ .clock, .sclr(sclr || dir_changed), .ena(init == '0), .dir(dir_reg[0]),
+ .A(A_flt), .B(B_flt),
+ .bidir_counter,
+ .error
+ );
+
+endmodule :rot_enc_flt
+
+`endif
Index: q_rotary_enc/rot_enc_flt_tb.sv
===================================================================
--- q_rotary_enc/rot_enc_flt_tb.sv (nonexistent)
+++ q_rotary_enc/rot_enc_flt_tb.sv (revision 2)
@@ -0,0 +1,81 @@
+timeunit 1ns;
+timeprecision 100ps;
+
+module rot_enc_flt_tb;
+
+ bit clock = 0, sclr = 1;
+ wire A, B;
+ bit dir = 0;
+ wire signed [31:0] bidir_counter;
+ wire error;
+ wire ready;
+
+ always #10ns clock++;
+
+ bit rot_dir = 0, run = 0;
+
+ initial begin
+ repeat(10) @(posedge clock);
+
+ sclr = 1'b0;
+
+ wait(ready);
+
+ repeat(10_000) @(posedge clock);
+
+ rot_dir = 0;
+ run = 1;
+
+ #1ms
+
+ run = 0;
+
+ #500us
+
+ rot_dir = 1;
+ run = 1;
+
+ #1ms
+
+ run = 0;
+
+ repeat(10_000) @(posedge clock);
+
+ $stop(2);
+
+ end
+
+ rot_enc_flt dut(.*);
+ rotary_sensor sensor(.clock, .run, .rot_dir, .A, .B);
+
+endmodule :rot_enc_flt_tb
+
+module rotary_sensor(input clock, run, rot_dir, output A, B);
+
+ bit[1:0] gray[4] = {2'b00, 2'b01, 2'b11, 2'b10};
+
+ localparam BPS = 10_000 * 256 * 4 / 60;
+ localparam CLOCK_MHZ = 50_000_000;
+ localparam CNT_MAX = CLOCK_MHZ / BPS - 1;
+
+ int bit_cnt = 0;
+
+ always_ff @(posedge clock)
+ if (bit_cnt == CNT_MAX)
+ bit_cnt <= 0;
+ else
+ bit_cnt <= bit_cnt + 1;
+
+ wire bit_clk = bit_cnt == CNT_MAX;
+
+ bit [1:0] gray_cnt = 0;
+ always_ff @(posedge clock)
+ if (bit_clk && run)
+ if (rot_dir == 0)
+ gray_cnt <= gray_cnt + 1'b1;
+ else
+ gray_cnt <= gray_cnt - 1'b1;
+
+ assign {B, A} = gray[gray_cnt];
+
+endmodule :rotary_sensor