基于状态机的按键消抖
本博文可参考小梅哥的视频学习!!
按键存在一个反作用弹簧,因此当按下或者松开时均会产生额外的物理抖动,物理抖动便会产生电平的抖动。在按键从按下再到松开的过程中,其电平变化如下图所示,上为理想波形输出,下为实际波形输出。
上图中,产生的抖动次数以及间隔时间均是不可预期的,这就需要通过滤波来消除抖动可能对外部其他设备造成的影响。一般情况下抖动的总时间会持续 20ms 以内。这种抖动,可以通过硬件电路或者逻辑设计的方式来消除,也可以通过软件的方式完成(这些方法可在csdn站内其他博文找到)。
1.设计文件
设计状态机
module key_filter(
Clk,
Reset_n,
key,
key_flag,
key_state
);
input Clk;
input Reset_n;
input key;
output key_flag;
output reg key_state;
//首先边沿检测 pedge 和nedge
wire pedge;
wire nedge;
reg [1:0] r_key;
always@(posedge Clk)
r_key <= {r_key[0],key};
assign pedge = r_key == 2'b01;
assign nedge = r_key == 2'b10;
// cnt计数20ms 1_000_000
reg [19:0] cnt;
//状态机
reg key_p_flag; //按下成功
reg key_r_flag; //释放成功
assign key_flag = (key_p_flag | key_r_flag); //“|”按位或 ; “||”逻辑或
reg [1:0] state;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)begin
state <= 0;
cnt <= 0;
key_p_flag <= 0;
key_r_flag <= 0;
key_state <= 1;
end
else begin
case (state)
0:begin
key_r_flag <= 0;
if(nedge == 1)begin
state <= 1;
cnt <= 0;
end
else
state <= 0;
end
1:begin
if((pedge == 1) && (cnt < 1_000_000 - 1))
state <= 0;
else if(cnt >= 1_000_000 - 1)begin
state <= 2;
key_p_flag <= 1;
key_state <= 0;
end
else begin
cnt <= cnt + 1;
state <= 1;
end
end
2:begin
key_p_flag <= 0;
if(pedge == 1)begin
cnt <= 0;
state <= 3;
end
else
state <= 2;
end
3:begin
if((cnt < 1_000_000 - 1)&&(nedge == 1))
state <= 2;
else if(cnt >= 1_000_000 - 1)begin
state <= 0;
key_r_flag <= 1;
key_state <= 1;
end
else begin
cnt <= cnt +1;
state <= 3;
end
end
endcase
end
endmodule
2.测试仿真
`timescale 1ns / 1ns
module key_filter_tb();
reg Clk;
reg Reset_n;
reg key;
wire key_flag;
wire key_state;
key_filter key_filter(
Clk,
Reset_n,
key,
key_flag,
key_state
);
initial Clk = 0;
always #10 Clk = !Clk;
initial begin
Reset_n = 0;
#201;
Reset_n = 1;
#3000;
key = 0;
#5_000_000;
key = 1;
#5_000_000;
key = 0;
#5_000_000;
key = 1;
#5_000_000;
key = 0;
#50_000_000;
key = 1;
#5_000_000;
key = 0;
#5_000_000;
key = 1;
#5_000_000;
key = 0;
#5_000_000;
key = 1;
#50_000_000;
$stop;
end
endmodule
巧用task和random 编写测试文件
`timescale 1ns / 1ns
module key_filter_tb2();
reg Clk;
reg Reset_n;
reg key;
wire key_flag;
wire key_state;
key_filter key_filter(
Clk,
Reset_n,
key,
key_flag,
key_state
);
initial Clk = 0;
always #10 Clk = !Clk;
initial begin
Reset_n = 0;
key = 1;
#201;
Reset_n = 1;
#3000;
press_key(1);
$stop;
end
reg [31:0] rand;
task press_key;
input [3:0] seed;
begin
key = 1;
#20_000_000;
repeat(5)begin
rand = {$random(seed)} % 10_000_000;//(0,9999999)
#rand key = ~key;
end
key = 0;
#40_000_000;
repeat(5)begin
rand = {$random(seed)} % 10000000;//(0,9999999)
#rand key = ~key;
end
key = 1;
#40_000_000;
end
endtask
endmodule
3.仿真结果
其中产生微差的原因主要是因为nedge,细节如下图所示:
tb2仿真结果如下:
关于此文章可能遇到亚稳态问题:
亚稳态问题参考文章:亚稳态专题(亚稳态的概念、亚稳态的产生、亚稳态的后果、以及如何避免亚稳态)
常见现象:在状态2中出不去
解决办法: