按键默认状态为高电平,当按键被按下并稳定后,为低电平,如图1所示:
图1
按键在被按下过程中的电平变化如图2所示:
图2
据统计,按键单次抖动时间小于20ms,此处可用计数器 进行计时;根据图2,按键被按下过程可以分解为4中状态:
1、当按键被按下抖动期间;
2、按键被按下;
3、按键被释放抖动y
4、按键释放后恢复高电平。
因此,可以采用状态机进行设计,可分为4个状态:
IDEL:等待按键被按下,即下降沿的来临,跳转到FILTER0;
FILTER0:计数按键被按下后单次抖动所耗时间,当时间大于20ms时,跳转到DOWN;
DOWN:按键确定被按下,等待上升沿来临,跳转到FILTER1;
FILTER1:计数按键被释放后单次抖动所耗时间,当时间大于20ms时,按键被释放,按键的整个过程结束,跳转到IDEL,等待下一轮按键被按下;
图3 状态图
按键消抖工程代码如下所示:
module key_filter(
input clk,
input rst_n,
input key,
output reg key_flag,
output reg key_state);
reg[2:0] key_shift;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_shift <= 3'b111;
end
else begin
key_shift <= {key_shift[1:0],key};
end
end
wire key_pos;
assign key_pos = key_shift[1] && (!key_shift[2]);
wire key_neg;
assign key_neg = (!key_shift[1]) && key_shift[2];
localparam IDEL = 4'b0000;
localparam FILTER0 = 4'b0001;
localparam DOWN = 4'b0011;
localparam FILTER1 = 4'b0010;
reg cnt_en;
reg[22:0] cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 23'd0;
end
else begin
if(cnt_en)begin
cnt <= cnt + 23'd1;
end
else begin
cnt <= 23'd0;
end
end
end
reg cnt_full;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_full <= 1'b0;
end
else begin
if(cnt == 23'd399999)begin
cnt_full <=1'b1;
end
else begin
cnt_full <= 1'b0;
end
end
end
reg[3:0] state;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_en <= 1'b0;
key_flag <= 1'b0;
key_state <= 1'b1;
state <= IDEL;
end
else begin
case(state)
IDEL:begin
key_flag <= 1'b0;
if(key_neg)begin
cnt_en <= 1'b1;
state <= FILTER0;
end
else begin
state <= IDEL;
end
end
FILTER0:begin
if(cnt_full)begin
cnt_en <= 1'b0;
key_flag <= 1'b1;
key_state <= 1'b0;
state <= DOWN;
end
else begin
if(key_pos)begin
cnt_en <= 1'b0;
state <= IDEL;
end
else begin
state <= FILTER0;
end
end
end
DOWN:begin
key_flag <= 1'b0;
if(key_pos)begin
cnt_en <= 1'b1;
state <= FILTER1;
end
else begin
state <= DOWN;
end
end
FILTER1:begin
if(cnt_full)begin
cnt_en <= 1'b0;
key_flag <= 1'b1;
key_state <= 1'b1;
state <= IDEL;
end
else begin
if(key_neg)begin
cnt_en <= 1'b0;
state <= DOWN;
end
else begin
state <= FILTER1;
end
end
end
default:begin
cnt_en <= 1'b0;
key_flag <= 1'b0;
key_state <= 1'b1;
state <= IDEL;
end
endcase
end
end
endmodule
此处对输入的信号key打了三拍,第一拍实现异步信号转同步,后两拍实现处理亚稳态;本工程还采用用来沿触发技术。
模拟真实按键被按下的过程,其模块代码如下所示:
`timescale 1ns/1ps
module key_mode(
output reg key);
initial begin
key = 1'b1;
press_key;
#10000;
press_key;
#10000;
press_key;
#10000;
$stop;
end
reg[15:0] myrand;
task press_key;
begin
repeat(50)begin
myrand = {$random} % 65535;
# myrand key = ~key;
end
key = 1'b0;
#25000000;
repeat(50)begin
myrand = {$random} % 65535;
# myrand key = ~key;
end
key = 1'b1;
#25000000;
end
endtask
endmodule
仿真代码如下所示:
`timescale 1ns/1ps
`define clk_period 20
module key_filter_tb;
reg clk;
reg rst_n;
wire key;
wire key_flag;
wire key_state;
key_mode key_mode_inst(
.key(key));
key_filter key_filter_inst(
.clk(clk),
.rst_n(rst_n),
.key(key),
.key_flag(key_flag),
.key_state(key_state));
initial clk = 1'b0;
always #(`clk_period / 2) clk = ~clk;
initial begin
rst_n = 1'b0;
#(`clk_period*10);
rst_n = 1'b1;
#(`clk_period*10 + 1);
end
endmodule
工程代码可百度网盘下载
链接:https://pan.baidu.com/s/1Vb-7TB-SeIbt6Zq84TsBSA
提取码:foge