廣州建網(wǎng)站培訓(xùn)刷粉網(wǎng)站推廣馬上刷
🎉歡迎來到FPGA專欄~按鍵消抖模塊設(shè)計與驗證
- ☆* o(≧▽≦)o *☆嗨~我是小夏與酒🍹
- ?博客主頁:小夏與酒的博客
- 🎈該系列文章專欄:FPGA學(xué)習(xí)之旅
- 文章作者技術(shù)和水平有限,如果文中出現(xiàn)錯誤,希望大家能指正🙏
- 📜 歡迎大家關(guān)注! ??
🎉 目錄-按鍵消抖模塊設(shè)計與驗證
- 一、效果演示
- 二、模塊設(shè)計
- 三、仿真測試
- 3.1 常規(guī)編寫
- 3.2 task編寫
- 四、仿真模型
一、效果演示
🥝模塊設(shè)計:
🥝按鍵消抖模塊的完整代碼,可直接使用:
//
//模塊:按鍵消抖模塊
//key_state:輸出消抖之后按鍵的狀態(tài)
//key_flag:按鍵消抖結(jié)束時產(chǎn)生一個時鐘周期的高電平脈沖
/
module KeyFilter(input Clk,input Rst_n,input key_in,output reg key_flag,output reg key_state
);//按鍵的四個狀態(tài)localparamIDLE = 4'b0001,FILTER1 = 4'b0010,DOWN = 4'b0100,FILTER2 = 4'b1000;//狀態(tài)寄存器reg [3:0] curr_st;//邊沿檢測輸出上升沿或下降沿wire pedge;wire nedge;//計數(shù)寄存器reg [19:0]cnt;//使能計數(shù)寄存器reg en_cnt;//計數(shù)滿標(biāo)志信號reg cnt_full;//計數(shù)滿寄存器//------<邊沿檢測電路的實現(xiàn)>------//邊沿檢測電路寄存器reg key_tmp0;reg key_tmp1;//邊沿檢測always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)beginkey_tmp0 <= 1'b0;key_tmp1 <= 1'b0;endelse beginkey_tmp0 <= key_in;key_tmp1 <= key_tmp0;end endassign nedge = (!key_tmp0) & (key_tmp1);assign pedge = (key_tmp0) & (!key_tmp1);//------<狀態(tài)機(jī)主程序>------ //狀態(tài)機(jī)主程序always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endelse begincase(curr_st)IDLE:beginkey_flag <= 1'b0;if(nedge)begincurr_st <= FILTER1;en_cnt <= 1'b1;endelsecurr_st <= IDLE;endFILTER1:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b0;curr_st <= DOWN;en_cnt <= 1'b0;end else if(pedge)begincurr_st <= IDLE;en_cnt <= 1'b0;endelsecurr_st <= FILTER1;endDOWN:beginkey_flag <= 1'b0;if(pedge)begincurr_st <= FILTER2;en_cnt <= 1'b1;endelsecurr_st <= DOWN;endFILTER2:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b1;curr_st <= IDLE;en_cnt <= 1'b0;end else if(nedge)begincurr_st <= DOWN;en_cnt <= 1'b0;endelsecurr_st <= FILTER2;enddefault:begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endendcaseendend//------<20ms計數(shù)器>------ //20ms計數(shù)器//Clk 50_000_000Hz//一個時鐘周期為20ns//需要計數(shù)20_000_000 / 20 = 1_000_000次always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt <= 20'd0;else if(en_cnt)cnt <= cnt + 1'b1;elsecnt <= 20'd0;endalways@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt_full <= 1'b0;else if(cnt == 999_999)cnt_full <= 1'b1;elsecnt_full <= 1'b0;endendmodule
🥝RTL視圖:
🥝狀態(tài)轉(zhuǎn)移:
🥝仿真結(jié)果:
二、模塊設(shè)計
🥝模塊設(shè)計:
信號 | 作用 |
---|---|
clk | 時鐘信號輸入 |
rst_n | 復(fù)位信號輸入 |
key_in | 按鍵信號輸入 |
key_flag | 消抖結(jié)束之后的標(biāo)志位 |
key_state | 消抖結(jié)束之后按鍵的狀態(tài) |
🥝上升沿檢測電路:
🥝下降沿檢測電路:
🥝邊沿檢測電路的實現(xiàn):
檢測到下降沿,nedge輸出高電平;檢測到上升沿,pedge輸出高電平。
//------<邊沿檢測電路的實現(xiàn)>------
//邊沿檢測電路寄存器
reg key_tmp0;
reg key_tmp1;//邊沿檢測
always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)beginkey_tmp0 <= 1'b0;key_tmp1 <= 1'b0;endelse beginkey_tmp0 <= key_in;key_tmp1 <= key_tmp0;end
endassign nedge = (!key_tmp0) & (key_tmp1);//檢測到下降沿,nedge輸出高電平
assign pedge = (key_tmp0) & (!key_tmp1);//檢測到上升沿,pedge輸出高電平
🥝一段式狀態(tài)機(jī)設(shè)計:
按鍵的四種狀態(tài):
//按鍵的四個狀態(tài)
localparamIDLE = 4'b0001,FILTER1 = 4'b0010,DOWN = 4'b0100,FILTER2 = 4'b1000;
計數(shù)器:
//------<20ms計數(shù)器>------
//20ms計數(shù)器
//Clk 50_000_000Hz
//一個時鐘周期為20ns
//需要計數(shù)20_000_000 / 20 = 1_000_000次//計數(shù)寄存器
reg [19:0]cnt;//使能計數(shù)寄存器
reg en_cnt;//計數(shù)滿標(biāo)志信號
reg cnt_full;//計數(shù)滿寄存器always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt <= 20'd0;else if(en_cnt)cnt <= cnt + 1'b1;elsecnt <= 20'd0;
endalways@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt_full <= 1'b0;else if(cnt == 999_999)cnt_full <= 1'b1;elsecnt_full <= 1'b0;
end
狀態(tài)機(jī)主程序:
//------<狀態(tài)機(jī)主程序>------
//狀態(tài)機(jī)主程序
always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endelse begincase(curr_st)IDLE:beginkey_flag <= 1'b0;if(nedge)begincurr_st <= FILTER1;en_cnt <= 1'b1;endelsecurr_st <= IDLE;endFILTER1:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b0;curr_st <= DOWN;en_cnt <= 1'b0;end else if(pedge)begincurr_st <= IDLE;en_cnt <= 1'b0;endelsecurr_st <= FILTER1;endDOWN:beginkey_flag <= 1'b0;if(pedge)begincurr_st <= FILTER2;en_cnt <= 1'b1;endelsecurr_st <= DOWN;endFILTER2:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b1;curr_st <= IDLE;en_cnt <= 1'b0;end else if(nedge)begincurr_st <= DOWN;en_cnt <= 1'b0;endelsecurr_st <= FILTER2;enddefault:begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endendcaseend
end
三、仿真測試
3.1 常規(guī)編寫
`timescale 1ns/1ns
`define clock_period 20module KeyFilter_tb;reg Clk;reg Rst_n;reg key_in;wire key_flag;wire key_state;KeyFilter KeyFilter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));initial Clk = 1;always#(`clock_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;key_in = 1'b1;#(`clock_period*10);Rst_n = 1'b1;#(`clock_period*10 + 1);key_in = 0;#1000;key_in = 1;#2000;key_in = 0;#1400;key_in = 1;#2600;key_in = 0;#1300;key_in = 1;#200;key_in = 0;#20000100;#50000000;key_in = 1;#2600;key_in = 0;#1000;key_in = 1;#2000;key_in = 0;#1400;key_in = 1;#2600;key_in = 0;#1300;key_in = 1;#200;key_in = 1;#20000100;#50000000;$stop;endendmodule
仿真結(jié)果:
3.2 task編寫
`timescale 1ns/1ns
`define clock_period 20module KeyFilter_tb;reg Clk;reg Rst_n;reg key_in;wire key_flag;wire key_state;KeyFilter KeyFilter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));initial Clk = 1;always#(`clock_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;key_in = 1'b1;#(`clock_period*10);Rst_n = 1'b1;#(`clock_period*10 + 1);#30000;PressKey; #10000;PressKey; #10000;PressKey; #10000;$stop;endreg [15:0]myrand;task PressKey;begin//50次隨機(jī)時間按下抖動repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key_in = ~key_in;endkey_in = 0;#50_000_000;//按下穩(wěn)定//50次隨機(jī)時間釋放抖動repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key_in = ~key_in;endkey_in = 1;#50_000_000;//釋放穩(wěn)定endendtaskendmodule
注意$random
隨機(jī)函數(shù)的用法:
$random
這一系統(tǒng)函數(shù)可以產(chǎn)生一個有符號的32bit
隨機(jī)整數(shù)。一般的用法是$random%b
,其中b>0
。這樣就會生成一個范圍在(-b+1):(b-1)
中的隨機(jī)數(shù)。如果只得到正數(shù)的隨機(jī)數(shù),可采用{$random}%b
來產(chǎn)生。
myrand = {$random}%65536;//0~65535
上述語句的作用即是產(chǎn)生了0~65535
之間的隨機(jī)數(shù)。
通過repeat語句
循環(huán)50
次,就產(chǎn)生了50次不同的延時效果:
repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key_in = ~key_in;
end
仿真結(jié)果:
四、仿真模型
編寫key_model并添加到測試激勵文件中:
`timescale 1ns/1nsmodule key_model(key);output reg key;reg [15:0]myrand;initial beginkey = 1'b1;PressKey; #10000;PressKey; #10000;PressKey; #10000;$stop;endtask PressKey;begin//50次隨機(jī)時間按下抖動repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key = ~key;endkey = 0;#50_000_000;//按下穩(wěn)定//50次隨機(jī)時間釋放抖動repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key = ~key;endkey = 1;#50_000_000;//釋放穩(wěn)定endendtask endmodule
修改KeyFilter_tb:
`timescale 1ns/1ns
`define clock_period 20module KeyFilter_tb;reg Clk;reg Rst_n;wire key_in;wire key_flag;wire key_state;KeyFilter KeyFilter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));key_model key_model0(.key(key_in));initial Clk = 1;always#(`clock_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;#(`clock_period*10);Rst_n = 1'b1;#(`clock_period*10 + 1);endendmodule
整個激勵文件的內(nèi)部結(jié)構(gòu):
仿真結(jié)果:
🧸結(jié)尾
- ?? 感謝您的支持和鼓勵! 😊🙏
- 📜您可能感興趣的內(nèi)容:
- 【FPGA】串口通信講解-狀態(tài)機(jī)判斷數(shù)據(jù)值
- 【Python】串口通信-與FPGA、藍(lán)牙模塊實現(xiàn)串口通信(Python+FPGA)
- 【Arduino TinyGo】【最新】使用Go語言編寫Arduino-環(huán)境搭建和點亮LED燈
- 【全網(wǎng)首發(fā)開源教程】【Labview機(jī)器人仿真與控制】Labview與Solidworks多路支配關(guān)系-四足爬行機(jī)器人仿真與控制