【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验四:按键模块③ — 单击与双击

简介: 实验四:按键模块③ — 单击与双击 实验三我们创建了“点击”还有“长点击”等有效按键的多功能按键模块。在此,实验四同样也是创建多功能按键模块,不过却有不同的有效按键。实验四的按键功能模块有以下两项有效按键: l 单击(按下有效); l 双击(连续按下两下有效)。

实验四:按键模块③ — 单击与双击

实验三我们创建了“点击”还有“长点击”等有效按键的多功能按键模块。在此,实验四同样也是创建多功能按键模块,不过却有不同的有效按键。实验四的按键功能模块有以下两项有效按键:

l 单击(按下有效);

l 双击(连续按下两下有效)。

clip_image002

图4.1 单击有效按键,时序示意图。

实验四的“单击”基本上与实验三的“点击”一模一样,既按键被按下,经过消抖以后isSClick信号被拉高一个时钟,结果如图4.1所示,过程非常单调。反之,“双击”实现起来,会比较麻烦一些,因为我们还要考虑而外的细节,即人为连打极限。所谓人为连打极限就是两次按下按键之间的最短间隔。

clip_image004

如图4.2 双击有效按键,时序示意图(双击成功)。

根据笔者的理解,常人的连打极限是60ms左右,笔者是80ms左右,超人是20ms左右。

为了兼容常人还有笔者的连打极限,我们必须设置有效的连击时限,为此100ms是最好的选择。如图4.2所示,假设那是按键过程,笔者先是缓缓按下然后又缓缓释放按键完成第一次按键行为,结果有如往常般,按下事件发生,抖动发生,释放事件发生,抖动发生,但是 isDClick(Double Click)信号还有isSClick(Single Click)信号都没有产生高脉冲。

第一次按键完成以后就会引来第二次按键的黄金时间,亦即有效连击时限,在此笔者设为100ms。假设笔者在这100ms的黄金时间内按下按键,那么 isDClick信号会立即产生高脉冲。余下有如往常那样,抖动发生,释放事件发生,抖动发生 ... 对此,isSClick由始至终都没有状况发生。

clip_image006

如图4.3 双击有效按键,时序示意图(双击失败)。

假设笔者没在有限的100ms黄金时间内执行第二次按键按下的动作,那么“双击”就会失败,结果如图4.3所示。第一次按键过程与图4.2一样,反之第二次按键却不同了,如图4.3所示,第二次按键的按下事件是发生在100ms以后,为此isSClick产生高脉冲,然而isDClick信号却没有动静。

明白上述这些简单的原理以后,我们就可以开始建模了。

clip_image008

图4.4 实验四的建模图。

如图4.4所示,那是实验四的建模图,它有一位 KEY输入端,连接至按键资源。此外,它也有两位 LED输出端,分别连接两位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         = 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.         
14.         /**********************************/ //sub
15.         
16.         reg F2,F1;
17.             
18.         always @ ( posedge CLOCK or negedge RESET )
19.             if( !RESET ) 
20.                  { F2, F1 } <= 2'b11;
21.              else 
22.                  { F2, F1 } <= { F1, KEY };
23.                    
24.         /**********************************/ //core
25.        
26.         wire isH2L = ( F2 == 1 && F1 == 0 );
27.         wire isL2H = ( F2 == 0 && F1 == 1 );

以上内容为相关常量声明,周边操作以及即时声明。第7~12行是各种时间的常量声明,除了10毫秒声明消抖时间以外,第8~12行的时间常量是用来自定义双击的敏感度。第18~23行是检测电平状态的周边操作,第26~27行则是按下事件还有释放事件。

28.         reg [3:0]i;
29.         reg isDClick,isSClick;
30.         reg [1:0]isTag;
31.         reg [27:0]C1;
32.         
33.         always @ ( posedge CLOCK or negedge RESET )
34.             if( !RESET )
35.                   begin
36.                         i <= 4'd0;
37.                         isDClick <= 1'd0;
38.                         isSClick <= 1'b0;
39.                         isTag <= 2'd0;
40.                         C1 <= 28'd0;
41.                     end
42.              else

以上内容为相关寄存器声以及复位操作。i用作指向步骤,isDClick 还有 isSClick是用作标示“双击”还有“单击”。isTag是有效按键的标签,C1则用来计数。至于第33~41行则是核心操作的复位活动。

43.              case(i)
44.                         
45.                    0: // Wait H2L
46.                    if( isH2L ) begin i <= i + 1'b1; end
47.                         
48.                    1: // H2L debounce
49.                    if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
50.                    else C1 <= C1 + 1'b1;
51.                         
52.                    2: // Wait L2H
53.                    if( isL2H ) i <= i + 1'b1;
54.                         
55.                    3: // L2H debounce
56.                    if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
57.                    else C1 <= C1 + 1'b1;
58.                         
59.                    4: // Key Tag Check     
60.                    if( isH2L && C1 <= T100MS -1 ) begin isTag <= 2'd2; C1 <= 28'd0; i <= i + 1'b1; end
61.                    else if( C1 >= T100MS -1) begin isTag <= 2'd1; C1 <= 28'd0; i <= i + 1'b1; end
62.                    else C1 <= C1 + 1'b1;    
63.                         
64.                    5: // Key trigger press up
65.                    if( isTag == 2'd2 ) begin isDClick <= 1'b1; i <= i + 1'b1; end
66.                    else if( isTag == 2'd1 ) begin isSClick <= 1'b1; i <= i + 1'b1; end
67.                         
68.                    6: // Key trigger pree down
69.                    begin { isSClick , isDClick } <= 2'b00; i <= i + 1'b1; end
70.                         
71.                    7: // L2H deounce check
72.                    if( isTag == 2'd1 ) begin isTag <= 2'd0; i <= 4'd0; end
73.                    else if( isTag == 2'd2 ) begin isTag <= 2'd0; i <= i + 1'b1; end
74.                         
75.                    8: // Wait L2H
76.                    if( isL2H ) begin i <= i + 1'b1; end
77.                             
78.                    9: // L2H debounce
79.                    if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= 4'd0; end
80.                    else C1 <= C1 + 1'b1;
81.                         
82.            endcase

以上内容为核心操作。具体过程如下所示:

步骤0,等待第一次按下事件;

步骤1,过滤抖动;

步骤2,等待第一次释放事件;

步骤3,过滤抖动;

步骤4,如果100ms发生第二回按下事件,isTag设置为2,反之isTag设置为1;

步骤5~6,根据isTag的内容拉高又拉低 isDClick或者 isSClick;

步骤7,根据S内容,S为1便清除S然后返回步骤0。反之,isTag为2就清除isTag然后步骤继续前进;

步骤8,等待第二次释放事件;

步骤9:,过滤抖动然后返回步骤0。

83.                
84.         /*************************/ // sub-demo            
85.        
86.        reg [1:0]D1;
87.        
88.        always @ ( posedge CLOCK or negedge RESET )
89.            if( !RESET )
90.                 D1 <= 2'd0;
91.             else if( isDClick )
92.                 D1[1] <= ~D1[1];
93.             else if( isSClick )
94.                 D1[0] <= ~D1[0];
95.                  
96.        /***************************/
97.             
98.        assign LED = D1;
99.    
100.    endmodule

以上内容为演示用的周边操作以及输出驱动声明。它会根据 isDClick 还有 isSClick的高脉冲翻转 D1[1] 还有 D1[0] 的内容。第98行则是输出驱动声明。编译完后便下载程序。

当我们双击 <KEY2> 建 LED[1] 就会发亮,然后再双击 <KEY2> 建 LED[1] 则会消灭,发生双击的前提条件是 ... 第一次按下时间的100ms之内必须发生第二次按下时间才能成立。换之,如果我们单击 <KEY2>建 LED[0] 便会发亮,再单击 <KEY2> 建 LED[0]则会消灭。

细节一: 双击的敏感度

  parameter T100MS     = 28'd5_000_000; 
  parameter T200MS     = 28'd10_000_000; 
  parameter T300MS     = 28'd15_000_000; 
  parameter T400MS     = 28'd20_000_000; 
  parameter T500MS     = 28'd25_000_000; 

代码4.1

代码4.1是key_funcmod 的部分内容,亦即100ms~500ms的时间声明。所谓双击的敏感度就是按键第二次按下所有效的时限。事实上,有效时间100ms 已经足够应付一般“双击”要求,然而“双击”的敏感度除了人为连打极限这个因数以外,还有按键资源本身。开发板常见的按键都是经济型机械按键,手感较为迟钝,所以有效时间推荐在100ms~500ms范围之内。

如果读者所使用的按键资源是精致的家伙,想必手感一定很爽,例如鼠标之类的按键,100ms 有效时间可能会影响双击的敏感度。为此,有效时间必须设置在 40ms~100ms的范围,常见的有效时间是50ms。最后不管怎么样,手感还有敏感度这种东西非常暧昧,完全因人而异 ... 也有传言说那些骨灰级的游戏鼠标是可以自定义敏感度,事实究竟如何?对于笔者这种游戏冷漠者则是永远的迷。

细节二:完成的个体模块

clip_image010

图4.5 完整的按键功能模块。

如图4.5所示,那是完整的按键功能模块,输入端一边的KEY连接至按键资源,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         = 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.         
14.         /**********************************/ //sub
15.         
16.         reg F2,F1;
17.             
18.         always @ ( posedge CLOCK or negedge RESET )
19.             if( !RESET ) 
20.                  { F2, F1 } <= 2'b11;
21.              else 
22.                  { F2, F1 } <= { F1, KEY };
23.                    
24.         /**********************************/ //core
25.        
26.         wire isH2L = ( F2 == 1 && F1 == 0 );
27.         wire isL2H = ( F2 == 0 && F1 == 1 );
28.         reg [3:0]i;
29.         reg isDClick,isSClick;
30.         reg [1:0]isTag;
31.         reg [27:0]C1;
32.         
33.         always @ ( posedge CLOCK or negedge RESET )
34.             if( !RESET )
35.                   begin
36.                         i <= 4'd0;
37.                         isDClick <= 1'd0;
38.                         isSClick <= 1'b0;
39.                         isTag <= 2'd0;
40.                         C1 <= 28'd0;
41.                     end
42.              else
43.                  case(i)
44.                         
45.                         0: // Wait H2L
46.                         if( isH2L ) begin i <= i + 1'b1; end
47.                         
48.                         1: // H2L debounce
49.                         if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
50.                         else C1 <= C1 + 1'b1;
51.                         
52.                         2: // Wait L2H
53.                         if( isL2H ) i <= i + 1'b1;
54.                         
55.                         3: // L2H debounce
56.                         if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end
57.                         else C1 <= C1 + 1'b1;
58.                         
59.                         4: // Key Tag Check     
60.                         if( isH2L && C1 <= T100MS -1 ) begin isTag <= 2'd2; C1 <= 28'd0; i <= i + 1'b1; end
61.                         else if( C1 >= T100MS -1) begin isTag <= 2'd1; C1 <= 28'd0; i <= i + 1'b1; end
62.                         else C1 <= C1 + 1'b1;    
63.                         
64.                         5: // Key trigger press up
65.                         if( isTag == 2'd2 ) begin isDClick <= 1'b1; i <= i + 1'b1; end
66.                         else if( isTag == 2'd1 ) begin isSClick <= 1'b1; i <= i + 1'b1; end
67.                         
68.                         6: // Key trigger pree down
69.                         begin { isSClick , isDClick } <= 2'b00; i <= i + 1'b1; end
70.                         
71.                         7: // L2H deounce check
72.                         if( isTag == 2'd1 ) begin isTag <= 2'd0; i <= 4'd0; end
73.                         else if( isTag == 2'd2 ) begin isTag <= 2'd0; i <= i + 1'b1; end
74.                         
75.                         8: // Wait L2H
76.                         if( isL2H ) begin i <= i + 1'b1; end
77.                             
78.                         9: // L2H debounce
79.                         if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= 4'd0; end
80.                         else C1 <= C1 + 1'b1;
81.                         
82.                    endcase
83.    
84.        /***************************/
85.             
86.        assign oTrig = { isSClick,isDClick };
87.    
88.    endmodule
目录
相关文章
|
9月前
|
数据采集 移动开发 算法
【硬件测试】基于FPGA的QPSK调制+软解调系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR
本文基于FPGA实现QPSK调制与软解调系统,包含Testbench、高斯信道、误码率统计模块,并支持不同SNR设置。硬件版本新增ILA在线数据采集和VIO在线SNR设置功能,提供无水印完整代码及测试结果。通过VIO分别设置SNR为6dB和12dB,验证系统性能。配套操作视频便于用户快速上手。 理论部分详细解析QPSK调制原理及其软解调实现过程,涵盖信号采样、相位估计、判决与解调等关键步骤。软解调通过概率估计(如最大似然法)提高抗噪能力,核心公式为*d = d_hat / P(d_hat|r[n])*,需考虑噪声对信号点分布的影响。 附Verilog核心程序代码及注释,助力理解与开发。
301 5
|
10月前
|
数据采集 算法 数据安全/隐私保护
【硬件测试】基于FPGA的MSK调制解调系统系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR
本文基于FPGA实现MSK调制解调系统,采用Verilog开发,包含同步模块、高斯信道模拟、误码率统计等功能。相比仿真版本,新增ILA数据采集与VIO在线SNR设置模块。通过硬件测试验证,展示不同SNR(如10dB和16dB)下的性能表现。研究聚焦软件无线电领域,优化算法复杂度以适应硬件限制,利用MSK恒定包络、相位连续等特性提升频谱效率。核心代码实现信号生成、调制解调、滤波及误码统计,提供完整的硬件设计与分析方案。
356 19
|
10月前
|
数据采集 算法 数据安全/隐私保护
【硬件测试】基于FPGA的16QAM调制+软解调系统开发与硬件片内测试,包含信道模块,误码统计模块,可设置SNR
本文基于之前开发的16QAM调制与软解调系统,增加了硬件测试功能。该系统包含FPGA实现的16QAM调制、软解调、高斯信道、误码率统计模块,并新增了ILA在线数据采集和VIO在线SNR设置模块。通过硬件测试,验证了不同SNR条件下的系统性能。16QAM软解调通过比较接收信号采样值与16个调制点的距离,选择最近的调制点来恢复原始数据。核心Verilog代码实现了整个系统的功能,包括SNR设置、信号处理及误码率统计。硬件测试结果表明系统在不同SNR下表现良好,详细操作步骤可参考配套视频。
276 13
|
9月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于FPGA的SNN脉冲神经网络之LIF神经元verilog实现,包含testbench
本项目展示了 LIF(Leaky Integrate-and-Fire)神经元算法的实现与应用,含无水印运行效果预览。基于 Vivado2019.2 开发,完整代码配有中文注释及操作视频。LIF 模型模拟生物神经元特性,通过积分输入信号并判断膜电位是否达阈值产生脉冲,相较于 Hodgkin-Huxley 模型更简化,适合大规模神经网络模拟。核心程序片段示例,助您快速上手。
|
算法 数据安全/隐私保护 异构计算
基于FPGA的变步长LMS自适应滤波器verilog实现,包括testbench
### 自适应滤波器仿真与实现简介 本项目基于Vivado2022a实现了变步长LMS自适应滤波器的FPGA设计。通过动态调整步长因子,该滤波器在收敛速度和稳态误差之间取得良好平衡,适用于信道均衡、噪声消除等信号处理应用。Verilog代码展示了关键模块如延迟单元和LMS更新逻辑。仿真结果验证了算法的有效性,具体操作可参考配套视频。
424 74
|
10月前
|
算法 数据安全/隐私保护 异构计算
基于LSB最低有效位的音频水印嵌入提取算法FPGA实现,包含testbench和MATLAB对比
本项目展示了一种基于FPGA的音频水印算法,采用LSB(最低有效位)技术实现版权保护与数据追踪功能。使用Vivado2019.2和Matlab2022a开发,完整代码含中文注释及操作视频。算法通过修改音频采样点的最低有效位嵌入水印,人耳难以察觉变化。然而,面对滤波或压缩等攻击时,水印提取可能受影响。该项目运行效果无水印干扰,适合实时应用场景,核心逻辑简单高效,时间复杂度低。
|
10月前
|
算法 数据安全/隐私保护 异构计算
基于FPGA的2ASK+帧同步系统verilog开发,包含testbench,高斯信道,误码统计,可设置SNR
本内容展示了基于Vivado2019.2的算法仿真效果,包括设置不同信噪比(SNR=8db和20db)下的结果及整体波形。同时,详细介绍了2ASK调制解调技术的原理与实现,即通过改变载波振幅传输二进制信号,并提供数学公式支持。此外,还涉及帧同步理论,用于确定数据帧起始位置。最后,给出了Verilog核心程序代码,实现了2ASK解调与帧同步功能,结合DDS模块生成载波信号,完成信号处理流程。
193 0
|
算法 数据安全/隐私保护 异构计算
基于FPGA的16QAM调制+软解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本项目基于FPGA实现了16QAM基带通信系统,包括调制、信道仿真、解调及误码率统计模块。通过Vivado2019.2仿真,设置不同SNR(如8dB、12dB),验证了软解调相较于传统16QAM系统的优越性,误码率显著降低。系统采用Verilog语言编写,详细介绍了16QAM软解调的原理及实现步骤,适用于高性能数据传输场景。
565 69
|
10月前
|
编解码 算法 数据安全/隐私保护
基于FPGA的信号DM编解码实现,包含testbench和matlab对比仿真
本项目展示了DM编解码算法的实现与测试结果。FPGA测试结果显示为T1,Matlab仿真结果为T2。使用软件版本为Matlab 2022a和Vivado 2019.2。核心程序包含详细中文注释和操作视频。DM编解码通过比较信号样本差值进行编码,适用于音频等低频信号处理。硬件结构包括编码器(采样器、减法器、比较器)和解码器(解码器、积分器)。
|
移动开发 算法 数据安全/隐私保护
基于FPGA的QPSK调制+软解调系统,包含testbench,高斯信道模块,误码率统计模块,可以设置不同SNR
本文介绍了基于FPGA的QPSK调制解调系统,通过Vivado 2019.2进行仿真,展示了在不同信噪比(SNR=1dB, 5dB, 10dB)下的仿真效果。与普通QPSK系统相比,该系统的软解调技术显著降低了误码率。文章还详细阐述了QPSK调制的基本原理、信号采样、判决、解调及软解调的实现过程,并提供了Verilog核心程序代码。
464 26

热门文章

最新文章