Last active
October 1, 2019 12:38
-
-
Save wdevore/014845dfbfa9572c7d770ce3a4dcf8b3 to your computer and use it in GitHub Desktop.
Simple Rotary Encoder example using IceStorm toolchain and targeted at TinyFPGA-B2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Rotary Encoder test - top.v | |
// | |
// -------------------------------------------------------------------------- | |
// Sync switch debouncer | |
// -------------------------------------------------------------------------- | |
module digital_filter( | |
input clk, | |
input D, | |
output reg Q); | |
reg[3:0] dfilter4; | |
localparam valid0 = 4'b0000, valid1 = 4'b1111; | |
always @(posedge clk) | |
begin | |
dfilter4 <= {dfilter4[2:0], D}; | |
case(dfilter4) | |
valid0: Q <= 0; | |
valid1: Q <= 1; | |
default: Q <= Q; // hold value | |
endcase | |
end | |
endmodule | |
// -------------------------------------------------------------------------- | |
// Basic flip flop | |
// -------------------------------------------------------------------------- | |
module flip_flop | |
#( | |
parameter Default = 0 | |
) | |
( | |
input D, | |
input C, | |
output Q, | |
output notQ | |
); | |
reg state; | |
assign Q = state; | |
assign notQ = ~state; | |
always @ (posedge C) begin | |
state <= D; | |
end | |
// Simulation | |
initial begin | |
state = Default; | |
end | |
endmodule | |
// -------------------------------------------------------------------------- | |
// Rotary decoder | |
// Base on: https://www.fpga4fun.com/QuadratureDecoder.html | |
// This decoder is sometimes called a "4x decoder" because it counts all | |
// the transitions of the quadrature inputs. | |
// Note: I still want to build a variation based on these: | |
// https://www.fpga4student.com/2017/04/simple-debouncing-verilog-code-for.html | |
// https://www.beyond-circuits.com/wordpress/tutorial/tutorial12/ | |
// -------------------------------------------------------------------------- | |
module quadrature_decoder ( | |
input A, | |
input B, | |
input Clk, | |
output Dir, | |
output Shift | |
); | |
wire s0; | |
wire s1; | |
wire s2; | |
wire s3; | |
wire s4; | |
wire s5; | |
flip_flop #( | |
.Default(0) | |
) | |
DIG_D_FF_1bit_i0 ( | |
.D( A ), | |
.C( Clk ), | |
.Q( s0 ) | |
); | |
flip_flop #( | |
.Default(0) | |
) | |
DIG_D_FF_1bit_i1 ( | |
.D( B ), | |
.C( Clk ), | |
.Q( s5 ) | |
); | |
flip_flop #( | |
.Default(0) | |
) | |
DIG_D_FF_1bit_i2 ( | |
.D( s0 ), | |
.C( Clk ), | |
.Q( s1 ) | |
); | |
flip_flop #( | |
.Default(0) | |
) | |
DIG_D_FF_1bit_i3 ( | |
.D( s5 ), | |
.C( Clk ), | |
.Q( s4 ) | |
); | |
flip_flop #( | |
.Default(0) | |
) | |
DIG_D_FF_1bit_i4 ( | |
.D( s1 ), | |
.C( Clk ), | |
.Q( s2 ) | |
); | |
flip_flop #( | |
.Default(0) | |
) | |
DIG_D_FF_1bit_i5 ( | |
.D( s4 ), | |
.C( Clk ), | |
.Q( s3 ) | |
); | |
assign Dir = (s1 ^ s3); | |
assign Shift = (s1 ^ s2 ^ s4 ^ s3); | |
endmodule | |
// -------------------------------------------------------------------------- | |
// x4 decoder produces all 4 events from 1 turn of the rotary, however, we only want | |
// to recognize one of them. So this module is a basic 2 bit counter that | |
// emits true when the counter = 0. | |
// -------------------------------------------------------------------------- | |
module event_detect ( | |
input Clk, | |
output Detected | |
); | |
wire s0; | |
wire s1; | |
wire s2; | |
wire s3; | |
flip_flop #( | |
.Default(0) | |
) | |
DIG_D_FF_1bit_i0 ( | |
.D( s0 ), | |
.C( Clk ), | |
.Q( s1 ), | |
.notQ ( s0 ) | |
); | |
flip_flop #( | |
.Default(0) | |
) | |
DIG_D_FF_1bit_i1 ( | |
.D( s2 ), | |
.C( s0 ), | |
.Q( s3 ), | |
.notQ ( s2 ) | |
); | |
assign Detected = (s1 & s3); | |
endmodule | |
// -------------------------------------------------------------------------- | |
// Main module | |
// -------------------------------------------------------------------------- | |
module top ( | |
output pin1_usb_dp,// USB pull-up enable, set low to disable | |
output pin2_usb_dn, | |
input pin3_clk_16mhz, // 16 MHz on-board clock | |
// pins 13-4 should be connected to 10 LEDs | |
output pin13, | |
output pin12, | |
output pin11, | |
output pin10, | |
output pin9, | |
output pin8, | |
output pin7, | |
output pin6, | |
output pin5, | |
output pin4, | |
input pin14_sdo, // Rotary input A : Yellow before Green = CW | |
input pin15_sdi, // Rotary input B | |
output pin16_sck, // debounced ~A | |
output pin17_ss, // debounced ~B | |
output pin18, // quad_dir (Yellow channel on DSO) | |
output pin19 // quad_shift (Green) | |
); | |
reg [22:0] clk_1hz_counter = 23'b0; // Hz clock generation counter | |
reg clk_cyc = 1'b0; // Hz clock | |
reg[9:0] shift = 1; // N bit shift register that shifts at each turn | |
// 2KHz because of the quadrature | |
localparam FREQUENCY = 23'd2000; | |
wire inv_A, inv_B; | |
wire quad_dir; | |
wire quad_shift; | |
wire event_enabled; | |
// Debouncers | |
digital_filter dfA(.clk(clk_cyc), .D(pin14_sdo), .Q(pin16_sck)); | |
digital_filter dfB(.clk(clk_cyc), .D(pin15_sdi), .Q(pin17_ss)); | |
// Convert from negative logic to positive logic | |
not(inv_A, pin16_sck); | |
not(inv_B, pin17_ss); | |
// Generate events based on A/B | |
quadrature_decoder decoder(.A(inv_A), .B(inv_B), .Clk(clk_cyc), .Dir(quad_dir), .Shift(quad_shift)); | |
// Filter out 3 of the 4 events. We just want one. | |
event_detect ed(.Clk(quad_shift), .Detected(event_enabled)); | |
// Clock divder and generator | |
always @(posedge pin3_clk_16mhz) begin | |
if (clk_1hz_counter < 23'd7_999_999) | |
clk_1hz_counter <= clk_1hz_counter + FREQUENCY; | |
else begin | |
clk_1hz_counter <= 23'b0; | |
clk_cyc <= ~clk_cyc; | |
end | |
end | |
// Ping/Pong effect, driven by Decoder | |
// Warning! You can't use the 16MHz clock because: for one that would introduce cross-domain | |
// clocking, and two, quadrature is based off of the 2KHz clock so they would be out of sync. | |
// Introducing a FIFO would be nutty and overkill. | |
always @(posedge clk_cyc) begin | |
// Shift active LED only if Shift flag is High and Event detected. | |
if (event_enabled == 1 && quad_shift == 1) begin | |
if (quad_dir == 0) begin | |
if (shift == 10'b1000000000) | |
shift <= 1; // Wrap around | |
else | |
shift <= shift << 1; | |
end | |
else begin | |
if (shift == 10'b0000000001) | |
shift <= 10'b1000000000; // Wrap around | |
else | |
shift <= shift >> 1; | |
end | |
end | |
else | |
shift <= shift; | |
end | |
// Route to pins | |
assign | |
pin13 = shift[9], | |
pin12 = shift[8], | |
pin11 = shift[7], | |
pin10 = shift[6], | |
pin9 = shift[5], | |
pin8 = shift[4], | |
pin7 = shift[3], | |
pin6 = shift[2], | |
pin5 = shift[1], | |
pin4 = shift[0]; | |
// Debug | |
assign | |
pin18 = quad_dir, | |
pin19 = quad_shift; | |
assign | |
pin1_usb_dp = 1'b0, | |
pin2_usb_dn = 1'b0; | |
endmodule // top |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The Encoder circuit modeled in Digital. This is a 4x-decoder which means it generates all 4 events surrounding the A/B inputs.