1. 程式人生 > >FPGA-11-狀態機的實現例項(按鍵的消抖)

FPGA-11-狀態機的實現例項(按鍵的消抖)

大致思路有了,如何設計實現呢?貌似這是一個很複雜的設計,實則不然,FSM的本質就是對具有邏輯規律和時序邏輯的事物的描述,採用FSM設計,問題迎刃而解!

  1、從狀態變數入手,分析狀態變數

    IDLE:按鍵空閒狀態(由於上拉電阻的作用,按鍵未被按下時保持高電平);

    FILTER_DOWN:按下濾波狀態;

    DOWN:按下穩定狀態;

    FILTER_UP:釋放濾波狀態;

  2、分析狀態轉移條件,繪製狀態轉移圖(visio)

  3、照圖施工,選用合適的描述方案

    在描述的時候,有兩個重要問題需要解決:

    1)按鍵訊號屬於非同步訊號,在狀態轉移中需要對按鍵邊沿敏感,所以首先採用一級D觸發器將key_in與clk同步,產生pedge和nedge訊號,也就是邊沿檢測電路,程式碼如下:

//邊沿檢測電路
    [email protected](posedge clk)
        key_temp <= key_in;                                 //暫存上一個clk按鍵狀態
    assign key_nedge = (key_temp)&&(!key_in);        //下降沿檢測
    assign key_pedge = (!key_temp)&&(key_in);        //上升沿檢測

    2)當20ms延時完畢後,應該輸出一個脈衝,通知其它模組檢測key_flag引腳電平;

    完整的verilog描述程式碼如下:

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Module Name: key_filter
// Description: //獨立按鍵消抖模組
//////////////////////////////////////////////////////////////////////////////////
module key_filter(
    input clk,                    //50M時鐘訊號
    input rst,                    //低電平復位
    input key_in,                 //按鍵輸入
    output reg key_flag,          //消抖完畢輸出脈衝
    output reg key_state          //按鍵狀態輸出
);
    reg  [3:0]NS;            //nextstate
    reg  key_temp;
    wire key_pedge;    
    wire key_nedge;
    reg en_cnt;
    reg [19:0]cnt;            //需要計數次數1_000_000
    
    //邊沿檢測電路
    
[email protected]
(posedge clk) key_temp <= key_in; //暫存上一個clk按鍵狀態 assign key_nedge = (key_temp)&&(!key_in); //下降沿檢測 assign key_pedge = (!key_temp)&&(key_in); //上升沿檢測 //帶使能端計數器,用於20ms延時 [email protected](posedge clk,negedge rst) if(!rst) cnt <= 0; else if(en_cnt) cnt <= cnt + 1'b1; else cnt <= 0; //狀態one-hot編碼 localparam IDLE = 4'b0001, //空閒狀態 FILTER_DOWN = 4'b0010, //按下消抖狀態 DOWN = 4'b0100, //按下穩定狀態 FILTER_UP = 4'b1000; //釋放消抖狀態 //一段式狀態機 [email protected](posedge clk,negedge rst) if(!rst)begin NS <= IDLE; en_cnt <= 0; key_flag <= 0; key_state <= 1; end else case(NS) IDLE: begin key_flag <= 0; key_state <= 1; if(key_nedge)begin NS <= FILTER_DOWN; en_cnt <= 1'b1; //使能計數器 end else NS <= IDLE; end FILTER_DOWN: if(cnt >= 20'd999_999)begin en_cnt <= 0; //20ms時間到,失能計數器,進入穩定狀態 key_flag <= 1'b1; //key_flag輸出一個clk高脈衝 NS <= DOWN; end else if(key_pedge)begin en_cnt <= 0; //20ms時間內發生上升沿,失能計數器,保持空閒狀態 NS <= IDLE; end DOWN: begin key_flag <= 0; key_state <= 0; if(key_pedge)begin NS <= FILTER_UP; en_cnt <= 1'b1; //使能計數器 end else NS <= DOWN; end FILTER_UP: if(cnt >= 20'd999_999)begin en_cnt <= 0; NS <= IDLE; //20ms時間到,失能計數器,進入穩定狀態 key_flag <= 1; end else if(key_nedge)begin en_cnt <= 0; //20ms時間內發生上升沿,失能計數器,保持按下穩定狀態 NS <= DOWN; end default: NS <= IDLE; endcase endmodule