按键消抖
RTL代码
`timescale 1ns / 1ps
//按键消抖
module key_filter(
clk,
reset_n,
key,
// key_p_flag,
// key_n_flag,
//当出现电平变化时(按键按下或松开),会有flag标记
key_flag,
key_state
);
input clk;
input reset_n;
input key;
// output reg key_p_flag;
// output reg key_n_flag;
output key_flag;
//两种按键状态,1为松开,0为按下
output reg key_state;
wire key_p_flag;
wire key_n_flag;
//无论是按键松开还是按下,出现电平变化,都会有flag
assign key_flag = (key_p_flag|key_n_flag);
//电平检测(上升沿或下降沿)
reg [2-1:0] r_key;
always@(posedge clk)begin
r_key[0]<=key;
r_key[1]<=r_key[0];
end
//等效写法
// always@(posedge clk)
// r_key<={r_key[0],key};
wire pedge_key;
wire nedge_key;
assign pedge_key = (r_key==2'b01);
assign nedge_key = (r_key==2'b10);
//状态机的写法!!非常重要
//尤其是对于不同状态的参数定义!!
reg [4-1:0] state;
parameter [4-1:0] IDLE = 4'B0001,
P_FLITER = 4'b0010,
WAIT_R = 4'b0100,
R_FILTER = 4'b1000;
//分频计数1us
parameter time_cnt = 1_000_000-1;
//分频计数
reg [20-1:0] div_cnt;
always@(posedge clk or negedge reset_n)begin
if(reset_n==0)
div_cnt<=0;
else if(div_cnt==time_cnt)
div_cnt<=0;
else
div_cnt<=div_cnt+1;
end
//状态机!
always@(posedge clk or negedge reset_n)begin
if(reset_n==0)begin
state<=IDLE;
key_p_flag<=0;
key_n_flag<=0;
key_state<=1;
end
else
case(state)
IDLE:begin
key_n_flag<=0;
if(nedge_key==1)
state<=P_FLITER;
else if(nedge_key==0)begin
state<=IDLE;
div_cnt<=0;
end
end
P_FLITER:begin
if((div_cnt<=time_cnt)&&(pedge_key==1))begin
state<=IDLE;
div_cnt<=0;
end
else if((div_cnt>=time_cnt)&&(pedge_key==0))begin
state<=WAIT_R;
key_p_flag<=1;
key_state<=0;
end
else
state<=P_FLITER;
end
WAIT_R:begin
key_p_flag<=0;
if(pedge_key==1)
state<=R_FILTER;
else if(pedge_key==0) begin
state<=WAIT_R;
div_cnt<=0;
end
end
R_FILTER:begin
if((div_cnt<=time_cnt)&&(nedge_key==1))begin
state<=WAIT_R;
div_cnt<=0;
end
else if((div_cnt>=time_cnt)&&(nedge_key==0))begin
state<=IDLE;
key_n_flag<=1;
key_state<=1;
end
else
state<=R_FILTER;
end
endcase
end
endmodule
状态转移图
代码解析
- 按键在松开状态电平为高,按下为低,在过程中会出现抖动,导致电平出现变化
- 按键消抖原理,在于监视电平,当按键按下时,电平变为0,并持续一段时间(自行拟定)后,视作按下完成
- 在按键松开时,电平变为1,并持续一段时间(自行拟定)后,视作松开完成
- 状态机的rtl代码实现很简单,case语句即可实现,但需要关注的点在于状态跳转条件
- 在状态条件中,对于分频计数,需要关注何时清零,这可能会对于仿真波形干扰
测试平台
`timescale 1ns / 1ps
module key_filter_tb();
reg clk;
reg reset_n;
reg key;
// wire key_p_flag;
// wire key_n_flag;
wire key_flag
wire key_state;
key_filter key_filter(
clk,
reset_n,
key,
// key_p_flag,
// key_n_flag,
key_flag,
key_state
);
initial begin
clk = 1;
forever #10 clk = ~clk;
end
initial begin
reset_n=0;
key=1;
#201;
reset_n=1;
#200;
key_data(8'b1010_0101);
#30_000_000;
$stop;
end
task key_data;
input [8-1:0] tx_data;
begin
key=1;
#20;
key = tx_data[0];
#30_000_000;
key = tx_data[1];
#20;
key = tx_data[2];
#20;
key = tx_data[3];
#20;
key = tx_data[4];
#30_000_000;
key = tx_data[5];
#20;
key = tx_data[6];
#20;
key = tx_data[7];
#20;
end
endtask
endmodule