Verilog实现按键消抖(状态机方法)

文章目录

      • 按键抖动分析
      • 按键消抖逻辑
      • Verilog设计与仿真
      • Modelsim工程下载

按键抖动分析

Verilog实现按键消抖(状态机方法)_第1张图片
常用的轻触按键内部结构为金属弹片,在手按下、松开的过程中往往会发生细微抖动。
Verilog实现按键消抖(状态机方法)_第2张图片
输出的逻辑电平也会发生快速翻转,按键按下和释放的过程中,都会产生抖动,虽然时间非常短暂,但是对于单片机、FPGA这种实时性非常高的系统来说是不可接受的,为了保证系统能正确识别按键的开关,必须对按下和释放的过程进行滤波处理。
Verilog实现按键消抖(状态机方法)_第3张图片

按键消抖逻辑

和单片机消抖逻辑类似,FPGA可以通过两个计数器来对持续的高低电平进行计时,当达到一定的时间时,可以认为触点稳定,可以认为是一次有效的按下或释放,从而输出对应的信号。

Verilog设计与仿真

下面是基于Verilog状态机思路,使用两个计数器分别对按下和释放进行滤波处理,理论上按下和释放不会同时出现,所以使用一个计数器也可以实现的,为了便于理解,我们使用两个计数器分别对高低电平进行计时。

按键消抖驱动模块drv_key.v

/*********************************************************************
 * Copyright(C), 2010-2023, CSDN @ whik1194
 * ModuleName : drv_key.v 
 * Date       : 2023年2月20日
 * Time       : 21:26:01
 * Author     : https://blog.csdn.net/whik1194
 * Function   : drv_key
 * Version    : v1.0
 *      Version | Modify
 *      ----------------------------------
 *       v1.0    .....
 *********************************************************************/

module drv_key(
    //Inputs
    input clk,
    input rst_n,
    input key,
    
    //Outputs
    output flag_press,
    output flag_release
);

//parameter 
parameter KEY_PRESS   = 1'b0;       //按键按下有效
parameter FILTER_TIME = 27_000_0;   //按键消抖时间

//localparam 
localparam KEY_RELEASE = !KEY_PRESS;
localparam S0_IDLE = 0;
localparam S1_KEY_PRESS = 1;
localparam S2_KEY_RELEASE = 2;

//reg
reg [31:0] cnt_press;
reg [31:0] cnt_release;
reg [4:0] key_sreg;       //shift reg
reg [3:0] fsm;
reg en;

wire key_is_press   = (key_sreg[4:1] == {4{KEY_PRESS}});
wire key_is_release = (key_sreg[4:1] == {4{KEY_RELEASE}});
wire key_is_shake   = !(key_is_press || key_is_release);

//assign
assign flag_press   = (fsm == S1_KEY_PRESS);
assign flag_release = (fsm == S2_KEY_RELEASE);

//always 
//按键边沿捕获移位寄存器
always @ (posedge clk) begin
    if(!rst_n) begin
        key_sreg <= {5{KEY_RELEASE}};
    end
    else begin
        key_sreg <= key_sreg << 1 | key;
    end
end

always @ (posedge clk) begin
    if(!rst_n) begin
        fsm <= S0_IDLE;
        cnt_press   <= 0;
        cnt_release <= 0;
        en <= 0;
    end
    else begin
        case (fsm)
            S0_IDLE: begin
                if(key_is_shake) begin
                    cnt_press <= 'd0;
                    cnt_release <= 'd0;
                    en <= 1;
                end                
                else if(key_is_press && en) begin
                    if(cnt_press < FILTER_TIME)
                        cnt_press <= cnt_press + 1;
                    else begin
                        cnt_press <= 0; //max = FILTER_TIME
                        fsm <= S1_KEY_PRESS;
                    end
                end
                else if(key_is_release && en) begin
                    if(cnt_release < FILTER_TIME)
                        cnt_release <= cnt_release + 1;
                    else begin
                        cnt_release <= 0; //max = FILTER_TIME
                        fsm <= S2_KEY_RELEASE;
                    end
                end
                else begin
                    fsm <= fsm;
                    cnt_press   <= 0;
                    cnt_release <= 0;
                    en <= 0;
                end
            end
            
            S1_KEY_PRESS: begin
                fsm <= S0_IDLE;
                en <= 0; 
            end
            
            S2_KEY_RELEASE: begin
                fsm <= S0_IDLE;
                en <= 0; 
            end
            
            default: begin
                fsm <= fsm;
            end
        endcase
    end
end

endmodule   //get_key end

Modelsim仿真激励模块drv_key_tb.v

/*********************************************************************
 * Copyright(C), 2010-2023, CSDN @ whik1194
 * ModuleName : drv_key.v 
 * Date       : 2023年2月20日
 * Time       : 21:26:01
 * Author     : https://blog.csdn.net/whik1194
 * Function   : drv_key
 * Version    : v1.0
 *      Version | Modify
 *      ----------------------------------
 *       v1.0    .....
 *********************************************************************/

`timescale 1ns/1ps

module drv_key_tb;

localparam PERIOD = 10;      //ns
localparam KEY_PRESS   = 1'b0; 
localparam KEY_RELEASE = !KEY_PRESS;
localparam FILTER_TIME = 500;   

reg clk;
reg rst_n;
reg key;

always #(PERIOD/2) clk <= !clk;

initial begin
    $display("testbench: %s", drv_key_tb);
    
    rst_n = 0;
    clk = 0;
    key = KEY_RELEASE;
    
    #(PERIOD*100)
    rst_n = 1;
    #(PERIOD*1000)
    
    task_press;
    task_release;
    task_press;
    task_release;
   
end

drv_key #(
    .FILTER_TIME(FILTER_TIME)
)drv_key(
    //Inputs
    .clk(clk),
    .rst_n(rst_n),
    .key(key),
    
    //Outputs
    .flag_press(),
    .flag_release()
);

integer i;
task task_press;
begin
    @(posedge clk);
    for(i = 0; i <= 10; i = i + 1) begin
        key = KEY_PRESS;
        #(100*PERIOD);
        @(posedge clk);
        key = !key;
        #(100*PERIOD);
        @(posedge clk);
    end
    
    key = KEY_PRESS;
    #(FILTER_TIME*PERIOD)
    #(FILTER_TIME*PERIOD)
    @(posedge clk);
    
end
endtask

task task_release;
begin
    @(posedge clk);
    for(i = 0; i <= 10; i = i + 1) begin
        key = KEY_RELEASE;
        #(100*PERIOD);
        @(posedge clk);
        key = !key;
        #(100*PERIOD);
        @(posedge clk);
    end
    
    key = KEY_RELEASE;
    #(FILTER_TIME*PERIOD)
    #(FILTER_TIME*PERIOD)
    @(posedge clk);
    
end
endtask

endmodule   //drv_key_tb end

Modelsim仿真波形:
Verilog实现按键消抖(状态机方法)_第4张图片
实际调用也非常方便,只需要指定按键按下的电平状态和滤波的周期即可:


drv_key #(
    .KEY_PRESS(1'b0),
    .FILTER_TIME(100_000_000/20) //100ms
)drv_key_ut0(
    //Inputs
    .clk(gclk),  	//50MHz=20ns
    .rst_n(gresetn),
    .key(key1),
    
    //Outputs
    .flag_press(flag_press),
    .flag_release(flag_release)
);

Modelsim工程下载

  • fsm_key_filter.rar

你可能感兴趣的:(FPGA,按键,Verilog,状态机,单片机)