【边学边记_09】——基于状态机的按键消抖

基于状态机的按键消抖

本博文可参考小梅哥的视频学习!!
按键存在一个反作用弹簧,因此当按下或者松开时均会产生额外的物理抖动,物理抖动便会产生电平的抖动。在按键从按下再到松开的过程中,其电平变化如下图所示,上为理想波形输出,下为实际波形输出。
在这里插入图片描述
上图中,产生的抖动次数以及间隔时间均是不可预期的,这就需要通过滤波来消除抖动可能对外部其他设备造成的影响。一般情况下抖动的总时间会持续 20ms 以内。这种抖动,可以通过硬件电路或者逻辑设计的方式来消除,也可以通过软件的方式完成(这些方法可在csdn站内其他博文找到)。

1.设计文件

在这里插入图片描述

设计状态机

在这里插入图片描述

module key_filter(
    Clk,
    Reset_n,
    key,
    key_flag,
    key_state
    );
    
    input Clk;
    input Reset_n;
    input key;
    output key_flag;
    output reg key_state;
    
    //首先边沿检测 pedge 和nedge
    wire pedge;
    wire nedge;
    reg [1:0] r_key;
    always@(posedge Clk)
        r_key <= {r_key[0],key};
    assign pedge = r_key == 2'b01;
    assign nedge = r_key == 2'b10;
    
    // cnt计数20ms  1_000_000
    reg [19:0] cnt;      
    //状态机
    reg key_p_flag;   //按下成功
    reg key_r_flag;    //释放成功
    assign key_flag = (key_p_flag | key_r_flag);   //“|”按位或 ; “||”逻辑或
    reg [1:0] state;
    always@(posedge Clk or negedge Reset_n)
        if(!Reset_n)begin
            state <= 0;
            cnt <= 0;
            key_p_flag <= 0;
            key_r_flag <= 0;
            key_state <= 1;
        end
        else begin
            case (state)
                0:begin
                        key_r_flag <= 0;
                        if(nedge == 1)begin
                            state <= 1;
                            cnt <= 0;
                            end
                        else
                            state <= 0;
                   end
                1:begin
                        if((pedge == 1) && (cnt < 1_000_000 - 1))
                            state <= 0;
                        else if(cnt >= 1_000_000 - 1)begin
                            state <= 2;
                            key_p_flag <= 1;
                            key_state <= 0;
                            end
                        else begin
                            cnt <= cnt + 1;
                            state <= 1;
                            end                
                   end
                2:begin
                        key_p_flag <= 0;
                        if(pedge == 1)begin
                            cnt <= 0;
                            state <= 3;
                            end    
                        else
                            state <= 2;      
                   end
                3:begin
                        if((cnt < 1_000_000 - 1)&&(nedge == 1))
                            state <= 2;
                        else if(cnt >= 1_000_000 - 1)begin
                            state <= 0; 
                            key_r_flag <= 1;
                            key_state <= 1;
                            end
                        else begin
                            cnt <= cnt +1;
                            state <= 3;               
                        end      
                   end            
            endcase
        end
    
endmodule

2.测试仿真

`timescale 1ns / 1ns
module key_filter_tb();

    reg Clk;
    reg Reset_n;
    reg key;
    wire key_flag;
    wire key_state;

    key_filter key_filter(
            Clk,
            Reset_n,
            key,
            key_flag,
            key_state
        );

    initial Clk = 0;
    always #10 Clk = !Clk;
    
    initial begin
        Reset_n = 0;
        #201;
        Reset_n = 1;
        #3000;
        key = 0;
        #5_000_000;
        key = 1;
        #5_000_000;  
        key = 0;
        #5_000_000;
        key = 1;
        #5_000_000;
        key = 0;
        #50_000_000;
        
        key = 1;
        #5_000_000;  
        key = 0;
        #5_000_000;
        key = 1;
        #5_000_000;
        key = 0;
        #5_000_000;
        key = 1;               
        #50_000_000;
        $stop;   
        
    end 

endmodule

巧用task和random 编写测试文件

`timescale 1ns / 1ns
module key_filter_tb2();

reg Clk;
    reg Reset_n;
    reg key;
    wire key_flag;
    wire key_state;

    key_filter key_filter(
            Clk,
            Reset_n,
            key,
            key_flag,
            key_state
        );

    initial Clk = 0;
    always #10 Clk = !Clk;
    
    initial begin
        Reset_n = 0;
        key = 1;
        #201;
        Reset_n = 1;
        #3000;
        press_key(1);
        $stop;   
    end 

    reg [31:0] rand;
    task press_key;
        input [3:0] seed;
        begin
            key = 1;
            #20_000_000;
            repeat(5)begin
                rand = {$random(seed)} % 10_000_000;//(0,9999999)
                #rand key = ~key;
            end
            key = 0;
            #40_000_000;
            
            repeat(5)begin
                rand = {$random(seed)} % 10000000;//(0,9999999)
                #rand key = ~key;
            end
            key = 1;
            #40_000_000;           
        end
    endtask        

endmodule

3.仿真结果

在这里插入图片描述
其中产生微差的原因主要是因为nedge,细节如下图所示:
在这里插入图片描述
tb2仿真结果如下
在这里插入图片描述

关于此文章可能遇到亚稳态问题
亚稳态问题参考文章:亚稳态专题(亚稳态的概念、亚稳态的产生、亚稳态的后果、以及如何避免亚稳态)
常见现象:在状态2中出不去
解决办法:
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

通信学会了吗?

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值