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

文章介绍了按键抖动问题及其在单片机和FPGA中的影响,提出了使用Verilog设计的按键消抖逻辑,通过两个计数器对高低电平进行滤波处理。提供了基于状态机的Verilog代码示例和Modelsim仿真激励模块,以确保系统能正确识别按键操作。
摘要由CSDN通过智能技术生成

按键抖动分析


常用的轻触按键内部结构为金属弹片,在手按下、松开的过程中往往会发生细微抖动。

输出的逻辑电平也会发生快速翻转,按键按下和释放的过程中,都会产生抖动,虽然时间非常短暂,但是对于单片机、FPGA这种实时性非常高的系统来说是不可接受的,为了保证系统能正确识别按键的开关,必须对按下和释放的过程进行滤波处理。

按键消抖逻辑

和单片机消抖逻辑类似,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仿真波形:

实际调用也非常方便,只需要指定按键按下的电平状态和滤波的周期即可:


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工程下载

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

whik1194

如果对你有帮助,欢迎打赏。谢谢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值