Created
September 30, 2019 18:38
-
-
Save wdevore/8b1c0d7ac769b59e955cb390b0a03134 to your computer and use it in GitHub Desktop.
Simple BarGraph using a Rotary Encoder and using IceStorm toolchain while 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 but we only want | |
// to recognize only 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, // Left most bit | |
output pin12, | |
output pin11, | |
output pin10, | |
output pin9, | |
output pin8, | |
output pin7, | |
output pin6, | |
output pin5, | |
output pin4, // Right most bit | |
input pin14_sdo, // Rotary input A : Yellow before Green = CW | |
input pin15_sdi, // Rotary input B | |
); | |
reg [22:0] clk_1hz_counter = 23'b0; // Hz clock generation counter | |
reg clk_cyc = 1'b0; // Hz clock | |
// The bargraph works by shifting a fixed with number of 1s (i.e. 10 in this case) | |
// _out of view | |
// / /- in view | |
// | | | |
// | V | V | | |
reg[19:0] bar_height = 20'b11111111110000000000; // Holds bar height | |
// 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 | |
// Shrink = shift left | |
if (bar_height == 20'b11111111110000000000) | |
bar_height <= bar_height; | |
else | |
bar_height <= bar_height << 1; | |
end | |
else begin | |
// Grow = shift right | |
if (bar_height == 20'b00000000001111111111) | |
bar_height <= bar_height; | |
else | |
bar_height <= bar_height >> 1; | |
end | |
end | |
else | |
bar_height <= bar_height; | |
end | |
// Route to pins | |
assign | |
pin13 = bar_height[9], | |
pin12 = bar_height[8], | |
pin11 = bar_height[7], | |
pin10 = bar_height[6], | |
pin9 = bar_height[5], | |
pin8 = bar_height[4], | |
pin7 = bar_height[3], | |
pin6 = bar_height[2], | |
pin5 = bar_height[1], | |
pin4 = bar_height[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
Turn rotary clockwise and it counts up, counter clockwise and it counts down.
See rotary_encoder.v for circuit and diagrams.