Verilog 按鍵消抖模塊
遇到的問題:第一邊沿檢測以及對key_flag和key_state的作用瞭解耗費了我很多時間,經過不斷的分析後還是找出了錯誤
在解決問題的過程中讓我也對層次化設計FPGA更加了解,同時對阻塞賦值和非阻塞賦值也有了更深刻的瞭解,熟悉了
定時器的設計與應用
感悟:FPGA是個大坑啊,進去了就出不來了,哈哈哈!
希望自己在接下來的路上越搓越勇
設計代碼:
/*
*NAME: Rayone
*DATE: 2019/5/21
*FUNC: key filter
*/
module key_filter(Clk,Rst_n,key_in,key_flag,key_state);
//端口定義
input Clk;
input Rst_n;
input key_in;
output reg key_flag; //濾波後如果檢測確實按下了將其置1
output reg key_state;//按鍵在空閒穩定狀態下爲1 在按下穩定狀態爲0
//key_filter模塊設計
//——>利用兩級D觸發器降低亞穩態發生的可能
reg key_in_tmpa,key_in_tmpb;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
key_in_tmpa <= 1'b0;
key_in_tmpb <= 1'b0;
end
else begin
key_in_tmpa <= key_in;
key_in_tmpb <= key_in_tmpa;
end
//——>捕獲key_in變化信號
wire pedge,nedge;//key_in的上升沿和下降沿
reg key_in_a,key_in_b;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
key_in_a <= 1'b0;
key_in_b <= 1'b0;
end
else begin
key_in_a <= key_in_tmpb;//key_in_tmpb是key_in經過二級D觸發器處理後的,這裏是非阻塞賦值
//key_in_a在給key_in_b賦值的同時也給key_in_b賦值,處於時時更新的
key_in_b <= key_in_a;
end
//now :key_in_a ago :key_in_b
assign pedge = ((!key_in_b) & key_in_a); //key_in上升沿信號到來
assign nedge = ( key_in_b & (!key_in_a));//key_in下降沿信號到來
//前期設計已經準備好了,接下來就是狀態機的設計
localparam //one-hot code
IDLE = 4'b0001,
FILTER0 = 4'b0010,
DOWN = 4'b0100,
FILTER1 = 4'b1000;
//用一個狀態寄存器保存狀態
reg [3:0]state;
//設計定時器
reg [19:0]cnt; //——>需要一個計數寄存器,因爲只用計數20ms,所以一個20位的寄存器已經足夠了
reg en_cnt; //——>計數器開始計數使能信號
reg cnt_full_flag;//——>計數器加滿標誌
//狀態機設計
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
state <= IDLE;
en_cnt <= 1'b0;
key_flag <= 1'b0;
key_state <= 1'b1;
end
else begin
case(state)
IDLE:begin
key_flag <= 1'b0;
if(nedge)begin//如果檢測到下降沿
state <= FILTER0;//進入按下抖動濾波狀態
en_cnt <= 1'b1; //使能計數(此時需要計時20ms,前面我們沒有設計計數器,於是我們在最後設計一個計數器哦)
end
else
state <= IDLE;
end
FILTER0:begin
if(cnt_full_flag)begin
state <= DOWN;
en_cnt <= 1'b0;
key_flag <= 1'b1; //標誌按鍵此時確實按下
key_state <= 1'b0;//此時按鍵處於按下穩定狀態
end
else if(pedge)begin
en_cnt <= 1'b0;
state <= IDLE;
end
else
state <= FILTER0;
end
DOWN:begin
key_flag <= 1'b0;
if(pedge)begin
en_cnt <= 1'b1;
state <= FILTER1;
end
else
state <= DOWN;
end
FILTER1:begin
if(cnt_full_flag == 1'b1)begin
state <= IDLE; //計數滿標誌成功計數
en_cnt <= 1'b0; //關閉計數
// key_flag <= 1'b1; //標誌按鍵此時確實釋放
key_state <= 1'b1;//空閒穩定狀態下爲1
end
else if(nedge)begin
en_cnt <= 1'b0;
state <= DOWN;
end
else
state <= FILTER1;
end
default:
begin
state <= IDLE;
en_cnt <= 1'b0;
key_flag <= 1'b0;
key_state <= 1'b1;
end
endcase
end
//計數器加1操作
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
cnt <= 20'd0;
end
else if(en_cnt)
cnt <= cnt + 1'b1;
else
cnt <= 20'd0;
//產生計數20ms滿信號 cnt_full_flag
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_full_flag <= 1'b0;
else if(cnt == 999_999)
cnt_full_flag <= 1'b1;
else
cnt_full_flag <= 1'b0;
endmodule
按鍵的狀態圖:
仿真波形圖:
可以看到:當檢測到按鍵按下時key_flag置1表示按鍵已經按下,key_state用於判斷按下後是否處於穩定狀態,如果要將該模塊運用於其他模塊就可以通過檢測這兩個信號狀態來進行使用
module led_ctrl(Clk,Rst_n,key_flag0,key_flag1,key_state0,key_state1,led);
input Clk;
input Rst_n;
input key_flag0,key_flag1;
input key_state0,key_state1;
output [3:0]led;
reg [3:0]led_r;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
led_r <= 4'b0000;
else if(key_flag0 && !key_state0)
led_r <= led_r + 1'b1;
else if(key_flag1 && !key_state1)
led_r <= led_r - 1'b1;
else
led_r <= led_r;
assign led = ~led_r;
endmodule