【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验四:按键模块③ — 单击与双击
实验四:按键模块③ — 单击与双击
实验三我们创建了“点击”还有“长点击”等有效按键的多功能按键模块。在此,实验四同样也是创建多功能按键模块,不过却有不同的有效按键。实验四的按键功能模块有以下两项有效按键:
l 单击(按下有效);
l 双击(连续按下两下有效)。
图4.1 单击有效按键,时序示意图。
实验四的“单击”基本上与实验三的“点击”一模一样,既按键被按下,经过消抖以后isSClick信号被拉高一个时钟,结果如图4.1所示,过程非常单调。反之,“双击”实现起来,会比较麻烦一些,因为我们还要考虑而外的细节,即人为连打极限。所谓人为连打极限就是两次按下按键之间的最短间隔。
如图4.2 双击有效按键,时序示意图(双击成功)。
根据笔者的理解,常人的连打极限是60ms左右,笔者是80ms左右,超人是20ms左右。
为了兼容常人还有笔者的连打极限,我们必须设置有效的连击时限,为此100ms是最好的选择。如图4.2所示,假设那是按键过程,笔者先是缓缓按下然后又缓缓释放按键完成第一次按键行为,结果有如往常般,按下事件发生,抖动发生,释放事件发生,抖动发生,但是 isDClick(Double Click)信号还有isSClick(Single Click)信号都没有产生高脉冲。
第一次按键完成以后就会引来第二次按键的黄金时间,亦即有效连击时限,在此笔者设为100ms。假设笔者在这100ms的黄金时间内按下按键,那么 isDClick信号会立即产生高脉冲。余下有如往常那样,抖动发生,释放事件发生,抖动发生 ... 对此,isSClick由始至终都没有状况发生。
如图4.3 双击有效按键,时序示意图(双击失败)。
假设笔者没在有限的100ms黄金时间内执行第二次按键按下的动作,那么“双击”就会失败,结果如图4.3所示。第一次按键过程与图4.2一样,反之第二次按键却不同了,如图4.3所示,第二次按键的按下事件是发生在100ms以后,为此isSClick产生高脉冲,然而isDClick信号却没有动静。
明白上述这些简单的原理以后,我们就可以开始建模了。
图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。最后不管怎么样,手感还有敏感度这种东西非常暧昧,完全因人而异 ... 也有传言说那些骨灰级的游戏鼠标是可以自定义敏感度,事实究竟如何?对于笔者这种游戏冷漠者则是永远的迷。
细节二:完成的个体模块
图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
【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验四:按键模块③ — 单击与双击的更多相关文章
- [黑金原创教程] FPGA那些事儿《设计篇 III》- 图像处理前夕·再续
简介 一本为入门图像处理的入门书,另外还教你徒手搭建平台(片上系统),内容请看目录. 注意 为了达到最好的实验的结果,请准备以下硬件. AX301开发板, OV7670摄像模块, VGA接口显示器, ...
- [黑金原创教程] FPGA那些事儿《设计篇 II》- 图像处理前夕·续
简介 一本为入门图像处理的入门书,另外还教你徒手搭建平台(片上系统),内容请看目录. 注意 为了达到最好的实验的结果,请准备以下硬件. AX301开发板, OV7670摄像模块, VGA接口显示器, ...
- [黑金原创教程] FPGA那些事儿《设计篇 I》- 图像处理前夕
简介 一本为入门图像处理的入门书,另外还教你徒手搭建平台(片上系统),内容请看目录. 注意 为了达到最好的实验的结果,请准备以下硬件. AX301开发板, OV7670摄像模块, VGA接口显示器, ...
- [黑金原创教程] FPGA那些事儿《数学篇》- CORDIC 算法
简介 一本为完善<设计篇>的书,教你CORDIC算法以及定点数等,内容请看目录. 贴士 这本教程难度略高,请先用<时序篇>垫底. 目录 Experiment 01:认识CORD ...
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】连载导读
前言: 无数昼夜的来回轮替以后,这本<驱动篇I>终于编辑完毕了,笔者真的感动到连鼻涕也流下来.所谓驱动就是认识硬件,还有前期建模.虽然<驱动篇I>的硬件都是我们熟悉的老友记,例 ...
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】原创教程连载导读【连载完成,共二十九章】
前言: 无数昼夜的来回轮替以后,这本<驱动篇I>终于编辑完毕了,笔者真的感动到连鼻涕也流下来.所谓驱动就是认识硬件,还有前期建模.虽然<驱动篇I>的硬件都是我们熟悉的老友记,例 ...
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二:按键模块① - 消抖
实验二:按键模块① - 消抖 按键消抖实验可谓是经典中的经典,按键消抖实验虽曾在<建模篇>出现过,而且还惹来一堆麻烦.事实上,笔者这是在刁难各位同学,好让对方的惯性思维短路一下,但是惨遭口 ...
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十:PS/2模块④ — 普通鼠标
实验十:PS/2模块④ - 普通鼠标 学习PS/2键盘以后,接下来就要学习 PS/2 鼠标.PS/2鼠标相较PS/2键盘,驱动难度稍微高了一点点,因为FPGA(从机)不仅仅是从PS/2鼠标哪里读取数据 ...
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验三:按键模块② — 点击与长点击
实验三:按键模块② - 点击与长点击 实验二我们学过按键功能模块的基础内容,其中我们知道按键功能模块有如下操作: l 电平变化检测: l 过滤抖动: l 产生有效按键. 实验三我们也会z执行同样的事情 ...
随机推荐
- Meclipse alt+/ 没有提示
- Windows下Kettle定时任务执行并发送错误信息邮件
Windows下Kettle定时任务执行并发送错误信息邮件 1.首先安装JDK 2.配置JDK环境 3.下载并解压PDI(kettle) 目前我用的是版本V7的,可以直接百度搜索下载社区版,企业版收费 ...
- tmux 基本用法
C代表Ctrl,按下Ctrl+b之后tmux不会有任何反应,只要继续再按下一个键就会有反应. tmux #开启tmux tmux ls #显示已有tmux列表(C-b s) tmux attach-s ...
- javascript中的数据结构
Javascript中的关键字 abstract continue finally instanceof private this boolean ...
- 查看占用IO的进程
查看占用IO的进程 http://www.xaprb.com/blog/2009/08/23/how-to-find-per-process-io-statistics-on-linux/
- openssl命令行工具简介 - 指令x509
原文链接: http://blog.csdn.net/allwtg/article/details/4982507 openssl命令行工具简介 - 指令x509 用法: open ...
- Lambert漫反射的BRDF
Lambert漫反射brdf=Albedo/pi. 推导: 如图,设Lambert面元获得入射照度Ei,各方向均匀释放亮度,每个方向释放的亮度都是Lo. 又设此表面反射率为Albedo,根据反射率定义 ...
- Android webview clearHistory 不符合逾期的解决办法
目前在业务开发中有这么一个需求,切换不同的 Fragment, 切换回 WebView 的Fragment时候,要求是打开的初始页面,然后我在 onHiddenChanged() 方法中加载默认地址, ...
- hdoj:2033
#include <iostream> #include <string> using namespace std; int main() { int n; int h, m, ...
- 5 款最新的 jQuery 图片裁剪插件
这篇文章主要介绍最新的 5 款 jQuery 图片裁剪插件,可以帮助你轻松的实现你网站需要的图像裁剪功能. Cropit Cropit 是一个 jQuery 插件,支持图像裁剪和缩放功能.Cropit ...