OpenCores
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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.