【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗四:按鍵模組③ — 單擊與雙擊
實驗四:按鍵模組③ — 單擊與雙擊
實驗三我們建立了“點選”還有“長點選”等有效按鍵的多功能按鍵模組。在此,實驗四同樣也是建立多功能按鍵模組,不過卻有不同的有效按鍵。實驗四的按鍵功能模組有以下兩項有效按鍵:
l 單擊(按下有效);
l 雙擊(連續按下兩下有效)。
圖4.1 單擊有效按鍵,時序示意圖。
實驗四的“單擊”基本上與實驗三的“點選”一模一樣,既按鍵被按下,經過消抖以後isSClick訊號被拉高一個時鐘,結果如圖4.1所示,過程非常單調。反之,“雙擊”實現起來,會比較麻煩一些,因為我們還要考慮而外的細節,即人為連打極限。所謂人為連打極限就是兩次按下按鍵之間的最短間隔。
如圖4.2 雙擊有效按鍵,時序示意圖(雙擊成功)。
根據筆者的理解,常人的連打極限是60ms左右,筆者是80ms左右,超人是20ms左右。
為了相容常人還有筆者的連打極限,我們必須設定有效的連擊時限,為此100ms是最好的選擇。如圖4.2所示,假設那是按鍵過程,筆者先是緩緩按下然後又緩緩釋放按鍵完成第一次按鍵行為,結果有如往常般,按下事件發生,抖動發生,釋放事件發生,抖動發生,但是 isDClick(Double Click)訊號還有isSClick(Single Click)訊號都沒有產生高脈衝。
第一次按鍵完成以後就會引來第二次按鍵的黃金時間,亦即有效連擊時限,在此筆者設為100ms。假設筆者在這100ms的黃金時間內按下按鍵,那麼 isDClick訊號會立即產生高脈衝。餘下有如往常那樣,抖動發生,釋放事件發生,抖動發生 ... 對此,isSClick由始至終都沒有狀況發生。
如圖4.3 雙擊有效按鍵,時序示意圖(雙擊失敗)。
假設筆者沒在有限的100ms黃金時間內執行第二次按鍵按下的動作,那麼“雙擊”就會失敗,結果如圖4.3所示。第一次按鍵過程與圖4.2一樣,反之第二次按鍵卻不同了,如圖4.3所示,第二次按鍵的按下事件是發生在100ms以後,為此isSClick產生高脈衝,然而isDClick訊號卻沒有動靜。
明白上述這些簡單的原理以後,我們就可以開始建模了。
圖4.4 實驗四的建模圖。
如圖4.4所示,那是實驗四的建模圖,它有一位 KEY輸入端,連線至按鍵資源。此外,它也有兩位 LED輸出端,分別連線兩位LED資源。
key_funcmod.v
1. module key_funcmod
2. (
3. input CLOCK, RESET,
4. input KEY,
5. output [1:0]LED
6. );
以上內容為出入端的相關宣告。
7. parameter T10MS = 28'd500_000;
8. parameter T100MS = 28'd5_000_000;
9. parameter T200MS = 28'd10_000_000;
10. parameter T300MS = 28'd15_000_000;
11. parameter T400MS = 28'd20_000_000;
12. parameter T500MS = 28'd25_000_000;
13.
14. /**********************************/ //sub
15.
16. reg F2,F1;
17.
18. always @ ( posedge CLOCK or negedge RESET )
19. if( !RESET )
20. { F2, F1 } <= 2'b11;
21. else
22. { F2, F1 } <= { F1, KEY };
23.
24. /**********************************/ //core
25.
26. wire isH2L = ( F2 == 1 && F1 == 0 );
27. wire isL2H = ( F2 == 0 && F1 == 1 );
以上內容為相關常量宣告,周邊操作以及即時宣告。第7~12行是各種時間的常量宣告,除了10毫秒宣告消抖時間以外,第8~12行的時間常量是用來自定義雙擊的敏感度。第18~23行是檢測電平狀態的周邊操作,第26~27行則是按下事件還有釋放事件。
28. reg [3:0]i;
29. reg isDClick,isSClick;
30. reg [1:0]isTag;
31. reg [27:0]C1;
32.
33. always @ ( posedge CLOCK or negedge RESET )
34. if( !RESET )
35. begin
36. i <= 4'd0;
37. isDClick <= 1'd0;
38. isSClick <= 1'b0;
39. isTag <= 2'd0;
40. C1 <= 28'd0;
41. end
42. else
以上內容為相關暫存器聲以及復位操作。i用作指向步驟,isDClick 還有 isSClick是用作標示“雙擊”還有“單擊”。isTag是有效按鍵的標籤,C1則用來計數。至於第33~41行則是核心操作的復位活動。
43. case(i)
44.
45. 0: // Wait H2L
46. if( isH2L ) begin i <= i + 1'b1; end
47.
48. 1: // H2L debounce
49. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
50. else C1 <= C1 + 1'b1;
51.
52. 2: // Wait L2H
53. if( isL2H ) i <= i + 1'b1;
54.
55. 3: // L2H debounce
56. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
57. else C1 <= C1 + 1'b1;
58.
59. 4: // Key Tag Check
60. if( isH2L && C1 <= T100MS -1 ) begin isTag <= 2'd2; C1 <= 28'd0; i <= i + 1'b1; end
61. else if( C1 >= T100MS -1) begin isTag <= 2'd1; C1 <= 28'd0; i <= i + 1'b1; end
62. else C1 <= C1 + 1'b1;
63.
64. 5: // Key trigger press up
65. if( isTag == 2'd2 ) begin isDClick <= 1'b1; i <= i + 1'b1; end
66. else if( isTag == 2'd1 ) begin isSClick <= 1'b1; i <= i + 1'b1; end
67.
68. 6: // Key trigger pree down
69. begin { isSClick , isDClick } <= 2'b00; i <= i + 1'b1; end
70.
71. 7: // L2H deounce check
72. if( isTag == 2'd1 ) begin isTag <= 2'd0; i <= 4'd0; end
73. else if( isTag == 2'd2 ) begin isTag <= 2'd0; i <= i + 1'b1; end
74.
75. 8: // Wait L2H
76. if( isL2H ) begin i <= i + 1'b1; end
77.
78. 9: // L2H debounce
79. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= 4'd0; end
80. else C1 <= C1 + 1'b1;
81.
82. endcase
以上內容為核心操作。具體過程如下所示:
步驟0,等待第一次按下事件;
步驟1,過濾抖動;
步驟2,等待第一次釋放事件;
步驟3,過濾抖動;
步驟4,如果100ms發生第二回按下事件,isTag設定為2,反之isTag設定為1;
步驟5~6,根據isTag的內容拉高又拉低 isDClick或者 isSClick;
步驟7,根據S內容,S為1便清除S然後返回步驟0。反之,isTag為2就清除isTag然後步驟繼續前進;
步驟8,等待第二次釋放事件;
步驟9:,過濾抖動然後返回步驟0。
83.
84. /*************************/ // sub-demo
85.
86. reg [1:0]D1;
87.
88. always @ ( posedge CLOCK or negedge RESET )
89. if( !RESET )
90. D1 <= 2'd0;
91. else if( isDClick )
92. D1[1] <= ~D1[1];
93. else if( isSClick )
94. D1[0] <= ~D1[0];
95.
96. /***************************/
97.
98. assign LED = D1;
99.
100. endmodule
以上內容為演示用的周邊操作以及輸出驅動宣告。它會根據 isDClick 還有 isSClick的高脈衝翻轉 D1[1] 還有 D1[0] 的內容。第98行則是輸出驅動宣告。編譯完後便下載程式。
當我們雙擊 <KEY2> 建 LED[1] 就會發亮,然後再雙擊 <KEY2> 建 LED[1] 則會消滅,發生雙擊的前提條件是 ... 第一次按下時間的100ms之內必須發生第二次按下時間才能成立。換之,如果我們單擊 <KEY2>建 LED[0] 便會發亮,再單擊 <KEY2> 建 LED[0]則會消滅。
細節一: 雙擊的敏感度
parameter T100MS = 28'd5_000_000;
parameter T200MS = 28'd10_000_000;
parameter T300MS = 28'd15_000_000;
parameter T400MS = 28'd20_000_000;
parameter T500MS = 28'd25_000_000;
程式碼4.1
程式碼4.1是key_funcmod 的部分內容,亦即100ms~500ms的時間宣告。所謂雙擊的敏感度就是按鍵第二次按下所有效的時限。事實上,有效時間100ms 已經足夠應付一般“雙擊”要求,然而“雙擊”的敏感度除了人為連打極限這個因數以外,還有按鍵資源本身。開發板常見的按鍵都是經濟型機械按鍵,手感較為遲鈍,所以有效時間推薦在100ms~500ms範圍之內。
如果讀者所使用的按鍵資源是精緻的傢伙,想必手感一定很爽,例如滑鼠之類的按鍵,100ms 有效時間可能會影響雙擊的敏感度。為此,有效時間必須設定在 40ms~100ms的範圍,常見的有效時間是50ms。最後不管怎麼樣,手感還有敏感度這種東西非常曖昧,完全因人而異 ... 也有傳言說那些骨灰級的遊戲滑鼠是可以自定義敏感度,事實究竟如何?對於筆者這種遊戲冷漠者則是永遠的迷。
細節二:完成的個體模組
圖4.5 完整的按鍵功能模組。
如圖4.5所示,那是完整的按鍵功能模組,輸入端一邊的KEY連線至按鍵資源,Trig[1]產生“單擊”的個高脈衝,Trig[0]產生“雙擊”的個高脈衝。
key_funcmod.v
1. module key_funcmod
2. (
3. input CLOCK, RESET,
4. input KEY,
5. output [1:0]oTrig
6. );
7. parameter T10MS = 28'd500_000;
8. parameter T100MS = 28'd5_000_000;
9. parameter T200MS = 28'd10_000_000;
10. parameter T300MS = 28'd15_000_000;
11. parameter T400MS = 28'd20_000_000;
12. parameter T500MS = 28'd25_000_000;
13.
14. /**********************************/ //sub
15.
16. reg F2,F1;
17.
18. always @ ( posedge CLOCK or negedge RESET )
19. if( !RESET )
20. { F2, F1 } <= 2'b11;
21. else
22. { F2, F1 } <= { F1, KEY };
23.
24. /**********************************/ //core
25.
26. wire isH2L = ( F2 == 1 && F1 == 0 );
27. wire isL2H = ( F2 == 0 && F1 == 1 );
28. reg [3:0]i;
29. reg isDClick,isSClick;
30. reg [1:0]isTag;
31. reg [27:0]C1;
32.
33. always @ ( posedge CLOCK or negedge RESET )
34. if( !RESET )
35. begin
36. i <= 4'd0;
37. isDClick <= 1'd0;
38. isSClick <= 1'b0;
39. isTag <= 2'd0;
40. C1 <= 28'd0;
41. end
42. else
43. case(i)
44.
45. 0: // Wait H2L
46. if( isH2L ) begin i <= i + 1'b1; end
47.
48. 1: // H2L debounce
49. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
50. else C1 <= C1 + 1'b1;
51.
52. 2: // Wait L2H
53. if( isL2H ) i <= i + 1'b1;
54.
55. 3: // L2H debounce
56. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
57. else C1 <= C1 + 1'b1;
58.
59. 4: // Key Tag Check
60. if( isH2L && C1 <= T100MS -1 ) begin isTag <= 2'd2; C1 <= 28'd0; i <= i + 1'b1; end
61. else if( C1 >= T100MS -1) begin isTag <= 2'd1; C1 <= 28'd0; i <= i + 1'b1; end
62. else C1 <= C1 + 1'b1;
63.
64. 5: // Key trigger press up
65. if( isTag == 2'd2 ) begin isDClick <= 1'b1; i <= i + 1'b1; end
66. else if( isTag == 2'd1 ) begin isSClick <= 1'b1; i <= i + 1'b1; end
67.
68. 6: // Key trigger pree down
69. begin { isSClick , isDClick } <= 2'b00; i <= i + 1'b1; end
70.
71. 7: // L2H deounce check
72. if( isTag == 2'd1 ) begin isTag <= 2'd0; i <= 4'd0; end
73. else if( isTag == 2'd2 ) begin isTag <= 2'd0; i <= i + 1'b1; end
74.
75. 8: // Wait L2H
76. if( isL2H ) begin i <= i + 1'b1; end
77.
78. 9: // L2H debounce
79. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= 4'd0; end
80. else C1 <= C1 + 1'b1;
81.
82. endcase
83.
84. /***************************/
85.
86. assign oTrig = { isSClick,isDClick };
87.
88. endmodule
相關推薦
【黑金原創教程】【FPGA那些事兒-驅動篇I 】【實驗一】流水燈模組
實驗一:流水燈模組 對於發展商而言,動土儀式無疑是最重要的任務。為此,流水燈實驗作為低階建模II的動土儀式再適合不過了。廢話少說,我們還是開始實驗吧。 圖1.1 實驗一建模圖。 如圖1.1 所示,實驗一有名為 led_funcmod的功能模組。如果無視環境訊號(時鐘訊號還有復位訊號),該功能模組只有
【黑金原創教程】【FPGA那些事兒-驅動篇I 】連載導讀
前言: 無數晝夜的來回輪替以後,這本《驅動篇I》終於編輯完畢了,筆者真的感動到連鼻涕也流下來。所謂驅動就是認識硬體,還有前期建模。雖然《驅動篇I》的硬體都是我們熟悉的老友記,例如UART,VGA等,但是《驅動篇I》貴就貴在建模技巧的昇華,亦即低階建模II。 話說低階建模II,讀過《建模篇》的朋友多少也會面
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗三:按鍵模組② — 點選與長點選
實驗三:按鍵模組② — 點選與長點選 實驗二我們學過按鍵功能模組的基礎內容,其中我們知道按鍵功能模組有如下操作: l 電平變化檢測; l 過濾抖動; l 產生有效按鍵。 實驗三我們也會z執行同樣的事情,不過卻是產生不一樣的有效按鍵: l 按下有效(點選); l 長按下有效(長點選)。 圖3
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗二:按鍵模組①
實驗二:按鍵模組① - 消抖 按鍵消抖實驗可謂是經典中的經典,按鍵消抖實驗雖曾在《建模篇》出現過,而且還惹來一堆麻煩。事實上,筆者這是在刁難各位同學,好讓對方的慣性思維短路一下,但是慘遭口水攻擊 ... 面對它,筆者宛如被甩的男人,對它又愛又恨。不管怎麼樣,如今 I’ll be back,筆者再也不會重複一
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗六:數碼管模組
實驗六:數碼管模組 有關數碼管的驅動,想必讀者已經學爛了 ... 不過,作為學習的新儀式,再爛的東西也要溫故知新,不然學習就會不健全。黑金開發板上的數碼管資源,由始至終都沒有改變過,筆者因此由身懷念。為了點亮多位數碼管從而顯示數字,一般都會採用動態掃描,然而有關動態掃描的資訊請怒筆者不再重複。在此,同樣也是
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗四:按鍵模組③ — 單擊與雙擊
實驗四:按鍵模組③ — 單擊與雙擊 實驗三我們建立了“點選”還有“長點選”等有效按鍵的多功能按鍵模組。在此,實驗四同樣也是建立多功能按鍵模組,不過卻有不同的有效按鍵。實驗四的按鍵功能模組有以下兩項有效按鍵: l 單擊(按下有效); l 雙擊(連續按下兩下有效)。 圖4.1 單擊有效按鍵,時序示意圖
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗五:按鍵模組④ — 點選,長點選,雙擊
實驗五:按鍵模組④ — 點選,長點選,雙擊 實驗二至實驗四,我們一共完成如下有效按鍵: l 點選(按下有效) l 點選(釋放有效) l 長擊(長按下有效) l 雙擊(連續按下有效) 然而,不管哪個實驗都是隻有兩項“功能”的按鍵模組而已,如今我們要建立三項“功能”的按鍵模組,亦即點選(按下有效),長
【黑金原創教程】【FPGA那些事兒-驅動篇I 】原創教程連載導讀【連載完成,共二十九章】
前言: 無數晝夜的來回輪替以後,這本《驅動篇I》終於編輯完畢了,筆者真的感動到連鼻涕也流下來。所謂驅動就是認識硬體,還有前期建模。雖然《驅動篇I》的硬體都是我們熟悉的老友記,例如UART,VGA等,但是《驅動篇I》貴就貴在建模技巧的昇華,亦即低階建模II。 話說低階建模II,讀過《建模篇》的朋友多少也會面
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗二十一:SDRAM模組④ — 頁讀寫 β
實驗二十一:SDRAM模組④ — 頁讀寫 β 未進入主題之前,讓我們先來談談一些重要的體外話。《整合篇》之際,筆者曾經比擬Verilog如何模仿for迴圈,我們知道for迴圈是順序語言的產物,如果Verilog要實現屬於自己的for迴圈,那麼它要考慮的東西除了步驟以外,還有非常關鍵的時鐘。 for(
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗十:PS/2模組④ — 普通滑鼠
實驗十:PS/2模組④ — 普通滑鼠 學習PS/2鍵盤以後,接下來就要學習 PS/2 滑鼠。PS/2滑鼠相較PS/2鍵盤,驅動難度稍微高了一點點,因為FPGA(從機)不僅僅是從PS/2滑鼠哪裡讀取資料,FPGA還要往滑鼠裡寫資料 ... 反之,FPGA只要對PS/2鍵盤讀取資料即可。然而,最傷腦筋的地方就在
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗十八:SDRAM模組① — 單字讀寫
實驗十八:SDRAM模組① — 單字讀寫 筆者與SDRAM有段不短的孽緣,它作為冤魂日夜不斷糾纏筆者。筆者嘗試過許多方法將其退散,不過屢試屢敗的筆者,最終心情像橘子一樣橙。《整合篇》之際,筆者曾經大戰幾回兒,不過內容都是點到即止。最近它破蠱而出,日夜不停:“好~痛苦!好~痛苦!”地呻吟著,嚇得筆者不敢半夜如
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗二十七:TFT模組
實驗二十七:TFT模組 - 顯示 所謂TFT(Thin Film Transistor)就是眾多LCD當中,其中一種支援顏色的LCD,相較古老的點陣LCD(12864笑),它可謂高階了。黑金的TFT LCD除了320×240大小以外,內建SSD1289控制器,同時也是獨立模組。事實上,無論是驅動點陣LCD還
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗十三:串列埠模組② — 接收
實驗十三:串列埠模組② — 接收 我們在實驗十二實現了串列埠傳送,然而這章實驗則要實現串列埠接收 ... 在此,筆者也會使用其它思路實現串列埠接收。 圖13.1 模組之間的資料傳輸。 假設我們不考慮波特率,而且一幀資料之間的傳輸也只是發生在FPGA之間,即兩隻模組之間互轉,並且兩塊模組都使用相同的時
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗七:PS/2模組① — 鍵盤
實驗七:PS/2模組① — 鍵盤 實驗七依然也是熟爛的PS/2鍵盤。相較《建模篇》的PS/2鍵盤實驗,實驗七實除了實現基本的驅動以外,我們還要深入解PS/2時序,還有PS/2鍵盤的行為。不過,為了節省珍貴的頁數,怒筆者不再重複有關PS/2的基礎內容,那些不曉得的讀者請複習《建模篇》或者自行谷歌一下。 市場
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗九:PS/2模組③ — 鍵盤與多組合鍵
實驗九:PS/2模組③ — 鍵盤與多組合鍵 筆者曾經說過,通碼除了單位元組以外,也有雙位元組通碼,而且雙位元組通碼都是 8’hE0開頭,別名又是 E0按鍵。常見的的E0按鍵有,<↑>,<↓>,<←>,<→>,<HOME>,<PRTSC>
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗二十五:SDHC模組
實驗二十五:SDHC模組 筆者曾經說過,SD卡發展至今已經衍生許多版本,實驗二十四就是針對版本SDV1.×的SD卡。實驗二十四也說過,CMD24還有CMD17會故意偏移地址29,讓原本範圍指向從原本的232 變成 223,原因是SD卡讀寫一次都有512個位元組。為此我們可以這樣計算: SDV1.x = 2
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗二十:SDRAM模組③ — 頁讀寫 α
實驗二十:SDRAM模組③ — 頁讀寫 α 完成單字讀寫與多字讀寫以後,接下來我們要實驗頁讀寫。醜話當前,實驗二十的頁讀寫只是實驗性質的東西,其中不存在任何實用價值,筆者希望讀者可以把它當成頁讀寫的熱身運動。 表示20.1 Mode Register的內容。 Mode Register
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗二十八:TFT模組
實驗二十八:TFT模組 - 觸屏 讀者在上一個實驗所玩弄過的 TFT LCD模組,除了顯示大小為 320 × 240,顏色為16位RGB的影象資訊以外,它還支援觸屏。所謂觸屏就是滑鼠還有鍵盤以外的輸入手段,例如現在流行平板還有智慧手機,觸屏輸入對我們來說,已經成為日常的一部分。描述語言一門偏向硬體的語言
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗二十二:SDRAM模組⑤ — FIFO讀寫
經過漫長的戰鬥以後,我們終於來到最後。對於普通人而言,頁讀寫就是一名戰士的墓碑(最終戰役) ... 然而,怕死的筆者想透過這個實驗告訴讀者,旅程的終點就是旅程的起點。一直以來,筆者都在煩惱“SDRAM是否應該成為儲存類?”SDRAM作為一介儲存資源(儲存器),它的好處就是大容量空間,壞處則就是麻煩的控制規
【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗十四:儲存模組
實驗十四比起動手筆者更加註重原理,因為實驗十四要討論的東西,不是其它而是低階建模II之一的模組類,即儲存模組。接觸順序語言之際,“儲存”不禁讓人聯想到變數或者陣列,結果它們好比資料的暫存空間。 1. int main() 2. { 3. int VarA; 4.