按键的抖动
按键在按下过程中,触点接触和断开瞬间都会产生机械抖动,如果不进行处理,每一次按键就会产生若干次的响应。在单片机中一般使用delay函数来去除抖动,那么硬件去抖动该如何实现呢?
按键去抖动电路
原理
该按键消抖电路主要由四个移位寄存器构成,在按键按下时,每个时钟上升沿都会对key_in进行检测,只有当连续四个时钟上升沿都检测到键值为1时,key
Verilog 结构描述
源代码
module xiaodou_structure(key_in,clk,key_out);
input key_in,clk;
output key_out;
reg q0,q1,q2,q3;
always @(posedge clk)
begin
q3 <= q2;
q2 <= q1;
q1 <= q0;
q0 <= key_in;
end
assign key_out=q0&q1&q2&q3;
endmodule
我一直对阻塞赋值和非阻塞赋值有疑惑,如果不能确定这样写是否会综合为移位寄存器,我们也可以这样写:
module xiaodou_structure2(key_in,clk,key_out);
input key_in,clk;
output key_out;
reg [3:0] q;
always @(posedge clk)
begin
q={q[2:0],key_in};
end
assign key_out=(q===4'b1111)?1:0;
endmodule
检查一下,综合器综合后生成的网表,确实是一个4位的移位寄存器。
Technology Map Viewer
再看看仿真的结果,也确实达到了消抖的效果
仿真波形
Verilog 行为描述
源代码
module xiaodou_action(key_in,clk,key_out);
input key_in,clk;
output reg key_out;
reg [1:0] state;
parameter S0='d0,S1='d1,S2='d2,S3='d3;
always @(posedge clk)
begin
case(state)
S0: begin
if(key_in) begin key_out<=0; state<=S1; end
else begin key_out<=0; state<=S0; end
end
S1: begin
if(key_in) begin key_out<=0; state<=S2; end
else begin key_out<=0; state<=S0; end
end
S2: begin
if(key_in) begin key_out<=0; state<=S3; end
else begin key_out<=0; state<=S0; end
end
S3: begin
if(key_in) begin key_out<=1; state<=S0; end //S3改为S0
else begin key_out<=0; state<=S0; end
end
default state<=S0;
endcase
end
endmodule
这段代码其实就是用状态机设计的1111序列检测器,这里我做了微微的改动,在日常经验中我们知道,如果长按某个按键,按键按下且稳定时,每隔一段时间,就会有一个按键有效脉冲,这样就可以实现对某个参数的连续调整。所以这里我将S3下一个状态设定为S0,而不是保持S3,这样在按键长按的情况下,每4个时钟就会有一个按键有效脉冲输出。
RTL
仿真结果
从图中可以看到,在第二次按键按下时,一共输出了3个按键有效脉冲。
Testbench
以上仿真所使用的testbench主要代码均相同,具体如下:
initial
begin
clk=0;
key_in=0;
//按下
#20 key_in=1;
#3 key_in=0;
#2 key_in=1;
#1 key_in=0;
#1 key_in=1;
#3 key_in=0;
#2 key_in=1;
#3 key_in=0;
#2 key_in=1;
//稳定
#90 key_in=0;
//松手
#2 key_in=1;
#1 key_in=0;
#1 key_in=1;
#3 key_in=0;
#2 key_in=1;
#3 key_in=0;
//按下
#150 key_in=1;
#3 key_in=0;
#2 key_in=1;
#1 key_in=0;
#1 key_in=1;
#3 key_in=0;
#2 key_in=1;
#3 key_in=0;
#2 key_in=1;
//稳定
#280 key_in=0;
//松手
#2 key_in=1;
#1 key_in=0;
#1 key_in=1;
#3 key_in=0;
#2 key_in=1;
#3 key_in=0;
#100 $stop;
$display("Running testbench");
end
always
begin
#10 clk=~clk;
end
endmodule
注意
最后需要注意的是时钟clk的选取,不能过大,一般取100Hz以下,但如果clk过小,也有可能得不到按键有效脉冲。
若文档有任何错误或不足,欢迎在博客下方留言指正;