(13)基于状态机的按键消抖

前言

在开发板上的按键具有以下特点:当未按下时输出为高电平,当按键按下时输出则变为低电平,具体原理可从如下原理图看出:

而实际的按键结构中存在一个反作用弹簧片,因此当按下或松开时会产生额外的物理抖动,从而产生电平抖动

因此,按键的理想波形与实际波形如下:

上图中抖动次数和抖动周期是随机的,一般持续时间会在20ns以内,这就需要滤波来消除抖动对外部设备产生的影响(例如规定按下一次就计数一次,可见抖动会对影响其正常功能的使用),一般有硬件设计和软件两种方式,其中硬件设计常用于按键较少的情形

分析

整个过程中按键有如下几种状态:

没有被按下,静止状态,高电平
按下过程中,抖动,输出高低不定,最终稳定低电平
按下后,抖动结束,输出低电平
释放过程中,抖动,输出高低不定,最终稳定高电平

因此,我们根据以下设计原理判断按键是否处于抖动状态:
按下过后开始计时,若在没有达到20ms内出现上升沿,则认为处于抖动

状态划分:

空闲(idle):等待按键按下,按下后跳转至按下消抖状态
按下消抖(p_filter):在20ms内一边计时一边检测上升沿,若检测到上升沿则返回空闲态,如果计满20ms没有出现上升沿,则发出按键按下通知
等待释放(wait_r):一旦出现上升沿,跳转至释放消抖状态
释放消抖(r_filter):在20ms内一边计时一边检测下降沿,若检测到下降沿则返回等待释放,如果计满20ms没有出现下降沿,则发出按键释放通知

程序设计

状态转移图:

源文件:

点击查看代码
module key_filter(
    input key,
    input clk,
    input reset_n,
    output reg key_p_flag,
    output reg key_r_flag
);

parameter IDLE = 2'b00,P_FILTER = 2'b01,WAIT_R = 2'b10,R_FILTER = 2'b11;

reg [1:0] r_key;
reg [20:0] cnt; //  20_000_000/20ns = 1_000_000
reg [1:0] state;

wire nedge_key;
wire posedge_key;

assign posedge_key = (r_key == 2'b01);
assign nedge_key = (r_key == 2'b10);

always @(posedge clk) begin
    r_key <= {r_key[0], key};
end

always @(posedge clk or negedge reset_n) begin
    if(!reset_n) begin
        state <= IDLE;
        key_p_flag <= 0;
        key_r_flag <= 0;
        cnt <= 0;
    end
    else begin
        case(state)
            IDLE: begin
                key_r_flag <= 0;
                if(nedge_key == 1) begin
                    state <= P_FILTER;
                end
                else if(nedge_key) begin
                    state <= IDLE;
                end
            end
            P_FILTER: begin
                if((posedge_key == 1)&&(cnt < 1000000-1)) begin
                    state <= IDLE;
                    cnt <= 0;
                end
                else if((posedge_key == 0)&&(cnt >= 1000000-1)) begin
                    state <= WAIT_R ;
                    key_p_flag <= 1;
                    cnt <= 0;
                end
                else begin
                    state <= P_FILTER;
                    cnt <= cnt + 1;
                end
            end
            WAIT_R: begin
                key_p_flag <= 0;
                if(posedge_key == 1) begin
                    state <= R_FILTER;
                end
                else begin
                    state <= WAIT_R;
                end
            end
            R_FILTER: begin
                if((nedge_key == 1)&&(cnt < 1000000-1)) begin
                    state <= WAIT_R;
                    cnt <= 0;
                end
                else if((nedge_key == 0)&&(cnt >= 1000000-1)) begin
                    state <= IDLE;
                    key_r_flag <= 1;
                    cnt <= 0;
                end
                else begin
                    state <= R_FILTER;
                    cnt <= cnt + 1;
                end
            end
        endcase
    end
end
endmodule
tb:
点击查看代码
`timescale 1ns/1ps
module key_filter_tb();

reg    key				 ;      
reg    clk               ;
reg    reset_n           ;
wire   key_p_flag        ;
wire   key_r_flag        ;



key_filter u_key_filter(
    .key             (key)              ,
    .clk             (clk)              ,
    .reset_n         (reset_n)          ,
    .key_p_flag      (key_p_flag)       ,
    .key_r_flag      (key_r_flag)
);

initial clk = 1;
always #10 clk = ~clk;


initial begin
    reset_n = 0;
    key = 1;
    #201
    reset_n = 1;
    #30000
    key = 0;
    #20000
    key = 1;
    #30000
    key = 0;
    #20000
    key = 1;
    #30000
    key = 0;
    #20000
    key = 1;
    #30000
    key = 0;

    #50000000

    key = 1;
    #30000
    key = 0;
    #20000
    key = 1;
    #30000
    key = 0;
    #20000
    key = 1;

    #50000000
	$finish;

end

/*iverilog */
initial
begin            
    $dumpfile("wave.vcd"); //生成的vcd文件名称
    $dumpvars(0, key_filter_tb);    //tb模块名称
end
/*iverilog */

endmodule

波形仿真:

最开始按键一直在按下——释放——按下——释放——按下,从state也可以看到状态机状态一直在idle和p_filter之间反复横跳,但key_p_flag始终为低电平

直到稳定了20ms后,key_p_flag才被拉高,产生一个正向脉冲,此时从state也可以看到状态机从p_filter进入到wait_r状态

之后这里也是在模拟按键释放抖动的状态

也是稳定了20ms后,key_r_flag也是被拉高产生一个正向脉冲,状态机也由wait_r返回到idle状态,说明程序设计正确

posted @ 2024-09-05 13:32  xuxuxu69  阅读(103)  评论(0编辑  收藏  举报