【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验三:按键模块② — 点击与长点击
实验三:按键模块② — 点击与长点击
实验二我们学过按键功能模块的基础内容,其中我们知道按键功能模块有如下操作:
l 电平变化检测;
l 过滤抖动;
l 产生有效按键。
实验三我们也会z执行同样的事情,不过却是产生不一样的有效按键:
l 按下有效(点击);
l 长按下有效(长点击)。

图3.1 按下有效,时序示意图。

图3.2 长按下有效,时序示意图。
如图3.1所示,按下有效既是“点击”,当按键被按下并且消抖完毕以后,isSClick信号就有被拉高一个时钟(Short Click)。换之,长按下有效也是俗称为的“长点击”,如图3.2所示,当按键被按下并且消抖完毕以后,如果按键3秒之内都没有被释放,那么isLClick信号就会拉高一个时钟(Long Click)。

图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,那么消抖过程照常。
细节二:完整的按键功能模块

图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
【黑金原创教程】【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 】实验五:按键模块④ — 点击,长点击,双击
实验五:按键模块④ - 点击,长点击,双击 实验二至实验四,我们一共完成如下有效按键: l 点击(按下有效) l 点击(释放有效) l 长击(长按下有效) l 双击(连续按下有效) 然而,不管哪个实验 ...
- 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二:按键模块① - 消抖
实验二:按键模块① - 消抖 按键消抖实验可谓是经典中的经典,按键消抖实验虽曾在<建模篇>出现过,而且还惹来一堆麻烦.事实上,笔者这是在刁难各位同学,好让对方的惯性思维短路一下,但是惨遭口 ...
随机推荐
- Scala:Functions and Closures
object Functions { def main(args: Array[String]) { // 本地函数 def localFun(msg: String) = println(msg) ...
- iOS:NSFileHandle和NSFileManger的使用
一.介绍 利用NSFilehandle类提供的方法,允许更有效地使用文件. 一般而言,处理文件时都要经历以下三个步骤: 1.打开文件,并获取一个NSFileHandle对象,以便在后面的I/O操作中引 ...
- Andorid源码 4.4 TAG
Fetching project platform/frameworks/opt/timezonepickerremote: Counting objects: 11169, doneremote: ...
- ios实例开发精品源码文章推荐(8.28)
iOS源码:游戏引擎-推箱子游戏 <ignore_js_op> http://www.apkbus.com/android-106392-1-11.html iOS源码:进度条-Color ...
- Python多进程库multiprocessing中进程池Pool类的使用[转]
from:http://blog.csdn.net/jinping_shi/article/details/52433867 Python多进程库multiprocessing中进程池Pool类的使用 ...
- SQL SERVER 行列转换(动态)
行转列测试数据: --测试数据 if not object_id(N'Tempdb..#T') is null drop table #T Go Create table #T([Name] nvar ...
- FFMPEG中关于ts流的时长估计的实现(转)
最近在做H.265 编码,原本只是做编码器的实现,但客户项目涉及到ts的封装,搞得我不得不配合了解点ts方面的东西.下面技术文档不错,转一下. ts流中的时间估计 我们知道ts流中是没有时间信息的,我 ...
- unity, ComputeScreenPos 作用
这帖子讲得很清楚:https://forum.unity3d.com/threads/what-does-the-function-computescreenpos-in-unitycg-cginc- ...
- 相关系数(CORRELATION COEFFICIENTS)会骗人?
CORRELATION COEFFICIENTS We've discussed how to summarize a single variable. The next question is ho ...
- 在Vue项目中使用vw实现移动端适配
有关于移动端的适配布局一直以来都是众说纷纭,对应的解决方案也是有很多种.在<使用Flexible实现手淘H5页面的终端适配>提出了Flexible的布局方案,随着viewport单位越来越 ...