项目名称
按键消抖设计
具体要求
独立按键消抖并控制4个led二进制增加显示
设计说明
按键分类比较多,这里主要以普通按键为例进行设计,按键未按下时IO口为高电平,按下时为低电平,当按下或者松开的时候会产生额外的物理抖动。产生抖动的次数与间隔是不确定的,一般情况下单次抖动的总时间会在20ms之内。
一般用软件实现按键消抖,在消抖之前先消除亚稳态,因为按键对FPGA来说是外部信号,先对按键信号进行同步处理,对按键信号寄存两次进行输出,同步到fpga时钟上。设计一个20ms的计数器,对按键消抖进行计数,然后对按键的上升沿与下降沿进行检测,当按下按键时,即出现下降沿,开始计数20ms,计数完成后,如果仍出现下降沿,表明按键消抖未完成继续消抖,否则消抖完成处于按下稳定期,此时如果出现上升沿,继续进行20ms计数,计数完成后,如果仍出现上升沿证明释放消抖未完成,继续消抖,否则按键处于释放稳定期。
按键消抖的状态转移图如下,根据状态转移图进行代码设计。
设计架构
key_flag表示按下稳定标志信号,key_state表示按键状态
代码设计
顶层模块设计
module key_top(
input clk,
input rst_n,
input key_in,
output [3:0] led_out
);
wire key_flag,key_state;
key_filter key_filter(
. clk(clk),
. rst_n(rst_n),
. key_in(key_in),
. key_flag(key_flag),
. key_state(key_state)
);
led led(
.clk(clk),
.rst_n(rst_n),
.key_flag(key_flag),
.key_state(key_state),
.led_out(led_out)
);
endmodule
按键消抖模块
module key_filter(
input clk,
input rst_n,
input key_in,
output reg key_flag,
output reg key_state
);
//异步信号处理
reg key_s0,key_s1;
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
key_s0<=0;
key_s1<=0;
end
else begin
key_s0<=key_in;
key_s1<=key_s0;
end
//上升沿与下降沿检测
reg key_in0;
reg key_in1;
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
key_in0<=0;
key_in1<=0;
end
else begin
key_in0<=key_s1;
key_in1<=key_in0;
end
wire nedge=key_in1 && (!key_in0);//下降沿检测
wire podge=(!key_in1) && key_in0;//上升沿检测
//20ms的计数
reg [24:0] cnt;
reg en_cnt;//使能计数
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
cnt<=0;
end
else if(en_cnt)begin
if(cnt<25'd1_000_000-1)
cnt<=cnt+1'b1;
else
cnt<=0;
end
else
cnt<=0;
wire delay_20ms_done=(cnt==25'd1_000_000-1) && (en_cnt);//计满标志信号
//状态机的设计
reg [2:0] state;
localparam IDLE =3'd0;
localparam FILTER1=3'd1;
localparam DOWM =3'd2;
localparam FILTER2=3'd3;
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
key_flag<=0;
key_state<=1;
state<=IDLE;
en_cnt<=0;
end
else begin
case(state)
IDLE : begin
if(nedge)begin
state<=FILTER1;
en_cnt<=1;
end
else
state<=IDLE;
end
FILTER1: begin
if(delay_20ms_done)begin
if(podge)begin
state<=IDLE;
en_cnt<=0;
end
else begin
state<=DOWM;
en_cnt<=0;
key_flag<=1;
key_state<=0;
end
end
else
state<=FILTER1;
end
DOWM : begin
key_flag<=0;
if(podge)begin
state<=FILTER2;
en_cnt<=1;
end
else begin
state<=DOWM;
end
end
FILTER2: begin
if(delay_20ms_done)begin
if(nedge)begin
state<=DOWM;
en_cnt<=0;
end
else begin
state<=IDLE;
en_cnt<=0;
key_state<=1;
end
end
else begin
state<=FILTER2;
end
end
default: begin
key_flag<=0;
key_state<=1;
state<=IDLE;
en_cnt<=0;
end
endcase
end
endmodule
led显示模块
module led(
input clk,
input rst_n,
input key_flag,
input key_state,
output [3:0]led_out
);
reg [3:0] led_r;
always@(posedge clk or negedge rst_n)
if(!rst_n)
led_r<=4'b0000;
else if(key_flag && ~key_state)
led_r<=led_r+1'b1;
else
led_r<=led_r;
assign led_out=~led_r;
endmodule