按键消抖的三种方案
首先,做两个假定,以方便后面的描述:
- 假定按键的默认状态为0,被按下后为1
- 假定按键抖动时长小于20ms,也即使用20ms的消抖时间
方案1:在按键电平稳定的情况下,当第一次检测到键位电平变化,开始20ms计时,计时时间到后将按键电平更新为当前电平
方案2:在20ms计时的过程中,有任何的电平变化都立即复位计时
方案3:在有电平变化时立即改变按键输出电平,并开始20ms计时,忽略这其中抖动
1、方案1代码:
module debounce(
input wire clk, nrst,
input wire key_in,
output reg key_out
);
// 20ms parameter
// localparam TIME_20MS = 1_000_000;
localparam TIME_20MS = 1_000; // just for test
// variable
reg [20:0] cnt;
reg key_cnt;
// debounce time passed, refresh key state
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
key_out <= 0;
//在20ms结束,才将key_in赋值给key_out
else if(cnt == TIME_20MS - 1)
key_out <= key_in;
end
// while in debounce state, count, otherwise 0
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
cnt <= 0;
else if(key_cnt)
cnt <= cnt + 1'b1;
else
cnt <= 0;
end
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
key_cnt <= 0;
else if(key_cnt == 0 && key_in != key_out)
key_cnt <= 1;
else if(cnt == TIME_20MS - 1)
key_cnt <= 0;
end
endmodule
2、方案2代码:
module debounce(
input wire clk, nrst,
input wire key_in,
output reg key_out
);
//localparam TIME_20MS = 1_000_000;
localparam TIME_20MS = 1_000;
reg key_cnt;
reg [20:0] cnt;
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
key_cnt <= 0;
else if(cnt == TIME_20MS - 1)
key_cnt <= 0;
else if(key_cnt == 0 && key_out != key_in)
key_cnt <= 1;
end
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
cnt <= 0;
else if(key_cnt) begin
//发生任何抖动,都立即复位,是通过这里实现的
//因为正常情况下,按下或者松开按键都会导致key_in发生变化
//以至于和key_out不相等。所以,如果相等了,说明中间key_in发生了变化,
//那么我们就需要重新复位,重新计数。还有一种情况就是,key_in压根一开始就没变化,
//例如按键没有被按下或者松开,那么我们也一直复位,不让其计数,这样key_out也可以不变化了。
if(key_out == key_in)
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
else
cnt <= 0;
end
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
key_out <= 0;
//在20ms结束,才将key_in赋值给key_out
else if(cnt == TIME_20MS - 1)
key_out <= key_in;
end
endmodule
3、方案3代码:
module debounce(
input wire clk, nrst,
input wire key_in,
output reg key_out
);
//localparam TIME_20MS = 1_000_000;
localparam TIME_20MS = 1_000; // just for test
reg key_cnt;
reg [20:0] cnt;
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
key_cnt <= 0;
else if(key_cnt == 0 && key_out != key_in)
key_cnt <= 1;
else if(cnt == TIME_20MS - 1)
key_cnt <= 0;
end
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
cnt <= 0;
else if(key_cnt)
cnt <= cnt + 1'b1;
else
cnt <= 0;
end
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
key_out <= 0;
//一开始,就直接将key_in赋值给key_out,也即方案3中说的:
//只要一发生变化,就立即将输入赋给输出,然后保持20ms后输出。
else if(key_cnt == 0 && key_out != key_in)
key_out <= key_in;
end
endmodule