新建工程,建立key_filter.v文件和key_filter_tb.v文件
input Clk;
input Rst_n;
input key_in;
output reg key_flag;
output reg key_state;
reg [19:0] cnt;
reg en_cnt;//使能计数寄存器,在使能之后i,再开始计数
reg key_temp0,key_temp1;
wire pedge,nedge;//pedge上升沿 nedge下降沿
reg cnt_full;//计数满寄存器,当计数满了之后,让cnt_full置1
//定义状态值
localparam
IDLE =4'b0001,//空闲状态
FILTER0 =4'B0010,//按键抖动期
DOWN =4'B0100,//按下稳定期
FILTER1 =4'B1000;
reg [3:0]state;//状态
//边沿检测,检测下降沿。如果想知道它是否有下降沿,我们可以判断,如果前一个时刻是高电平,下一个时刻是低电平,那么一定会有一个下降沿
//所以对前一时刻和后一时刻的状态寄存,再通过组合逻辑进行比较,来判断是否有下降沿
//通过时钟对信号进行采样
//按键输入key_in到reg0,reg0的输出连接到reg1的输入,两个reg是同一个时钟
//当key_in第一次输入为高电平,那么就保存到reg0.下一个时钟上升沿到来时,reg0里面的数值传输到reg1,同时当key_in第二次为低电平,则reg0又记录key_in的新的状态
//通过比较两个寄存器的输出,就可以进行比较
//将reg0和reg1第三次时钟的时候的输出的信息,将reg0通过取反之后,再将两者通过与门
// reg key_temp0,key_temp1;
// wire pedge,nedge;//pedge上升沿 nedge下降沿
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
key_temp0=1'b0;
key_temp1=1'b0;//当复位的时候,给两个数值清零
end
else begin
key_temp0<=key_in;
key_temp1<=key_temp0;//否则,当时钟上升沿到来时,key_temp0记录这个时候的key_in的值,同时把key_temp0里面的值传给key_temp1.注意这是非阻塞赋值,二者是同时进行的,也就是说,key_temp1里面保存的是上一个时钟上升沿中,key_temp0所保存的值。
end
end
assign nedge = !key_temp0 & key_temp1;
assign pedge = key_temp0 & (!key_temp1);//!取反 ~按位取反 因为这里temp是一位的,所以! 和 ~是一样的
//0110 ~ 1001
//0110 ! 0000
//接下来编写状态机
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
state <= IDLE;//复位状态的时候,让状态处于空闲状态,而且关闭计数器使能信号
en_cnt<=1'b0;
key_flag<=1'b0;
key_state<=1'b1;
end
else begin
case(state)
IDLE://
begin
key_flag<=1'b0;
if(nedge)begin//处于空闲状态的时候,如果检测到了下降沿,就进入下一个抖动状态
//此时还需要使能我们的计数器en_cnt
state<=FILTER0;
en_cnt<=1'b1;
end
else
state<=IDLE;
end
FILTER0:
if(cnt_full)begin //当计数器满的时候,也就是说计数到了
key_flag<=1'b1;//那么它确实是一个下降沿,那么久给flag置一,同时给key_state置零
key_state<=1'b0;
state<=DOWN;
en_cnt<=1'b0;
end
else if(pedge)begin
state<=IDLE;//否则,当我们检测到上升沿的时候,说明这只是一次抖动,我们回到idle状态,等待下一个时钟下降沿的到来
en_cnt<=1'b0;
end
else//没有计满,也没有检测到上升沿
state<=FILTER0;
DOWN:
begin
key_flag<=1'b0;
if(pedge)begin//这个时候检测到上升沿了,按键可能开始释放了,那么就让state进入滤波状态
state<=FILTER1;
en_cnt<=1'b1;//让计数器开始计数
end
else
state<=DOWN;
end
FILTER1:
if(cnt_full)begin
state<=IDLE;
key_flag<=1'b1;
key_state<=1'b1;
end
else if(nedge)begin //如果计数还没有满,说明这只是一次抖动
en_cnt<=1'b0;
state<=DOWN;
end
else
state<=FILTER1;
default:begin
state<=IDLE;
en_cnt<=1'b0;
key_flag<=1'b0;
key_state<=1'b1;
end
endcase
end
//系统时钟是50 000 000,20ns
//我们延时20ms,20——000——000ns
//我们需要计数1 000 000次
//当在idle状态中检测到下降沿的时候,我们开始计数
//在filter0状态中,如果我们检测到了上升沿或者计数还没有计满的时候
// reg [19:0] cnt;
// reg en_cnt;//使能计数寄存器,在使能之后i,再开始计数
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt<=20'd0;
else if(en_cnt)
cnt<=cnt+1'b1;
else
cnt<=20'd0;
// reg cnt_full;//计数满寄存器,当计数满了之后,让cnt_full置1
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_full<=1'b0;
else if(cnt==999_999)
cnt_full<=1'b1;
else
cnt_full<=1'b0;
endmodule
`timescale 1ns/1ns
`define clock_period 20
module key_filter_tb;
reg Clk;
reg Rst_n;
reg key_in;
wire key_flag;
wire key_state;
key_filter key_filter0
(
.Clk