基于FPGA的按键消抖

基于FPGA的按键消抖


前言

按键消抖属于一个经典问题了,放一张图
按键按下后的动态波形
大概就是根据经验,如果按键在20ms内没有变化则判定为按下。


一、按键状态机

本次是参考小梅哥的文档使用状态机写的按键消抖,状态转移图如下
参考的野火状态转移图的画法

二、按键消抖模块

1.模块框图

在这里插入图片描述
其中 :key_pose是在按键按下时刻产生一个时钟周期的高电平。
key_nege是在按键抬起时刻产生一个时钟周期的高电平。
key_out 是在按键消抖后的实际状态。

2.模块代码

开发平台:Vivado 2018.3
仿真平台:Modusim SE-64 10.5
开发平台:ALINX AX7Z020

代码如下:状态机的是按照野火的新式两段式状态机格式编写的

`timescale 1ns / 1ns

module key
#(
    parameter   CNT_20MS_MAX = 1_000_000
)
(
    input       wire        sys_clk     ,
    input       wire        sys_rst_n   ,
    input       wire        key_in      ,

    output      reg         key_pose    ,   
    output      reg         key_nege    ,   
    output      reg         key_out     
);

reg     key_in_reg1;
reg     key_in_reg2;

wire     key_in_pose;
wire     key_in_nege;

reg     key_out_reg;

reg                 cnt_en;
reg                 cnt_20ms_full;
reg     [19:0]      cnt_20ms;


reg     [3:0]       state;
localparam          IDLE        = 4'b0001,
                    DOWN_FILTER = 4'b0010,
                    DOWN        = 4'b0100,
                    UP_FILTER   = 4'b1000;
//将按键信号同步至系统时钟地域,同时打拍缓解亚稳态
always@(posedge sys_clk or  negedge  sys_rst_n) 
    if(sys_rst_n == 1'b0)
        key_in_reg1 <= 1'b1;
    else
        key_in_reg1 <= key_in;

always@(posedge sys_clk or  negedge  sys_rst_n) 
    if(sys_rst_n == 1'b0)
        key_in_reg2 <= 1'b1;
    else
        key_in_reg2 <= key_in_reg1;
//捕获按键按下的上升沿,下降沿
assign  key_in_nege = (~key_in_reg1)&&(key_in_reg2);
assign  key_in_pose = (key_in_reg1)&&(~key_in_reg2);
        
always@(posedge sys_clk or  negedge  sys_rst_n) 
    if(sys_rst_n == 1'b0)
        cnt_20ms <= 20'd0;
    else    if((cnt_20ms == CNT_20MS_MAX - 1'b1) && (cnt_en == 1'b1))
        cnt_20ms <= cnt_20ms;
    else    if((cnt_20ms != CNT_20MS_MAX - 1'b1) && (cnt_en == 1'b1))
        cnt_20ms <= cnt_20ms + 1'b1;
    else
        cnt_20ms <= 20'd0;    

//按键处于状态2、4时开启20ms定时器  
always@(posedge sys_clk or  negedge  sys_rst_n) 
    if(sys_rst_n == 1'b0)
        cnt_en <= 1'b0;
    else    if((state == DOWN_FILTER) || (state == UP_FILTER))
        cnt_en <= 1'b1;
    else
        cnt_en <= 1'b0;
        
  //按键记满将标志置1
always@(posedge sys_clk or  negedge  sys_rst_n) 
    if(sys_rst_n == 1'b0)
        cnt_20ms_full <= 1'b0;
    else    if(cnt_20ms == CNT_20MS_MAX - 2'd2)    
        cnt_20ms_full <= 1'b1;
    else
        cnt_20ms_full <= 1'b0;
        
            
always@(posedge sys_clk or  negedge  sys_rst_n) 
    if(sys_rst_n == 1'b0)
        state <= IDLE;
    else    case(state)
                IDLE        :   if(key_in_nege == 1'b1)
                                    state <= DOWN_FILTER;
                                else
                                    state <= IDLE;    
                DOWN_FILTER :   if(cnt_20ms_full == 1'b1)
                                    state <= DOWN;
                                else  if(key_in_pose == 1'b1)
                                    state <= IDLE;   
                                else
                                    state <= DOWN_FILTER;      
                
                DOWN        :   if(key_in_pose == 1'b1)
                                    state <= UP_FILTER;
                                else
                                    state <= DOWN;  
                                    
                UP_FILTER   :   if(cnt_20ms_full == 1'b1)  
                                    state <= IDLE;          
                                else    if(key_in_nege == 1'b1)  
                                    state <= DOWN;     
                                else    
                                    state <= UP_FILTER;   
                                    
                default     :       state <= IDLE;                   
            endcase                    


        
always@(posedge sys_clk or  negedge  sys_rst_n) 
    if(sys_rst_n == 1'b0)
        key_out <= 1'b1;
    else    if(state == DOWN)
        key_out <= 1'b0;
    else    if(state == IDLE)
        key_out <= 1'b1;    
    else
        key_out <= key_out;

always@(posedge sys_clk or  negedge  sys_rst_n) 
    if(sys_rst_n == 1'b0)
        key_out_reg <= 1'b1;
    else
        key_out_reg <= key_out;    
        
always@(posedge sys_clk or  negedge  sys_rst_n) 
    if(sys_rst_n == 1'b0)
        key_pose <= 1'b0;
    else    
        key_pose <= ~key_out_reg & key_out;
    
always@(posedge sys_clk or  negedge  sys_rst_n) 
    if(sys_rst_n == 1'b0)
        key_nege <= 1'b0;
    else    
        key_nege <= key_out_reg & ~key_out;


endmodule

2.模块仿真代码

参考了野火的代码

`timescale 1ns / 1ns

module tb_key();

parameter   CNT_1MS = 20'd19    , 
            CNT_11MS = 21'd69   , 
            CNT_41MS = 22'd149  , 
            CNT_51MS = 22'd199  , 
            CNT_60MS = 22'd249  ;
     
reg     sys_clk;
reg     sys_rst_n;
reg     key_in;

wire    key_pose;      
wire    key_nege;
wire    key_out;  

key
#(
    .CNT_20MS_MAX(20'd25)
)
key_inst
(
    .sys_clk    (sys_clk  ) ,
    .sys_rst_n  (sys_rst_n) ,
    .key_in     (key_in   ) ,

    .key_pose   (key_pose ) ,  
    .key_nege   (key_nege ) ,  
    .key_out    (key_out  ) 

);

reg     [21:0]    tb_cnt;

initial 
    begin
        sys_clk = 1'b1;
        sys_rst_n <= 1'b0;
        key_in <= 1'b0;
        #20
        sys_rst_n <= 1'b1;      
    end

always #10 sys_clk =~ sys_clk;

always@(posedge sys_clk or negedge sys_rst_n) 
    if(sys_rst_n == 1'b0) 
        tb_cnt <= 22'b0; 
    else if(tb_cnt == CNT_60MS) 
        tb_cnt <= 22'b0; 
    else 
        tb_cnt <= tb_cnt + 1'b1;

always@(posedge sys_clk or negedge sys_rst_n) 
    if(sys_rst_n == 1'b0) 
        key_in <= 1'b1; 
    else if((tb_cnt >= CNT_1MS && tb_cnt <= CNT_11MS) 
    || (tb_cnt >= CNT_41MS && tb_cnt <= CNT_51MS)) 
        key_in <= {$random} % 2; 
    else if(tb_cnt >= CNT_11MS && tb_cnt <= CNT_41MS) 
        key_in <= 1'b0; 
    else 
        key_in <= 1'b1;

endmodule


三、总结(含文件)

工程文件点这里~~
提取码:bemh

黑金的板子到了,真滴帅,赶紧点个灯试试手。 
打算基于黑金的板子,将以前做的仿真都重构下下。
但是最近事情有点多,进度缓慢,呜呜呜 >o<

此工程可以应用于实际项目,如果认为文章写的不错请点赞支持。
FPGA初学者,欢迎交流鸭。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值