08-按键消抖

按键消抖

RTL代码

`timescale 1ns / 1ps

//按键消抖
module key_filter(
    clk,
    reset_n,
    key,
//    key_p_flag,
//    key_n_flag,
//当出现电平变化时(按键按下或松开),会有flag标记
    key_flag,
    key_state
    );
    
    input clk;
    input reset_n;
    input key;
    
//    output reg key_p_flag;
//    output reg key_n_flag;
    output key_flag;
	
	//两种按键状态,1为松开,0为按下
    output reg key_state;
    
    wire key_p_flag;
    wire key_n_flag;
    
	//无论是按键松开还是按下,出现电平变化,都会有flag
    assign key_flag = (key_p_flag|key_n_flag);
    
	//电平检测(上升沿或下降沿)
    reg [2-1:0] r_key;
    always@(posedge clk)begin
        r_key[0]<=key;
        r_key[1]<=r_key[0];
    end
    
    //等效写法
//    always@(posedge clk)
//        r_key<={r_key[0],key};
    
    wire pedge_key;
    wire nedge_key;
    assign pedge_key = (r_key==2'b01);
    assign nedge_key = (r_key==2'b10);
    
	//状态机的写法!!非常重要
	//尤其是对于不同状态的参数定义!!
    reg [4-1:0] state;
    parameter [4-1:0]   IDLE = 4'B0001,
                        P_FLITER = 4'b0010,
                        WAIT_R = 4'b0100,
                        R_FILTER = 4'b1000;
                        
	//分频计数1us
    parameter time_cnt = 1_000_000-1;
                
	//分频计数
    reg [20-1:0] div_cnt;
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            div_cnt<=0;
        else if(div_cnt==time_cnt)
            div_cnt<=0;
        else 
            div_cnt<=div_cnt+1;
    end
    
	
	//状态机!
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)begin
            state<=IDLE;
            key_p_flag<=0;
            key_n_flag<=0;
            key_state<=1;
        end
        else 
            case(state)
                
                IDLE:begin
                    key_n_flag<=0;
                    if(nedge_key==1)
                        state<=P_FLITER;
                    else if(nedge_key==0)begin
                        state<=IDLE;
                        div_cnt<=0;
                    end
                end
                
                P_FLITER:begin
                    if((div_cnt<=time_cnt)&&(pedge_key==1))begin
                        state<=IDLE;
                        div_cnt<=0;
                    end
                    else if((div_cnt>=time_cnt)&&(pedge_key==0))begin 
                            state<=WAIT_R;
                            key_p_flag<=1;
                            key_state<=0;
                    end
                    else 
                        state<=P_FLITER;
                end
                
                WAIT_R:begin
                    key_p_flag<=0;
                    if(pedge_key==1)
                        state<=R_FILTER;
                    else if(pedge_key==0) begin
                        state<=WAIT_R;
                        div_cnt<=0;
                    end
                end
                
                R_FILTER:begin
                    if((div_cnt<=time_cnt)&&(nedge_key==1))begin
                        state<=WAIT_R;
                        div_cnt<=0;
                    end
                    else if((div_cnt>=time_cnt)&&(nedge_key==0))begin
                        state<=IDLE;
                        key_n_flag<=1;
                        key_state<=1;
                    end
                    else
                        state<=R_FILTER;
                end
                
            endcase
    end

endmodule

状态转移图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码解析

  • 按键在松开状态电平为高,按下为低,在过程中会出现抖动,导致电平出现变化
  • 按键消抖原理,在于监视电平,当按键按下时,电平变为0,并持续一段时间(自行拟定)后,视作按下完成
  • 在按键松开时,电平变为1,并持续一段时间(自行拟定)后,视作松开完成
  • 状态机的rtl代码实现很简单,case语句即可实现,但需要关注的点在于状态跳转条件
  • 在状态条件中,对于分频计数,需要关注何时清零,这可能会对于仿真波形干扰

测试平台

`timescale 1ns / 1ps

module key_filter_tb();

    reg clk;
    reg reset_n;
    reg key;
    
//    wire key_p_flag;
//    wire key_n_flag;
    wire key_flag
    wire key_state;

    key_filter key_filter(
        clk,
        reset_n,
        key,
//        key_p_flag,
//        key_n_flag,
        key_flag,
        key_state 
        );
        
    initial begin
        clk = 1;
        forever #10 clk = ~clk;
    end
    
    initial begin
        reset_n=0;
        key=1;
        #201;
        reset_n=1;
        #200;
        key_data(8'b1010_0101);
        #30_000_000;
        $stop;
    end
    
    task key_data;
        input [8-1:0] tx_data;
        begin
            key=1;
            #20;
            key = tx_data[0];
            #30_000_000;
            key = tx_data[1];
            #20;
            key = tx_data[2];
            #20;
            key = tx_data[3];
            #20;
            key = tx_data[4];
            #30_000_000;
            key = tx_data[5];
            #20;
            key = tx_data[6];
            #20;
            key = tx_data[7];
            #20;
        end
    endtask
    

endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值