• 【黑金原创教程】【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
  • 相关阅读:
    材料用词积累
    SqlServer 数据库/数据表 拆分(分布式)【转】
    SqlServer 数据库读写分离【转】
    (整理)在REHL6.5上部署ASP.NET MVC
    (整理)MySQL_REHL6.5 安装MySQL5.5
    (转)查看SQLServer最耗资源时间的SQL语句
    (转)SQLServer查询数据库各种历史记录
    (转)SqlServer2008 数据库同步:发布、订阅
    (整理)SQL Server 2008 CDC 功能使用
    (整理)EF分页的实现
  • 原文地址:https://www.cnblogs.com/alinx/p/3777233.html
Copyright © 2020-2023  润新知