This page is a note of FPGA key check.
`timescale 1ns / 1ps
//
//
// Create Date: 2023/12/02 14:44:05
// Module Name: ax_debounce
// Tool Versions: Vivado2022.2
//
//
module ax_debounce(
input clk , //system clock 50Mhz on board
input rst_n , //reset input, low active
input button_in , //button sign input
output reg button_en //button sign output
);
parameter N = 32 ; //time count wide
parameter FREQ = 50 ; //clock frequency(Mhz)
parameter MAX_TIME = 20 ; //delay time(ms)
localparam TIMER_MAX_VAL = MAX_TIME * 1000 * FREQ; //time count sign
//wire define
wire q_add ; //button dithering add sign
wire q_reset ; //signal keep sign
//reg define
reg DFF1 ; //the first reg to catch button_in
reg DFF2 ; //the second reg to catch button_in
reg button_out ; //the first button dithering reg to catch sign
reg button_out_d0 ; //the second button dithering reg to catch sign
reg [N-1 : 0] q_reg ; //button dithering time count reg
reg [N-1 : 0] q_next ; //together to q_reg in order to add button dithering time
//assign define
//if q_reg less than TIMER_MAX_VAL then q_add is high level
//if q_reg equal to TIMER_MAX_VAL then q_add is low level
assign q_add = ~(q_reg == TIMER_MAX_VAL) ;
//if signal changed then set q_reset high level
//if signal keep then q_reset is low level
assign q_reset = (DFF1 ^ DFF2) ;
always@(q_reset , q_add , q_reg) begin
case({q_reset , q_add})
//if signal keep and q_reg equal to TIMER_MAX_VAL then q_reg stop add
2'b00: begin
q_next <= q_reg;
end
//if signal keep and q_reg less than TIMER_MAX_VAL then q_reg add one
2'b01: begin
q_next <= q_reg + 1'b1;
end
//if any error happen empty reg q_next
default: begin
q_next <= {N{1'b0}};
end
endcase
end
//use two reg DFF1 and DFF2 to catch the sigal from button_in
//load q_next into q_reg
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
DFF1 <= 1'b0;
DFF2 <= 1'b0;
q_reg <= {N{1'b0}};
end
else begin
DFF1 <= button_in;
DFF2 <= DFF1;
q_reg <= q_next;
end
end
//when finish button dithering load DFF2 to button_out
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
button_out <= 1'b1;
end
else if(q_reg == TIMER_MAX_VAL) begin
button_out <= DFF2;
end
else begin
button_out <= button_out;
end
end
//indeed there two button dithering
//the first button dithering data in the button_out_d0 is low level
//when the second button dithering finish is load high level into button_out_d0
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
button_out_d0 <= 1'b1;
button_en <= 1'b0;
end
else begin
button_out_d0 <= button_out;
button_en <= button_out_d0 & ~button_out;
end
end
endmodule
Please follow me to understand this method.
As we all know we should catch the button edge change and check whether this is just a button dithering. So we need a cnt to count time when this singal is changed and if time count enough time we think this is a truly button click.
I use two regs DFF1 and DFF2 to catch sigal from button_in. If DFF1 is same to DFF2 means no edge change, q_reset will be 0(q_reset = DFF1 ^ DFF2). And whether button_in from 0 to 1 or 1 to 0, q_reset will be 1 and empty the time_cnt(q_reg). q_add means if q_reg is less than TIMER_MAX_VAL, it will be 1 and count time unless q_reg equal to TIMER_MAX_VAL.
Now you may could understand the code below.
//if q_reg less than TIMER_MAX_VAL then q_add is high level
//if q_reg equal to TIMER_MAX_VAL then q_add is low level
assign q_add = ~(q_reg == TIMER_MAX_VAL) ;
//if signal changed then set q_reset high level
//if signal keep then q_reset is low level
assign q_reset = (DFF1 ^ DFF2) ;
always@(q_reset , q_add , q_reg) begin
case({q_reset , q_add})
//if signal keep and q_reg equal to TIMER_MAX_VAL then q_reg stop add
2'b00: begin
q_next <= q_reg;
end
//if signal keep and q_reg less than TIMER_MAX_VAL then q_reg add one
2'b01: begin
q_next <= q_reg + 1'b1;
end
//if any error happen empty reg q_next
default: begin
q_next <= {N{1'b0}};
end
endcase
end
//use two reg DFF1 and DFF2 to catch the sigal from button_in
//load q_next into q_reg
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
DFF1 <= 1'b0;
DFF2 <= 1'b0;
q_reg <= {N{1'b0}};
end
else begin
DFF1 <= button_in;
DFF2 <= DFF1;
q_reg <= q_next;
end
end
Now we need to think how can we output enable just for one cycle.
We know reg can storage sigal for a cycle, so we can use this to output.
//when finish button dithering load DFF2 to button_out
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
button_out <= 1'b1;
end
else if(q_reg == TIMER_MAX_VAL) begin
button_out <= DFF2;
end
else begin
button_out <= button_out;
end
end
//indeed there two button dithering
//the first button dithering data in the button_out_d0 is low level
//when the second button dithering finish is load high level into button_out_d0
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
button_out_d0 <= 1'b1;
button_en <= 1'b0;
end
else begin
button_out_d0 <= button_out;
button_en <= button_out_d0 & ~button_out;
end
end
At first button_out and button_out_d0 both be 1, when we consider button is clicked 0 will be written in to button_out. However there is a cycle button_out is 0 but button_out_d0 is 1, and after this cycle button_out_d0 will be written 0 from button_out_d0. So sigal button_en just output one cycle.
This is a method in usual case to check button click I guess.