|
幾乎沒有哪一個(gè)系統(tǒng)沒有輸入輸出設(shè)備,大到顯示器,小到led燈,輕觸按鍵。作為一個(gè)系統(tǒng),要想穩(wěn)定的工作,輸入輸出設(shè)備的性能占了很重要的角色。本實(shí)驗(yàn),小梅哥就通過一個(gè)獨(dú)立按鍵的檢測實(shí)驗(yàn),來正式步入基本外設(shè)驅(qū)動(dòng)開發(fā)的大門。 一、 實(shí)驗(yàn)?zāi)康?br /> 實(shí)現(xiàn)4個(gè)獨(dú)立按鍵的抖動(dòng)檢測實(shí)驗(yàn),并通過4個(gè)獨(dú)立按鍵控制4個(gè)led燈亮滅狀態(tài)的翻轉(zhuǎn)。 二、 實(shí)驗(yàn)原理 實(shí)際系統(tǒng)中常用的按鍵大部分都是輕觸式按鍵,如圖2-1所示。該按鍵內(nèi)部由一個(gè)彈簧片和兩個(gè)固定觸點(diǎn)組成,當(dāng)彈簧片被按下,則兩個(gè)固定觸點(diǎn)接通,按鍵閉合。彈簧片松開,兩個(gè)觸點(diǎn)斷開,按鍵也就斷開了。根據(jù)這種按鍵的機(jī)械特性,在按鍵按下時(shí),會(huì)先有一段時(shí)間的不穩(wěn)定期,在這期間,兩個(gè)觸點(diǎn)時(shí)而接通,時(shí)而斷開,我們稱之為抖動(dòng),當(dāng)按鍵大約按下20ms后,兩個(gè)觸點(diǎn)才能處于穩(wěn)定的閉合狀態(tài),按鍵松開時(shí)和閉合時(shí)情況類似。而我們的FPGA工作在很高的頻率,按鍵接通或斷開時(shí)任何一點(diǎn)小的抖動(dòng)都能輕易的捕捉到,如果不加區(qū)分的將每一次閉合或斷開都當(dāng)做一次按鍵事件,那么勢必一次按鍵動(dòng)作會(huì)被FPGA識別為很多次按鍵操作,從而導(dǎo)致系統(tǒng)工作穩(wěn)定性下降。 圖2-1 輕觸按鍵實(shí)物圖 一次按鍵動(dòng)作的大致波形如下圖所示: 因此,我們所需要做的工作,就是濾除按鍵按下和釋放時(shí)各存在的20ms的不穩(wěn)定波形 三、 硬件設(shè)計(jì) 獨(dú)立按鍵屬于一種輸入設(shè)備,其與FPGA連接的IO口被接上了10K的上拉電阻,在按鍵沒有按下時(shí),F(xiàn)PGA會(huì)檢測到高電平;當(dāng)按鍵按下后,F(xiàn)PGA的IO口上則將呈現(xiàn)低電平。因此,按鍵檢測的實(shí)質(zhì)就是讀取FPGA的IO上的電平。 圖3-1 獨(dú)立按鍵典型電路 四、 架構(gòu)設(shè)計(jì) 本實(shí)驗(yàn)由總共四個(gè)模塊組成,分別為LED驅(qū)動(dòng)模塊、獨(dú)立按鍵檢測模塊、控制模塊和頂層模塊,其架構(gòu)如下: 以下為按鍵抖動(dòng)檢測的代碼,采用狀態(tài)機(jī)的方式編寫,總共有兩個(gè)狀態(tài),按下消抖為狀態(tài)0,釋放消抖為狀態(tài)1。具體的消抖流程代碼中的注釋已經(jīng)寫的比較清楚,但如果全部用文字解釋出來還是有一定的復(fù)雜性。這也是實(shí)地講解和網(wǎng)上文檔的一點(diǎn)點(diǎn)差距吧,希望我后期的視頻里面能講清楚。其實(shí)抖動(dòng)消除的核心思路就是對按鍵狀態(tài)的變化進(jìn)行計(jì)時(shí),若兩次電平變化之間時(shí)間小于20ms,則視為抖動(dòng),若低電平穩(wěn)定時(shí)間超過20ms,則表明檢測到了穩(wěn)定的按鍵狀態(tài)。釋放時(shí)的消抖過程與按下時(shí)的消抖過程類似。 以下是代碼片段: module normal_keys_detect #(parameter KEY_WIDTH = 4) (Clk,Rst_n,Key_in,Key_Flag,Key_Value); input Clk; input Rst_n; input [KEY_WIDTH-1:0]Key_in; output reg Key_Flag; output reg[KEY_WIDTH-1:0]Key_Value; reg [KEY_WIDTH-1:0]key_tmp,key_tmp1; reg [19:0]cnt1; reg state; wire level_change; /*按鍵狀態(tài)變化標(biāo)志信號*/ localparam cnt1_TOP = 1_000_000; /*-------存儲按鍵狀態(tài)的上一個(gè)狀態(tài)---------------*/ always @ (posedge Clk or negedge Rst_n) begin if(!Rst_n) begin key_tmp 按鍵檢測的結(jié)果進(jìn)行觀察和分析,通過仿真,驗(yàn)證設(shè)計(jì)的正確性和合理性。按鍵消抖模塊的testbench的代碼如下: 以下是代碼片段: `timescale 1ns/1ns module normal_keys_detect_tb; reg Clk; reg Rst_n; reg [3:0]Key_in; wire Key_Flag; wire [3:0]Key_Value; normal_keys_detect #( .KEY_WIDTH(4) ) normal_keys_detect_inst1( .Clk(Clk), .Rst_n(Rst_n), .Key_in(Key_in), .Key_Flag(Key_Flag), .Key_Value(Key_Value) ); initial begin Clk = 1; Rst_n = 0; Key_in = 4'b1111; #100; Rst_n = 1; press_key(0); #30000000; press_key(1); #30000000; press_key(2); #30000000; press_key(3); #30000000; $stop; end always #10 Clk = ~Clk; task press_key; input [1:0]Key; begin Key_in = 4'b1111; /*按下抖動(dòng)*/ #100 Key_in[Key] = 0; #200 Key_in[Key] = 1; #300 Key_in[Key] = 0; #400 Key_in[Key] = 1; #500 Key_in[Key] = 0; #600 Key_in[Key] = 1; #700 Key_in[Key] = 0; #800 Key_in[Key] = 1; #900 Key_in[Key] = 0; /*穩(wěn)定期*/ #22000000; /*釋放抖動(dòng)*/ #100 Key_in[Key] = 1; #200 Key_in[Key] = 0; #300 Key_in[Key] = 1; #400 Key_in[Key] = 0; #500 Key_in[Key] = 1; #600 Key_in[Key] = 0; #700 Key_in[Key] = 1; #800 Key_in[Key] = 0; #900 Key_in[Key] = 1; end endtask endmodule testben中使用了一個(gè)任務(wù)(task),該任務(wù)模擬按鍵抖動(dòng)的過程,給按鍵按下和釋放時(shí)增加抖動(dòng),調(diào)用時(shí)只需要輸入需要按下的按鍵編號,該任務(wù)便可自動(dòng)完成按下抖動(dòng)、穩(wěn)定、松開抖動(dòng)的過程。 整個(gè)工程的testbench與消抖模塊的testbench一樣,只需要在例化部分將消抖模塊替換為頂層模塊即可,同時(shí)將每個(gè)按鍵的任務(wù)由一次調(diào)用該為兩次調(diào)用即可,詳細(xì)代碼如下: 以下是代碼片段: `timescale 1ns/1ns module top_tb; reg Clk; reg Rst_n; reg [3:0]Key_in; wire [3:0]Led; top top_inst( .Clk(Clk), .Rst_n(Rst_n), .Key_in(Key_in), .Led(Led) ); initial begin Clk = 1; Rst_n = 0; Key_in = 4'b1111; #100; Rst_n = 1; press_key(0); #30000000; press_key(0); #30000000; press_key(1); #30000000; press_key(1); #30000000; press_key(2); #30000000; press_key(2); #30000000; press_key(3); #30000000; press_key(3); #30000000; $stop; end always #10 Clk = ~Clk; task press_key; input [1:0]Key; begin Key_in = 4'b1111; /*按下抖動(dòng)*/ #100 Key_in[Key] = 0; #200 Key_in[Key] = 1; #300 Key_in[Key] = 0; #400 Key_in[Key] = 1; #500 Key_in[Key] = 0; #600 Key_in[Key] = 1; #700 Key_in[Key] = 0; #800 Key_in[Key] = 1; #900 Key_in[Key] = 0; /*穩(wěn)定期*/ #22000000; /*釋放抖動(dòng)*/ #100 Key_in[Key] = 1; #200 Key_in[Key] = 0; #300 Key_in[Key] = 1; #400 Key_in[Key] = 0; #500 Key_in[Key] = 1; #600 Key_in[Key] = 0; #700 Key_in[Key] = 1; #800 Key_in[Key] = 0; #900 Key_in[Key] = 1; end endtask endmodule |