實驗三:按鍵模塊② — 點擊與長點擊
實驗二我們學過按鍵功能模塊的基礎內容,其中我們知道按鍵功能模塊有如下操作:
l 電平變化檢測;
l 過濾抖動;
l 產生有效按鍵。
實驗三我們也會z執行同樣的事情,不過卻是產生不一樣的有效按鍵:
l 按下有效(點擊);
l 長按下有效(長點擊)。
圖3.1 按下有效,時序示意圖。
圖3.2 長按下有效,時序示意圖。
如圖3.1所示,按下有效既是“點擊”,當按鍵被按下並且消抖完畢以后,isSClick信號就有被拉高一個時鍾(Short Click)。換之,長按下有效也是俗稱為的“長點擊”,如圖3.2所示,當按鍵被按下並且消抖完畢以后,如果按鍵3秒之內都沒有被釋放,那么isLClick信號就會拉高一個時鍾(Long Click)。
圖3.3 實驗三的建模圖。
如圖3.3所示,那是實驗三的建模圖,同樣按鍵功能模塊有一位KEY輸入,並且連接至按鍵資源,然后它有兩位LED輸出,並且連接至2位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 = 26'd500_000; // Deboucing time
8. parameter T3S = 28'd150_000_000; // Long press time
9.
10. /*****************************************/ //sub
11.
12. reg F2,F1;
13.
14. always @ ( posedge CLOCK or negedge RESET )
15. if( !RESET )
16. { F2, F1 } <= 2'b11;
17. else
18. { F2, F1 } <= { F1, KEY };
19.
20. /*****************************************/ //core
21.
22. wire isH2L = ( F2 == 1 && F1 == 0 );
23. wire isL2H = ( F2 == 0 && F1 == 1 );
以上內容為相關的常量聲明,周邊操作以及即時聲明。第12~18行是電平狀態檢測的周邊操作。第22~23行是按下事件與釋放事件的即時聲明。
24. reg [3:0]i;
25. reg isLClick,isSClick;
26. reg [1:0]isTag;
27. reg [27:0]C1;
28.
29. always @ ( posedge CLOCK or negedge RESET )
30. if( !RESET )
31. begin
32. i <= 4'd0;
33. isLClick <= 1'd0;
34. isSClick <= 1'b0;
35. isTag <= 2'd0;
36. C1 <= 28'd0;
37. end
38. else
以上內容為相關的寄存器聲明以及復位操作。i用作指向步驟,isLClick與isSClick同是標示寄存器,分別是長按下有效與按下有效。isTag用來判定那種有效按鍵,C1用來計數。
39. case(i)
40.
41. 0: // Wait H2L
42. if( isH2L ) i <= i + 1'b1;
43.
44. 1: // H2L debouce
45. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
46. else C1 <= C1 + 1'b1;
47.
48. 2: // Key S Check
49. if( isL2H ) begin isTag <= 2'd1; C1 <= 28'd0; i <= i + 1'b1; end
50. else if( {F2,F1} == 2'b00 && C1 >= T3S -1 ) begin isTag <= 2'd2; C1 <= 28'd0; i <= i + 1'd1; end
51. else C1 <= C1 + 1'b1;
52.
53. 3: // S Trigger (pree up)
54. if( isTag == 2'd1 ) begin isSClick <= 1'b1; i <= i + 1'b1; end
55. else if( isTag == 2'd2 ) begin isLClick <= 1'b1; i <= i + 1'b1; end
56.
57. 4: // S Trigger (pree down)
58. begin { isLClick,isSClick } <= 2'b00; i <= i + 1'b1; end
59.
60. 5: // L2H deboce check
61. if( isTag == 2'd1 ) begin isTag <= 2'd0; i <= i + 2'd2; end
62. else if( isTag == 2'd2 ) begin isTag <= 2'd0; i <= i + 1'b1; end
63.
64. 6: // Wait L2H
65. if( isL2H )i <= i + 1'b1;
66.
67. 7: // L2H debonce
68. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= 4'd0; end
69. else C1 <= C1 + 1'b1;
70.
71. endcase
以上內容為核心操作,至於核心的操作過程如下:
步驟0,等待按下事件;
步驟1,過濾又高變低所產生的抖動;
步驟2,檢測那種有效按鍵,如果3秒以內發生釋放事件就為isTag賦值1;反之,如果持續3秒低電平則為isTag賦值2;
步驟3~4,根據 Mode 的內容產生不同有效按鍵的高脈沖,isTag為1是“點擊”isSClick,isTag為2則是“長點擊”isLClick。
步驟5,用來檢測釋放事件,如果之前發生“點擊”就直接跳向步驟7。反之,如果之前發生“長點擊”就進入步驟6。
步驟6,等待釋放事件(長點擊有效)。
步驟7, 過濾又低變高所產生的抖動,然后返回步驟0。
72.
73. /*************************/ // sub demo
74.
75. reg [1:0]D1;
76.
77. always @ ( posedge CLOCK or negedge RESET )
78. if( !RESET )
79. D1 <= 2'b00;
80. else if( isLClick )
81. D1[1] <= ~D1[1];
82. else if( isSClick )
83. D1[0] <= ~D1[0];
84.
85. /***************************/
86.
87. assign LED = D1;
88.
89. endmodule
以上內容為演示用的周邊操作,它根據那種有效按鍵就翻轉那位D1寄存器。第87行則是輸出驅動聲明。編譯完成便下載程序。
我們會發現,第一次按下 <KEY2> 3秒不放會點亮 LED[1],換之按下 <KEY2> 不到3秒便釋放則會點亮 LED[0]。第二次按下 <KEY2> 3秒不放會消滅 LED[0],按下 <KEY2> 不到3秒便釋放會消滅 LED[0]。如此一來,實驗三已經成
細節一: 精密控時
2:
if( isL2H ) begin S <= 2'd1; C1 <= 28'd0; i <= i + 1'b1; end
else if( {F2,F1} == 2'b00 && C1 >= T3S -1 ) begin S <= 2'd2; C1 <= 28'd0; i <= i + 1'd1; end
else C1 <= C1 + 1'b1;
3:
if( S == 2'd1 ) begin isSClick <= 1'b1; i <= i + 1'b1; end
else if( S == 2'd2 ) begin isLClick <= 1'b1; i <= i + 1'b1; end
4:
begin { isLClick,isSClick } <= 2'b00; i <= i + 1'b1; end
5:
if( S == 2'd1 ) begin S <= 2'd0; i <= i + 2'd2; end
else if( S== 2'd2 ) begin S <= 2'd0; i <= i + 1'b1; end
6:
if( isL2H )i <= i + 1'b1;
7:
if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= 4'd0; end
else C1 <= C1 + 1'b1;
代碼3.1
代碼3.1是 key_funcmod 的部分代碼,分別是步驟2~7。如果筆者是精密控時狂人,事實上代碼3.1還可以進一步細化,然而還有什么可以細化的地方呢?如代碼3.1所示,距離步驟7(消抖)之前,共有步驟2~6等5個時鍾所消耗。如果也考慮消抖時間之內,然而步驟7 修改為if( C1 == T10MS -1 -5 ) 是行不通,因為兩種有效按鍵都有不通的情況。
如果 isTag 為1,步驟5會直接跳向步驟7,步驟7的消抖只要加入步驟2~5所消耗的4個時鍾。如果isTag為2,那么步驟5會前進步驟6,然后乖乖等待釋放事件,期間沒消耗而外的時鍾。為此,步驟7可以這樣修改,結果如代碼3.2所示:
5:
if( S == 2'd1 ) begin i <= i + 2'd2; end
else if( S == 2'd2 ) begin i <= i + 1'b1; end
6:
if( isL2H )i <= i + 1'b1;
7:
if( Mode == 2'd1 && C1 == T10MS -1 -4) begin Mode <= 2'd0; C1 <= 28'd0; i <= 4'd0; end
else if( Mode == 2'd2 && C1 == T10MS -1 ) begin Mode <= 2'd0; C1 <= 28'd0; i <= 4'd0; end
else C1 <= C1 + 1'b1;
代碼3.2
如代碼3.2所示,步驟5被拿掉 isTag <= 2'd0 操作,然后步驟7稍微修改一下消抖過程。如果 isTag 為1,那么消抖多考慮 4 個而外的時鍾消耗。反之,如果 isTag 為 2,那么消抖過程照常。
細節二:完整的按鍵功能模塊
圖3.4 完整的按鍵功能模塊。
如圖3.4所示,那是完整的按鍵功能模塊,它有一位鏈接至按鍵資源的KEYn信號,它也有一組兩位的溝通信號Trig。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 = 26'd500_000; // Deboucing time
8. parameter T3S = 28'd150_000_000; // Long press time
9.
10. /*****************************************/ //sub
11.
12. reg F2,F1;
13.
14. always @ ( posedge CLOCK or negedge RESET )
15. if( !RESET )
16. { F2, F1 } <= 2'b11;
17. else
18. { F2, F1 } <= { F1, KEY };
19.
20. /*****************************************/ //core
21.
22. wire isH2L = ( F2 == 1 && F1 == 0 );
23. wire isL2H = ( F2 == 0 && F1 == 1 );
24. reg [3:0]i;
25. reg isLClick,isSClick;
26. reg [1:0]isTag;
27. reg [27:0]C1;
28.
29. always @ ( posedge CLOCK or negedge RESET )
30. if( !RESET )
31. begin
32. i <= 4'd0;
33. isLClick <= 1'd0;
34. isSClick <= 1'b0;
35. isTag <= 2'd0;
36. C1 <= 28'd0;
37. end
38. else
39. case(i)
40.
41. 0: // Wait H2L
42. if( isH2L ) i <= i + 1'b1;
43.
44. 1: // H2L debouce
45. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
46. else C1 <= C1 + 1'b1;
47.
48. 2: // Key Tag Check
49. if( isL2H ) begin isTag <= 2'd1; C1 <= 28'd0; i <= i + 1'b1; end
50. else if( {F2,F1} == 2'b00 && C1 >= T3S -1 ) begin isTag <= 2'd2; C1 <= 28'd0; i <= i + 1'd1; end
51. else C1 <= C1 + 1'b1;
52.
53. 3: // Tag Trigger (pree up)
54. if( isTag == 2'd1 ) begin isSClick <= 1'b1; i <= i + 1'b1; end
55. else if( isTag == 2'd2 ) begin isLClick <= 1'b1; i <= i + 1'b1; end
56.
57. 4: // Tag Trigger (pree down)
58. begin { isLClick,isSClick } <= 2'b00; i <= i + 1'b1; end
59.
60. 5: // L2H deboce check
61. if( isTag == 2'd1 ) begin S <= 2'd0; i <= i + 2'd2; end
62. else if( isTag == 2'd2 ) begin S <= 2'd0; i <= i + 1'b1; end
63.
64. 6: // Wait L2H
65. if( isL2H )i <= i + 1'b1;
66.
67. 7: // L2H debonce
68. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= 4'd0; end
69. else C1 <= C1 + 1'b1;
70.
71. endcase
72.
73. /*************************/
74.
75. assign oTrig = { isSClick,isLClick };
76.
77. endmodule