【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验五:按键模块④ — 点击,长点击,双击
实验五:按键模块④ — 点击,长点击,双击
实验二至实验四,我们一共完成如下有效按键:
l 点击(按下有效)
l 点击(释放有效)
l 长击(长按下有效)
l 双击(连续按下有效)
然而,不管哪个实验都是只有两项“功能”的按键模块而已,如今我们要创建三项“功能”的按键模块,亦即点击(按下有效),长击,还有双击。实验继续之前,让我们先来复习一下各种有效按键。

图5.1 点击(按下有效)。
如图5.1所示,所谓点击(按下有效)就是按键按下以后,isSClick信号(Single Click)
产生一个高脉冲。

图5.2 长点击。
如图5.2所示,所谓长点击就是按键按下以后,长达3秒不放,isLClick信号(Long Click)
就会产生一个高脉冲。

图5.3 双点击。
如图5.3所示,所谓双点击就是距离第一次“点击(按下有效)”,如果在有效的连击时限内容完成第二次“点击(按下有效)”,那么isDClick信号(Double Click)就会产生一个高脉冲。
话语上,实验五虽然要整合3项按键功能,然而实验五相较实验二至实验四却有一定程度的难度。那是因为仅有两项功能的按键模块,仅需判断一次性的有效按键而已,反之三项功能的按键模块却要分开二次判断,而且判断也要有序。根据实验五,核心操作会优先判断按键是否“长点击”,然后再来判断“点击”还是“双点击”。期间绝对不能搞错判断的次序。
具体内容,我们还是直接来看代码吧:

图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资源。
细节一:完整的个体模块

图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
【黑金原创教程】【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执行同样的事情 ...
随机推荐
- selenium之关于 chromedriver的安装和使用
转自:https://blog.csdn.net/d77808675/article/details/79016271 最近在学习爬虫,用到了selenium 环境:Windows,python3 但 ...
- Swift学习笔记(十四)——字符,常量字符串与变量字符串
在学习Java过程中,字符串碰到过String和StringBuffer,当中前者是不可变的,不能对字符串进行改动:后者是可变的,能够不断改动. 来到Swift中,对字符串的定义变的更加简单. (1) ...
- tar 的–exclude参数,实现不包括某些文件(转)
最近有人问我tar压缩能不能不包含不想要的文件或者文件夹 压缩包很利于传送,备份等等.但比如大量的日志文件一般是不需要备份,或者需要分开备份. tar参数中的–exclude,这个是我在看鸟哥私房菜时 ...
- 在 OC 中调用 Swift 代码
1.在 Objective-C 项目中使用 Swift 代码 1)在 OC 项目中创建 .Swift 文件,文件中的格式为其本有的格式. 2)Xcode 提示是否创建 Objective-C brid ...
- Android开发(二十一)——自动更新
参考: [1] Android应用自动更新功能的代码实现.http://www.cnblogs.com/coolszy/archive/2012/04/27/2474279.html
- iOS之加载Gif图片
Gif图片是非常常见的图片格式,尤其是在聊天的过程中,Gif表情使用地很频繁.但是iOS竟然没有现成的支持加载和播放Gif的类. 简单地汇总了一下,大概有以下几种方法: 一.加载本地Gif文件 1.使 ...
- left join、right join、inner join的区别
left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录inner join(等值连接) 只 ...
- 【九天教您南方cass 9.1】 10 DTM土方计算的四种方法
同学们大家好,欢迎收看由老王测量上班记出品的cass9.1视频课程 我是本节课主讲老师九天. 我们讲课的教程附件也是共享的,请注意索取测量空间中. [点击索取cass教程]5元立得 (给客服说暗号:“ ...
- bootstrap 3.0 LESS源代码浅析(一)
我一直以为Bootstrap的LESS源代码精髓在mixins.less,所以这个系列主要也是讲解mixins.less的. 什么是mixins? 混入,或者翻译成混合.官网的说法是:我们可以定义一些 ...
- JVM 内部原理(五)— 基本概念之 Java 虚拟机官方规范文档,第 7 版
JVM 内部原理(五)- 基本概念之 Java 虚拟机官方规范文档,第 7 版 介绍 版本:Java SE 7 每位使用 Java 的程序员都知道 Java 字节码在 Java 运行时(JRE - J ...