【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗三:按鍵模塊② — 點擊與長點擊


實驗三:按鍵模塊② — 點擊與長點擊

實驗二我們學過按鍵功能模塊的基礎內容,其中我們知道按鍵功能模塊有如下操作:

l 電平變化檢測;

l 過濾抖動;

l 產生有效按鍵。

實驗三我們也會z執行同樣的事情,不過卻是產生不一樣的有效按鍵:

l 按下有效(點擊);

l 長按下有效(長點擊)。

clip_image002

圖3.1 按下有效,時序示意圖。

clip_image004

圖3.2 長按下有效,時序示意圖。

如圖3.1所示,按下有效既是“點擊”,當按鍵被按下並且消抖完畢以后,isSClick信號就有被拉高一個時鍾(Short Click)。換之,長按下有效也是俗稱為的“長點擊”,如圖3.2所示,當按鍵被按下並且消抖完畢以后,如果按鍵3秒之內都沒有被釋放,那么isLClick信號就會拉高一個時鍾(Long Click)。

clip_image006

圖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,那么消抖過程照常。

細節二:完整的按鍵功能模塊

clip_image008

圖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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM