基于FPGA的按键消抖

应用场景及实现原理

在电路中的按键由于按键的材料原因,在按键按下或弹开的时候会有抖动,而这种抖动在FPGA中不被允许存在。因为抖动就会产生按键的误判,这在电子电路中是非常常见的现象,抖动的去除可以用硬件电路来实现,想知道方法的可以参考阎石老师的数字电路技术基础这本书,在触发器的章节有讲。本次实验主要是利用软件代码实现按键消抖,实现的主要原理是,按键抖动时两个抖动的时间间隔不超过20ms。

状态转移图设计

即使再简单的代码,我们也要按照标准的流程来实现。因为按键消抖模块中含有状态机,所以我们画出状态机的状态转移图,如下:
在这里插入图片描述
由上面的状态转移图,我们可以很容易的写出代码,这里不多加解释。我的传统,直接上代码。

按键消抖模块代码

按键消抖模块的代码如下:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : key.v
// Create Time  : 2020-01-05 13:49:36
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module key(
    input                       sclk            ,
    input                       rst_n           ,
    input                       key             ,
    output  reg                 key_o       
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
parameter       IDLE        =       4'b0001     ;    
parameter       S1          =       4'b0010     ;
parameter       S2          =       4'b0100     ;
parameter       S3          =       4'b1000     ;

reg                 [ 3:0]      state           ;
reg                 [ 9:0]      cnt             ;
reg                             key_r1          ;
reg                             key_r2          ;
reg                             key_r3          ;
reg                             nege_flag       ;
reg                             pose_flag       ;
 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
always @(posedge sclk)
    key_r1          <=      key;

always @(posedge sclk)
    key_r2          <=      key_r1;

always @(posedge sclk)
    key_r3          <=      key_r2;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        nege_flag       <=      1'b0;
    else if(key_r3 == 1'b1 && key_r2 == 1'b0)
        nege_flag       <=      1'b1;
    else
        nege_flag       <=      1'b0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        pose_flag       <=      1'b0;
    else if(key_r3 == 1'b0 && key_r2 == 1'b1) 
        pose_flag       <=      1'b1;
    else
        pose_flag       <=      1'b0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        state           <=      IDLE;
    else case(state)
        IDLE    :   if(nege_flag == 1'b1)
                        state           <=      S1;
                    else
                        state           <=      IDLE;                        
        S1      :   if(cnt == 10'd999)
                        state           <=      S2;
                    else if(pose_flag == 1'b1)
                        state           <=      IDLE;
                    else
                        state           <=      S1;                        
        S2      :   if(pose_flag == 1'b1)
                        state           <=      S3;
                    else
                        state           <=      S2;                        
        S3      :   if(cnt == 10'd999)
                        state           <=      IDLE;
                    else if(nege_flag == 1'b1)
                        state           <=      S2;
                    else
                        state           <=      S3;
                        
        default :   state           <=      IDLE;
    endcase

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt             <=      10'd0;
    else if(state != S1 && state != S3)
        cnt             <=      10'd0;
    else
        cnt             <=      cnt + 1'b1;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        key_o           <=      1'b0;
    else if(state == S1 && cnt == 10'd999) 
        key_o           <=      1'b1;
    else
        key_o           <=      1'b0;

endmodule

相信经常看我文章的同学已经对上面的代码风格很熟悉,观察状态转移图与逻辑代码很容易可以搞懂代码实现。为了证明程序的正确性,我进行了程序仿真。

按键消抖模块测试代码

这里的测试代码同样使用了task语句,而且使用了task语句中嵌套task语句,经过这节课的练习,相信大家对测试模块的写法已经了如指掌。

`timescale 1ns / 1ps
`define     CLOCK       20
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : key_tb.v
// Create Time  : 2020-01-05 14:09:55
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module key_tb();
reg                         sclk                ;
reg                         rst_n               ;
reg                         key                 ;
wire                        key_o               ;

initial begin
    sclk            <=          1'b0;
    rst_n           =           1'b0;
    key             =           1'b1;
    #(100*`CLOCK)
    rst_n           =           1'b1;
    #(100*`CLOCK)
    key_pulse;
    #(100*`CLOCK)
    key_pulse;
    #(100*`CLOCK)
    key_pulse;
    #(100*`CLOCK)
    key_pulse;
    #(100*`CLOCK)
    key_pulse;
    #(100*`CLOCK)
    key_pulse;
    #(100*`CLOCK)
    key_pulse;
    #(100*`CLOCK)
    key_pulse;
end
always  #(`CLOCK/2)     sclk        <=      ~sclk;

task key_pulse;
    begin
        key         <=          1'b1;
        shake(1000);
        key         <=          1'b0;
        #(5000*`CLOCK)
        shake(1000);
        key         <=          1'b1;
        #(5000*`CLOCK);
    end
endtask

task shake   ;
    input      [10:0]           times   ;
    integer         i                   ;
    begin
        for(i = 0; i<times; i = i+1)begin
            key     <=          {$random} % 2;
            #(`CLOCK);
        end       
    end 
endtask

key key_inst(
    .sclk                   (sclk                   ),
    .rst_n                  (rst_n                  ),
    .key                    (key                    ),
    .key_o                  (key_o                  )
);  
endmodule

这里给出modelsim的仿真图,如下:
在这里插入图片描述
从仿真图中可以看出我们实验的准确性。

结束语

我博客中的代码是可以直接跑的程序,没有进行任何精简。创作不易,认为文章有帮助的同学们可以收藏点赞支持。对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群:
在这里插入图片描述

发布了14 篇原创文章 · 获赞 4 · 访问量 593

猜你喜欢

转载自blog.csdn.net/zhangningning1996/article/details/103843241