一、设计思路
nedge:检测到按键出现下降沿
pedge:检测到按键出现上升沿
end_cnt_20ms:计数器计满20ms
parameter IDLE = 4'b0001; //空闲状态
parameter S1 = 4'b0010; //按下
parameter S2 = 4'b0100; //等待
parameter S3 = 4'b1000; //释放
二、按键防抖代码
module key_filter(
clk ,
rst_n ,
key ,
key_state ,
key_flag
);
parameter IDLE = 4'b0001; //空闲状态
parameter S1 = 4'b0010; //按下
parameter S2 = 4'b0100; //等待
parameter S3 = 4'b1000; //释放
parameter CNTF_N = 1000000;
parameter CNTF_W = 20;
parameter STATE_W = 4; //状态机位宽
input clk;
input rst_n;
input key;
output key_flag;
output key_state;
reg key_p_flag;
reg key_r_flag;
wire key_flag;
reg key_state;
reg [CNTF_W-1:0] cnt_filter;
wire add_cnt_filter;
wire end_cnt_filter;
reg [STATE_W-1:0] state_c;
reg [STATE_W-1:0] state_n;
reg [2:0] key_sync;
wire IDLE2S1_start;
wire S12IDLE_start;
wire S12S2_start ;
wire S22S3_start ;
wire S32S2_start ;
wire S32IDLE_start;
wire nedge_flag;
wire pedge_flag;
//20ms计数器
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_filter <= 0;
else if(add_cnt_filter)begin
if(end_cnt_filter)
cnt_filter <= 0;
else
cnt_filter <= cnt_filter + 1'b1;
end
else
cnt_filter <= 0;
end
assign add_cnt_filter = (state_c == S1) || (state_c == S3);
assign end_cnt_filter = add_cnt_filter && cnt_filter == CNTF_N - 1;
//状态机
//状态转移描述
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
state_c <= IDLE;
else
state_c <= state_n;
end
//状态条件转移描述
always @(*)begin
case(state_c)
IDLE:begin
if(IDLE2S1_start)
state_n = S1;
else
state_n = state_c;
end
S1:begin
if(S12IDLE_start)
state_n = IDLE;
else if(S12S2_start)
state_n = S2;
else
state_n = state_c;
end
S2:begin
if(S22S3_start)
state_n = S3;
else
state_n = state_c;
end
S3:begin
if(S32S2_start)
state_n = S2;
else if(S32IDLE_start)
state_n = IDLE;
else
state_n = state_c;
end
default:state_n = IDLE;
endcase
end
//状态条件
assign IDLE2S1_start = state_c == IDLE && nedge_flag;
assign S12IDLE_start = state_c == S1 && pedge_flag;
assign S12S2_start = state_c == S1 && end_cnt_filter;
assign S22S3_start = state_c == S2 && pedge_flag;
assign S32S2_start = state_c == S3 && nedge_flag;
assign S32IDLE_start = state_c == S3 && end_cnt_filter;
//状态机输出
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
key_p_flag <= 0;
else if(S12S2_start)
key_p_flag <= 1;
else
key_p_flag <= 0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
key_r_flag <= 0;
else if(S32IDLE_start)
key_r_flag <= 1;
else
key_r_flag <= 0;
end
assign key_flag = (key_p_flag) || (key_r_flag);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
key_state <= 1;
else if(S12S2_start)
key_state <= 0;
else if(S32IDLE_start)
key_state <= 1;
end
//边沿检测
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
key_sync <= 3'b111;
else
key_sync <= {key_sync[1:0],key};
end
assign nedge_flag = key_sync[2:1]==2'b10;
assign pedge_flag = key_sync[2:1]==2'b01;
endmodule
三、按键防抖仿真文件
`timescale 1ns / 1ns
module key_filter_tb();
parameter CYCLE = 20;
reg clk;
reg rst_n;
reg key;
wire key_state;
wire key_flag;
reg [31:0] rand;
key_filter key_filter(
clk ,
rst_n ,
key ,
key_state ,
key_flag
);
initial clk = 1;
always #(CYCLE/2) clk = ~clk;
initial begin
rst_n = 1;
#3;
rst_n = 0;
#(10 * CYCLE);
rst_n = 1;
end
initial begin
key = 1;
#(15*CYCLE);
press_key(3);
$stop;
end
task press_key;
input [3:0] seed;
begin
key = 1;
#(20_000_000);
rand = {$random(seed)}%1_000_000; //0~999999
repeat(5)begin
#rand key = ~key;
end
key =0;
#(25_000_000);
repeat(5)begin
#rand key = ~key;
end
key = 1;
#(25_000_000);
end
endtask
endmodule
四、板级验证代码
module key_top(
clk ,
rst_n ,
key_in ,
led
);
//参数定义
parameter LED_W = 4;
//输入信号定义
input clk ;
input rst_n ;
input key_in ;
//输出信号定义
output[LED_W-1:0] led ;
//输出信号reg定义
wire [LED_W-1:0] led ;
wire key ;
//中间信号定义
wire key_flag ;
wire key_state ;
reg [LED_W-1:0] cnt ;
wire add_cnt ;
wire end_cnt ;
assign key = (key_flag && !key_state);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
end
assign add_cnt = (key == 1);
assign end_cnt = add_cnt && cnt == 16 - 1;
assign led = cnt;
key_filter key_filter(
.clk(clk) ,//50MHz时钟
.rst_n(rst_n) ,//复位
.key(key_in) ,//按键输入
.key_flag(key_flag) ,//按键计时标志信号
.key_state(key_state) //按键状态信号
);
endmodule