按键消抖设计
独立按键消抖并控制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 endmoduleled显示模块
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
