FPGA-11-狀態機的實現例項(按鍵的消抖)
阿新 • • 發佈:2018-11-17
大致思路有了,如何設計實現呢?貌似這是一個很複雜的設計,實則不然,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