基于状态机方法的按键消抖模块FPGA实现,包括testbench

发布时间 2023-05-24 15:53:16作者: 我爱C编程

1.算法仿真效果

vivado2019.2仿真结果如下:

 

 

 

系统RTL图:

 

 

 

2.算法涉及理论知识概要

        状态机,FSMFinite State Machine),也称为同步有限状态机从。指的是在同步电路系统中使用的,跟随同步时钟变化的,状态数量有限的状态机,简称状态机。

 

状态机分类

 

        根据状态机的输出是否与输入有关可以分为迷你(Mealy)状态机和摩尔(Moore)状态机。迷你状态机的输出结果的判断条件是当前状态&&输入信号,摩尔状态机的输出结果的判断条件只有当前状态。

 

         根据状态机的写法分为一段式、二段式、三段式状态机。

 

一段式状态机是把状态跳转逻辑、状态机状态和状态机结果输出都写到一个always块里。

 

在信号少的情况容易理解,信号多的话容易出错且复杂。

 

二段式状态机是把状态跳转逻辑用always组合逻辑,状态机状态用always时序逻辑,状态机结果输出用assign组合逻辑。

 

二段式状态机结果输出用组合逻辑判断容易产生毛刺。

 

三段式状态机是把状态跳转逻辑用always组合逻辑,状态机状态用always时序逻辑,状态机结果输出用always时序逻辑。

 

三段式状态机描述方式使得FSM做到了同步寄存器输出,消除了组合逻辑输出的不稳定与毛刺的隐患,更有利于时序稳定。

 

         本次实验中使用的按键开关为机械弹簧按键开关,当机械触点断开、闭合时,由于机械触点的弹性作用,按键开关在闭合时不会马上稳定的接通,在断开时也不会一下子断开。因而在闭合、断开的瞬间会伴随有一连串的抖动,而FPGA是检测按键对应引脚的电平来判断按键是否按下,因此就会使得按键的一次按下、断开操作引发FPGA的多次触发,这违背了操作者的本意,即一次按键的按下操作引发FPGA的一次触发。为了实现“一次按键的按下操作引发FPGA的一次触发”所采取的措施就是按键消抖。

 

        按键抖动的时间长短有机械按键的机械特性决定,分为前抖动和后抖动,一般为5ms~10ms。按键稳定闭合时间的长短由操作者的按键动作决定,一般为零点几秒至数秒。按键消抖原理就是在按键稳定时读取按键的状态,将该值作为FPGA是否触发的条件,并且要考虑后抖动(按键抬起)的影响,保证一次按键的按下操作只能触发一次FPGA。为确保按键对FPGA的触发可靠,本次实验将按键稳定闭合时间定为按键保持低电平单次持续20ms。当按键保持低电平单次持续为20ms时,即可认为按键按下操作有效,进而触发FPGA操作。

 

3.Verilog核心程序

 

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
 
 
// Create Date: 2023/03/14 22:27:58
// Design Name: 
// Module Name: tops_key
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//
 
 
module tops_key(
input clk,
input keyin,
output reg keyout
);
    
.....................................................		  
parameter state0 = 3'b000;
parameter state1 = 3'b001;
parameter state2 = 3'b010;
parameter state3 = 3'b011;
parameter state4 = 3'b100;				   
reg [2:0] state = state0;		
reg [16:0] timer = 17'b0;
reg timer_en = 1'b0, timer_tick = 1'b0;
    
always@ (posedge clk) 
begin
    
        case (state)
.......................................................
        
        if (timer_en == 1'b1) begin
            if (timer == (timerlim - 1)) begin
                timer_tick <= 1'b1;
                timer <= 17'b0;
            end    
            else begin
                timer_tick <= 1'b0;
                timer <= timer + 17'b1;
            end
        end       
        else begin
            timer <= 17'b0;
            timer_tick <= 1'b0;
        end    
            
end 
endmodule