• 【黑金原创教程】【FPGA那些事儿驱动篇I 】实验五:按键模块④ — 点击,长点击,双击


    实验五:按键模块④ — 点击,长点击,双击

    实验二至实验四,我们一共完成如下有效按键:

    l 点击(按下有效)

    l 点击(释放有效)

    l 长击(长按下有效)

    l 双击(连续按下有效)

    然而,不管哪个实验都是只有两项“功能”的按键模块而已,如今我们要创建三项“功能”的按键模块,亦即点击(按下有效),长击,还有双击。实验继续之前,让我们先来复习一下各种有效按键。

    clip_image002

    图5.1 点击(按下有效)。

    如图5.1所示,所谓点击(按下有效)就是按键按下以后,isSClick信号(Single Click)

    产生一个高脉冲。

    clip_image004

    图5.2 长点击。

    如图5.2所示,所谓长点击就是按键按下以后,长达3秒不放,isLClick信号(Long Click)

    就会产生一个高脉冲。

    clip_image006

    图5.3 双点击。

    如图5.3所示,所谓双点击就是距离第一次“点击(按下有效)”,如果在有效的连击时限内容完成第二次“点击(按下有效)”,那么isDClick信号(Double Click)就会产生一个高脉冲。

    话语上,实验五虽然要整合3项按键功能,然而实验五相较实验二至实验四却有一定程度的难度。那是因为仅有两项功能的按键模块,仅需判断一次性的有效按键而已,反之三项功能的按键模块却要分开二次判断,而且判断也要有序。根据实验五,核心操作会优先判断按键是否“长点击”,然后再来判断“点击”还是“双点击”。期间绝对不能搞错判断的次序。

    具体内容,我们还是直接来看代码吧:

    clip_image008

    图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资源。

    细节一:完整的个体模块

    clip_image010

    图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
  • 相关阅读:
    Python 使用 environs 库来更好地定义环境变量
    五分钟带你了解map、reduce和filter
    部署React前端和Django后端的3种方法
    marshmallow库更优雅的数据转换
    利用 attrs 和 cattrs 两个库实现 Python 面向对象编程
    parse库,更友好的格式化数据提取方案
    python之prettytable模块格式化打印
    使用类型注解让 Python 代码更易读
    jksj算法训练营-第三课01 数组、链表、跳表
    linux安装java步骤
  • 原文地址:https://www.cnblogs.com/alinx/p/3805468.html
Copyright © 2020-2023  润新知