實驗五:按鍵模塊④ — 點擊,長點擊,雙擊
實驗二至實驗四,我們一共完成如下有效按鍵:
l 點擊(按下有效)
l 點擊(釋放有效)
l 長擊(長按下有效)
l 雙擊(連續按下有效)
然而,不管哪個實驗都是只有兩項“功能”的按鍵模塊而已,如今我們要創建三項“功能”的按鍵模塊,亦即點擊(按下有效),長擊,還有雙擊。實驗繼續之前,讓我們先來復習一下各種有效按鍵。
圖5.1 點擊(按下有效)。
如圖5.1所示,所謂點擊(按下有效)就是按鍵按下以后,isSClick信號(Single Click)
產生一個高脈沖。
圖5.2 長點擊。
如圖5.2所示,所謂長點擊就是按鍵按下以后,長達3秒不放,isLClick信號(Long Click)
就會產生一個高脈沖。
圖5.3 雙點擊。
如圖5.3所示,所謂雙點擊就是距離第一次“點擊(按下有效)”,如果在有效的連擊時限內容完成第二次“點擊(按下有效)”,那么isDClick信號(Double Click)就會產生一個高脈沖。
話語上,實驗五雖然要整合3項按鍵功能,然而實驗五相較實驗二至實驗四卻有一定程度的難度。那是因為僅有兩項功能的按鍵模塊,僅需判斷一次性的有效按鍵而已,反之三項功能的按鍵模塊卻要分開二次判斷,而且判斷也要有序。根據實驗五,核心操作會優先判斷按鍵是否“長點擊”,然后再來判斷“點擊”還是“雙點擊”。期間絕對不能搞錯判斷的次序。
具體內容,我們還是直接來看代碼吧:
圖5.4 實驗五的建模圖。
如圖5.4所示,那是實驗五的按鍵功能模塊,輸入端一邊是連接至按鍵資源的KEY信號,輸出端另一邊則是連接至3位LED資源的LED信號。
key_funcmod.v
1. module key_funcmod
2. (
3. input CLOCK, RESET,
4. input KEY,
5. output [2: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. parameter T3S = 28'd150_000_000;
14.
15. /**********************************/ //sub
16.
17. reg F2,F1;
18.
19. always @ ( posedge CLOCK or negedge RESET )
20. if( !RESET )
21. { F2, F1 } <= 2'b11;
22. else
23. { F2, F1 } <= { F1, KEY };
24.
25. /**********************************/ //core
26.
27. wire isH2L = ( F2 == 1 && F1 == 0 );
28. wire isL2H = ( F2 == 0 && F1 == 1 );
以上內容是時間常量聲明以及周邊操作。第17~23行是檢測電平狀態的周邊操作,第27~28行則是按下事件還有釋放事件的聲明。
29. reg [3:0]i;
30. reg isLClick, isDClick,isSClick;
31. reg [1:0]isTag;
32. reg [27:0]C1;
33.
34. always @ ( posedge CLOCK or negedge RESET )
35. if( !RESET )
36. begin
37. i <= 4'd0;
38. isLClick <= 1'b0;
39. isDClick <= 1'b0;
40. isSClick <= 1'b0;
41. isTag <= 2'd0;
42. C1 <= 28'd0;
43. end
44. else
以上內容為相關的寄存器聲明以及復位操作。i指向步驟,寄存器isLClick,寄存器isDClick,還有寄存器isSClick則是相關的“有效按鍵”標志。isTag表示“有效按鍵”的標簽,1為“點擊”,2為“雙點擊”,3為“長點擊”。C1用來計數。第35~43行則是相關的復位操作。
45. case(i)
46.
47. 0: // Wait H2L
48. if( isH2L ) begin i <= i + 1'b1; end
49.
50. 1: // H2L debounce
51. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
52. else C1 <= C1 + 1'b1;
53.
54. 2: // Key Tag Check 1
55. if( isL2H ) begin C1 <= 28'd0; i <= i + 1'b1; end
56. else if( {F2,F1} == 2'b00 && C1 >= T3S -1 ) begin isTag <= 2'd3; C1 <= 28'd0; i <= 4'd5; end
57. else C1 <= C1 + 1'b1;
58.
59. 3: // L2H debounce
60. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
61. else C1 <= C1 + 1'b1;
62.
63. 4: // Key Tag Check 2
64. if( isH2L && C1 <= T100MS -1 ) begin isTag <= 2'd2; C1 <= 28'd0; i <= i + 1'b1; end
65. else if( C1 >= T100MS -1) begin isTag <= 2'd1; C1 <= 28'd0; i <= i + 1'b1; end
66. else C1 <= C1 + 1'b1;
67.
68. 5: // Key trigger press up
69. if( isTag == 2'd3 ) begin isLClick <= 1'b1; i <= i + 1'b1; end
70. else if( isTag == 2'd2 ) begin isDClick <= 1'b1; i <= i + 1'b1; end
71. else if( isTag == 2'd1 ) begin isSClick <= 1'b1; i <= i + 1'b1; end
72.
73. 6: // Key trigger pree down
74. begin { isLClick, isSClick, isDClick } <= 3'b000; i <= i + 1'b1; end
75.
76. 7: // L2H deounce check
77. if( isTag == 2'd1 ) begin isTag <= 2'd0; i <= i + 2'd2; end
78. else if( isTag == 2'd2 ) begin isTag <= 2'd0; i <= i + 1'b1; end
79. else if( isTag == 2'd3 ) begin isTag <= 2'd0; i <= i + 1'b1; end
80.
81. 8: // Wait L2H
82. if( isL2H ) begin i <= i + 1'b1; end
83.
84. 9: // L2H debounce
85. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= 4'd0; end
86. else C1 <= C1 + 1'b1;
87.
88. endcase
第45~88行是核心操作,具體的操作過程如下:
步驟0,等待第一次按下事件;
步驟1,過濾抖動;
步驟2,檢測是不是長點擊,如果是isTag為3然后出發步驟5,否則等待釋放事件;
步驟3,過濾抖動;
步驟4,檢測是不是雙擊,如果是isTag為2,否則S為1;
步驟5~6,根據isTag內容產生高脈沖;
步驟7,根據isTag內容檢測是否需要過濾抖動,isTag為1直接返回步驟0,其它需要;
步驟8,等待釋放事件;
步驟9,過濾抖動然后返回步驟0.
89.
90. /*************************/ // sub-demo
91.
92. reg [2:0]D1;
93.
94. always @ ( posedge CLOCK or negedge RESET )
95. if( !RESET )
96. D1 <= 2'd0;
97. else if( isLClick )
98. D12] <= ~D1[2];
99. else if( isDClick )
100. D1[1] <= ~D1[1];
101. else if( isSClick )
102. D1[0] <= ~D1[0];
103.
104. /***************************/
105.
106. assign LED = D1;
107.
108. endmodule
以上內容為演示用的周邊操作以及輸出驅動。它會根據各種“有效按鍵”翻轉D1的內容。第106行則是輸出驅動的聲明。編譯完后下載程序。
如果筆者點擊一下 <KEY1> 建,那么LED[0] 會點亮,如果筆者雙擊 <KEY1> 建,結果 LED[1] 會點亮,再如果筆者長按 <KEY1> 建 3秒不放,那么 LED[2] 則會點亮。總結說,一個按鍵資源可以執行3種功能,控制3位LED資源。
細節一:完整的個體模塊
圖5.5 完整的按鍵功能模塊。
如圖5.5所示,那是完整的按鍵功能模塊,它有一位輸入端KEY連接至按鍵資源,然后則有3位觸發信號,Trig[2]產生“單擊”的個高脈沖,Trig[1] 產生“雙擊”的個高脈沖,Trig[0]產生“長擊”的個高脈沖
key_funcmod.v
1. module key_funcmod
2. (
3. input CLOCK, RESET,
4. input KEY,
5. output [2: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. parameter T3S = 28'd150_000_000;
14.
15. /**********************************/ //sub
16.
17. reg F2,F1;
18.
19. always @ ( posedge CLOCK or negedge RESET )
20. if( !RESET )
21. { F2, F1 } <= 2'b11;
22. else
23. { F2, F1 } <= { F1, KEY };
24.
25. /**********************************/ //core
26.
27. wire isH2L = ( F2 == 1 && F1 == 0 );
28. wire isL2H = ( F2 == 0 && F1 == 1 );
29. reg [3:0]i;
30. reg isLClick, isDClick,isSClick;
31. reg [1:0]isTag;
32. reg [27:0]C1;
33.
34. always @ ( posedge CLOCK or negedge RESET )
35. if( !RESET )
36. begin
37. i <= 4'd0;
38. isLClick <= 1'b0;
39. isDClick <= 1'b0;
40. isSClick <= 1'b0;
41. isTag <= 2'd0;
42. C1 <= 28'd0;
43. end
44. else
45. case(i)
46.
47. 0: // Wait H2L
48. if( isH2L ) begin i <= i + 1'b1; end
49.
50. 1: // H2L debounce
51. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
52. else C1 <= C1 + 1'b1;
53.
54. 2: // Key Tag Check 1
55. if( isL2H ) begin C1 <= 28'd0; i <= i + 1'b1; end
56. else if( {F2,F1} == 2'b00 && C1 >= T3S -1 ) begin isTag <= 2'd3; C1 <= 28'd0; i <= 4'd5; end
57. else C1 <= C1 + 1'b1;
58.
59. 3: // L2H debounce
60. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
61. else C1 <= C1 + 1'b1;
62.
63. 4: // Key Tag Check 2
64. if( isH2L && C1 <= T100MS -1 ) begin isTag <= 2'd2; C1 <= 28'd0; i <= i + 1'b1; end
65. else if( C1 >= T100MS -1) begin isTag <= 2'd1; C1 <= 28'd0; i <= i + 1'b1; end
66. else C1 <= C1 + 1'b1;
67.
68. 5: // Key trigger press up
69. if( isTag == 2'd3 ) begin isLClick <= 1'b1; i <= i + 1'b1; end
70. else if( isTag == 2'd2 ) begin isDClick <= 1'b1; i <= i + 1'b1; end
71. else if( isTag == 2'd1 ) begin isSClick <= 1'b1; i <= i + 1'b1; end
72.
73. 6: // Key trigger pree down
74. begin { isLClick, isSClick, isDClick } <= 3'b000; i <= i + 1'b1; end
75.
76. 7: // L2H deounce check
77. if( isTag == 2'd1 ) begin isTag <= 2'd0; i <= i + 2'd2; end
78. else if( isTag == 2'd2 ) begin isTag <= 2'd0; i <= i + 1'b1; end
79. else if( isTag == 2'd3 ) begin isTag <= 2'd0; i <= i + 1'b1; end
80.
81. 8: // Wait L2H
82. if( isL2H ) begin i <= i + 1'b1; end
83.
84. 9: // L2H debounce
85. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= 4'd0; end
86. else C1 <= C1 + 1'b1;
87.
88. endcase
89.
90. /***************************/
91.
92. assign oTrig = { isSClick,isDClick,isLClick };
93.
94. endmodule





