一天一个设计实例-轻触开关的应用设计
轻触开关的应用设计
本节介绍轻触开关的有关设计,主要介绍按键防抖动和矩阵键盘的应用设计。
1.1.1轻触开关的功能描述
(1) 轻触开关的工作原理。轻触开关在操作时通过触动按键来改变当前的逻辑状态(高、低电平之间的变换),而达到开/关的目的。每按一次按键,就向 FPGA 控制系统发出一个电平信号,输入变为 一个与原输入相反的电平,松开后立即回到原来的状态。简单地说,轻触开关就是触发一个暂态的信号。基于以上特点,轻触开关通常被广泛地运用于 “+ ”、 “-”键置数和复位按钮等。
(2) 轻触开关的内部结构。轻触开关的几种内部结构如图5‑1所示。
图5‑1 轻触开关的几种内部结构
按键是在设计中常见的装置,不论进行复位或者其他模块控制,按键电平检测都是必须的。
按键抖动通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。
图5‑2 按键过程电平抖动
如果在FPGA引脚上直接读取按键的电平,就会造成误触发,所以对于电平检测需要一些“措施”以保证每次读取的电平值都是实际的电平值。
一、 硬件消抖 :
所示利用RC 积分电路来达成杂波的滤除与波形修整的电路(如图5‑3 )。
在S1 ON 的瞬间由于接触弹跳的关系,会使A 点电压呈现高速的断续现象,再S1 OFF时亦然,详(如图2所示),然而由于电容两端电压需由电压经电阻慢慢充电才会上升,使得B 点电位缓步上升情形:S1 OFF 时亦然,电容电压经R 放电,使B 点电压缓缓下降。此一变化,经史密特反相修整后,可得一标准负脉波输出,如波形图C 点所示。
图5‑3 硬件消抖
用其他的各类触发器,锁存器亦可达到消抖效果。
二、软件消抖 :
软件消抖主要利用2.3UART协议相关知识,在此不再赘述。
1.1.2轻触开关的应用
5.1.2.1 轻触开关的普通应用
图5‑4 轻触开关的普通应用框图
表5‑1 消抖模块(debounce_module.v)模块间信号定义
模块间信号 |
||
管脚 |
传输方向 |
作用/说明 |
H2L_Sig |
detect_module.v模块传输给delay_module.v |
输入电平由高到低输出标志 |
L2H_Sig |
detect_module.v模块传输给delay_module.v |
输入电平由低到高输出标志 |
时序 |
没有特殊要求。 |
|
表5‑2 消抖模块(debounce_module.v)顶层模块信号定义
顶层模块 |
|||
输入管脚 |
作用/说明 |
输出管脚 |
作用/说明 |
Pin_in |
待检测电平输入引脚 |
Pin_Out |
消抖之后的输出信号 |
时序 |
没有特殊要求。 |
||
图5‑4上是一个简单的按键消抖模块。设计的方法主要是由“电平检查模块”和“10ms 延迟模块”组合合成。
设计的思路如下:
1) 一但检测到按键资源按下(高电平到低电平变化),“电平检查模块” 就会拉高H2L_Sig 电平,然后拉低。
2) “10ms 延迟模块”,检测到 H2L_Sig 高电平,就会利用 10ms 过滤 H2L_Sig,拉高输出。
3) 当按键被释放“电平检测模块”,会拉高 L2H_Sig 电平,然后拉低。
4) “10ms 延迟模块”,检查到 L2H_Sig 就会利用 10ms 过滤 H2L_Sig,然后拉低输出。
电平检测模块详见2.3.4实用UART传输FPGA实现介绍。
图5‑5 10ms 延迟模块
代码5‑1 10ms 延迟模块
1.//****************************************************************************// 2.//# @Author: 碎碎思 3.//# @Date: 2019-06-05 21:05:29 4.//# @Last Modified by: zlk 5.//# @WeChat Official Account: OpenFPGA 6.//# @Last Modified time: 2019-06-05 21:15:41 7.//# Description: 8.//# @Modification History: 2012-10-03 16:26:17 9.//# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2012-10-03 16:26:17 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module delay_module 17.( 18. CLOCK, RST_n, H2L_Sig, L2H_Sig, Pin_Out 19.); 20. 21. input CLOCK; 22. input RST_n; 23. input H2L_Sig; 24. input L2H_Sig; 25. output Pin_Out; 26. 27. /****************************************/ 28. 29. parameter T1MS = 16'd49_999;//DB4CE15开发板使用的晶振为50MHz,50M*0.001-1=49_999 30. 31. /***************************************/ 32. 33. reg [15:0]Count1; 34. 35. always @ ( posedge CLOCK or negedge RST_n ) 36. if( !RST_n ) 37. Count1 <= 16'd0; 38. else if( isCount && Count1 == T1MS ) 39. Count1 <= 16'd0; 40. else if( isCount ) 41. Count1 <= Count1 + 1'b1; 42. else if( !isCount ) 43. Count1 <= 16'd0; 44. 45. /****************************************/ 46. 47. reg [3:0]Count_MS; 48. 49. always @ ( posedge CLOCK or negedge RST_n ) 50. if( !RST_n ) 51. Count_MS <= 4'd0; 52. else if( isCount && Count1 == T1MS ) 53. Count_MS <= Count_MS + 1'b1; 54. else if( !isCount ) 55. Count_MS <= 4'd0; 56. 57. /******************************************/ 58. 59. reg isCount; 60. reg rPin_Out; 61. reg [1:0]i; 62. 63. always @ ( posedge CLOCK or negedge RST_n ) 64. if( !RST_n ) 65. begin 66. isCount <= 1'b0; 67. rPin_Out <= 1'b0; 68. i <= 2'd0; 69. end 70. else 71. case ( i ) 72. 73. 2'd0 : 74. if( H2L_Sig ) i <= 2'd1; 75. else if( L2H_Sig ) i <= 2'd2; 76. 77. 2'd1 : 78. if( Count_MS == 4'd10 ) begin isCount <= 1'b0; rPin_Out <= 1'b1; i <= 2'd0; end 79. else isCount <= 1'b1; 80. 81. 2'd2 : 82. if( Count_MS == 4'd10 ) begin isCount <= 1'b0; rPin_Out <= 1'b0; i <= 2'd0; end 83. else isCount <= 1'b1; 84. 85. 86. endcase 87. 88. /********************************************/ 89. 90. assign Pin_Out = rPin_Out; 91. 92. /********************************************/ 93. 94. 95. 96.endmodule |
45~86 行的设计思路:
1) 如果 H2L_Sig 信号有反应,就进入步骤 1;
2) 在进入步骤 1 之后,由于达不到 if 条件, isCount 使能。定时器,计数器呀开始执行。
3)在经过 10ms 之后, isCount 不使能,定时器,计数器停止执行。rPin_Out 为逻辑 1。返回步骤 0。
又或者:
1)如果 L2H_Sig 信号有反应,就进入步骤 2,
2)在进入步骤 2 之后,由于达不到 if 条件, isCount 使能。定时器,计数器呀开始执行。
3)在经过 10ms 之后, isCount 不使能,定时器,计数器停止执行。rPin_Out 为逻辑 0。返回步骤 0。
最后就是将上述模块封装,封装的结构框图如图5‑4所示。
代码5‑2 模块封装
1.//****************************************************************************// 2.//# @Author: 碎碎思 3.//# @Date: 2019-06-05 21:05:29 4.//# @Last Modified by: zlk 5.//# @WeChat Official Account: OpenFPGA 6.//# @Last Modified time: 2019-06-05 21:15:43 7.//# Description: 8.//# @Modification History: 2010-08-07 14:36:26 9.//# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2010-08-07 14:36:26 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module debounce_module 17.( 18. CLOCK, RST_n, Pin_In, Pin_Out 19.); 20. 21. input CLOCK; 22. input RST_n; 23. input Pin_In; 24. output Pin_Out; 25. 26. /**************************/ 27. 28. wire H2L_Sig; 29. wire L2H_Sig; 30. 31. detect_module U1 32. ( 33. .CLOCK( CLOCK ), 34. .RST_n( RST_n ), 35. .Pin_In( Pin_In ), // input - from top 36. .H2L_Sig( H2L_Sig ), // output - to U2 37. .L2H_Sig( L2H_Sig ) // output - to U2 38. ); 39. 40. /**************************/ 41. 42. delay_module U2 43. ( 44. .CLOCK( CLOCK ), 45. .RST_n( RST_n ), 46. .H2L_Sig( H2L_Sig ), // input - from U1 47. .L2H_Sig( L2H_Sig ), // input - from U1 48. .Pin_Out( Pin_Out ) // output - to top 49. ); 50. 51. /*******************************/ 52. 53.endmodule |
完成后RTL如下:
图5‑6 轻触开关普通应用RTL
编译后将程序下载到板子上,按按键后,对应的输出引脚的LED灯就会随着按键按下而被点亮,而且不会出现亮多次或者点不亮的情况。
5.1.2.2 轻触开关的点击与长按
在使用轻触开关时,还会出现长按的使用情景,例如长按“关机”。轻触开关的长按的基本理论和5.1.2.1节一样,首先看下整个模块结构:
图5‑7 轻触开关的点击与长按
表5‑3 点击与长按(SClicKLClick_module.v)模块间信号定义
模块间信号 |
||
管脚 |
传输方向 |
作用/说明 |
Pin_Out[1:0] |
keyfuncmod_module.v模块传输给led_module.v |
Pin_Out[1]-点击isSClick, Pin_Out[0]-长点击 isLClick |
时序 |
没有特殊要求。 |
|
表5‑4 点击与长按(SClicKLClick_module.v)顶层模块信号定义
顶层模块 |
|||
输入管脚 |
作用/说明 |
输出管脚 |
作用/说明 |
KEY |
待检测电平输入引脚 |
LED[1:0] |
KEY的输出信号 |
时序 |
没有特殊要求。 |
||
图5‑7是一个简单的点击与长按模块。设计的方法主要是由“按键功能模块”和“LED显示模块”组合合成,“按键功能模块”主要做“点击”和“长按击”检测。
“按键功能模块”中电平检测思路详见2.3.4实用UART传输FPGA实现节介绍。设计的思路如图所示:
图5‑8 点击与长按模块设计框图及思路
代码5‑3 点击与长按模块设计代码
1.//****************************************************************************// 2.//# @Author: 碎碎思 3.//# @Date: 2019-06-09 00:13:23 4.//# @Last Modified by: zlk 5.//# @WeChat Official Account: OpenFPGA 6.//# @Last Modified time: 2019-06-09 01:29:16 7.//# Description: 8.//# @Modification History: 2019-06-09 01:29:16 9.//# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2019-06-09 01:29:16 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module keyfuncmod_module 17.( 18. CLOCK, RST_n, 19. KEY, 20. Pin_Out 21.); 22. 23. input CLOCK, RST_n; 24. input KEY; 25. output [1:0]Pin_Out; 26. 27. /*****************************************/ //sub 28. parameter T10MS = 26'd500_000; // Deboucing time 29. parameter T3S = 28'd150_000_000; // Long press time 30. 31. /*****************************************/ //sub 32. 33. reg F2,F1; 34. 35. always @ ( posedge CLOCK or negedge RST_n ) 36. if( !RST_n ) 37. { F2, F1 } <= 2'b11; 38. else 39. { F2, F1 } <= { F1, KEY }; 40. 41. /*****************************************/ //core 42. 43. wire isH2L = ( F2 == 1 && F1 == 0 ); 44. wire isL2H = ( F2 == 0 && F1 == 1 ); 45. reg [3:0]i; 46. reg isLClick,isSClick; 47. reg [1:0]isTag; 48. reg [27:0]C1; 49. 50. always @ ( posedge CLOCK or negedge RST_n ) 51. if( !RST_n ) 52. begin 53. i <= 4'd0; 54. isLClick <= 1'd0; 55. isSClick <= 1'b0; 56. isTag <= 2'd0; 57. C1 <= 28'd0; 58. end 59. else 60. case(i) 61. 62. 0: // Wait H2L 63. if( isH2L ) i <= i + 1'b1; 64. 65. 1: // H2L debouce 66. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end 67. else C1 <= C1 + 1'b1; 68. 69. 2: // Key Tag Check 70. if( isL2H ) begin isTag <= 2'd1; C1 <= 28'd0; i <= i="" end="" span="" style="font-size:9.0pt; font-family:Consolas; color:#0000FF; mso-font-kerning:0.0pt" p="" b00="" c1="">= T3S -1 ) begin isTag <= 2'd2; C1 <= 28'd0; i <= i + 1'd1; end 72. else C1 <= C1 + 1'b1; 73. 74. 3: // Tag Trigger (pree up) 75. if( isTag == 2'd1 ) begin isSClick <= 1'b1; i <= i + 1'b1; end 76. else if( isTag == 2'd2 ) begin isLClick <= 1'b1; i <= i + 1'b1; end 77. 78. 4: // Tag Trigger (pree down) 79. begin { isLClick,isSClick } <= 2'b00; i <= i + 1'b1; end 80. 81. 5: // L2H deboce check 82. if( isTag == 2'd1 ) begin isTag <= 2'd0; i <= i + 2'd2; end 83. else if( isTag == 2'd2 ) begin isTag <= 2'd0; i <= i + 1'b1; end 84. 85. 6: // Wait L2H 86. if( isL2H )i <= i + 1'b1; 87. 88. 7: // L2H debonce 89. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= 4'd0; end 90. else C1 <= C1 + 1'b1; 91. 92. endcase 93. 94. /*************************/ 95. 96. assign Pin_Out = { isSClick,isLClick }; 97. 98.endmodule |
其他模块就不再赘述,按照图5‑8进行整个模块的连接,整个模块的RTL如下所示:
图5‑9 点击与长按模块设计RTL
和图5‑8一样,综合下载后,第一次按下 3 秒不放会点亮 LED[1],换之按下不到 3秒便释放则会点亮 LED[0]。第二次按下3 秒不放会消灭 LED[0],按下不到 3 秒便释放会消灭 LED[0]。如此一来,实验已经完成。
5.1.2.3 轻触开关的单击与双击
在使用轻触开关时,还会出现双击的使用情景。轻触开关的双击的基本理论和5.1.2.2节一样。
双击实现起来,会比较麻烦一些,因为还要考虑额外的细节,即人为连打极限。所谓人为连打极限就是两次按下按键之间的最短间隔。
图5‑10 双击有效按键,时序示意图(双击成功)
常人的连打极限是 60ms 左右,超人是 20ms 左右。为了兼容常人的连打极限,我们必须设置有效的连击时限,为此 100ms 是最好的选择。如图5‑10所示,假设那是按键过程,笔者先是缓缓按下然后又缓缓释放按键完成第一次按键行为,结果有如往常般,按下事件发生,抖动发生,释放事件发生,抖动发生,但是 isDClick( Double Click) 信号还有 isSClick( Single Click)信号都没有产生高脉冲。第一次按键完成以后就会引来第二次按键的黄金时间,亦即有效连击时限,在此笔者设为 100ms。假设笔者在这 100ms 的黄金时间内按下按键,那么 isDClick 信号会立即产生高脉冲。余下有如往常那样,抖动发生,释放事件发生,抖动发生 ... 对此, isSClick由始至终都没有状况发生
图5‑11 双击有效按键,时序示意图(双击失败)
假设没在有限的 100ms 黄金时间内执行第二次按键按下的动作,那么“双击”就会失败,结果如图5‑11所示。第一次按键过程与图5‑10一样,反之第二次按键却不同了,如图5‑11所示,第二次按键的按下事件是发生在 100ms 以后,为此 isSClick 产生高脉冲,然而 isDClick 信号却没有动静。
接下来看下整个模块结构:
图5‑12 轻触开关的单击与双击框图
表5‑5 点击与双击(SClickDClick_module.v)模块间信号定义
模块间信号 |
||
管脚 |
传输方向 |
作用/说明 |
Pin_Out[1:0] |
keyfuncmod_module.v模块传输给led_module.v |
Pin_Out[1]-点击isSClick, Pin_Out[0]-长点击 isDClick |
时序 |
没有特殊要求。 |
|
表5‑6 点击与长按(SClicKLClick_module.v)顶层模块信号定义
顶层模块 |
|||
输入管脚 |
作用/说明 |
输出管脚 |
作用/说明 |
KEY |
待检测电平输入引脚 |
LED[1:0] |
KEY的输出信号 |
时序 |
没有特殊要求。 |
||
图5‑12上是一个简单的点击与长按模块。设计的方法主要是由“按键功能模块”和“LED显示模块”组合合成,“按键功能模块”主要做“点击”和“长按击”检测。
“按键功能模块”中电平检测思路详见2.3.4实用UART传输FPGA实现节介绍。设计的思路如图所示:
图5‑13 点击与长按设计思路及框图
代码5‑4 点击与长按设计代码
1.//****************************************************************************// 2.//# @Author: 碎碎思 3.//# @Date: 2019-06-09 02:48:43 4.//# @Last Modified by: zlk 5.//# @WeChat Official Account: OpenFPGA 6.//# @Last Modified time: 2019-06-09 02:52:59 7.//# Description: 8.//# @Modification History: 2019-06-09 02:52:59 9.//# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2019-06-09 02:52:59 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module keyfuncmod_module 17.( 18. CLOCK, RST_n, 19. KEY, 20. Pin_Out 21.); 22. 23. input CLOCK, RST_n; 24. input KEY; 25. output [1:0]Pin_Out; 26. 27. /*****************************************/ //sub 28. parameter T10MS = 28'd500_000; 29. parameter T100MS = 28'd5_000_000; 30. parameter T200MS = 28'd10_000_000; 31. parameter T300MS = 28'd15_000_000; 32. parameter T400MS = 28'd20_000_000; 33. parameter T500MS = 28'd25_000_000; 34. /*****************************************/ //sub 35. 36. reg F2,F1; 37. 38. always @ ( posedge CLOCK or negedge RST_n ) 39. if( !RST_n ) 40. { F2, F1 } <= 2'b11; 41. else 42. { F2, F1 } <= { F1, KEY }; 43. 44. /*****************************************/ //core 45. 46. wire isH2L = ( F2 == 1 && F1 == 0 ); 47. wire isL2H = ( F2 == 0 && F1 == 1 ); 48. reg [3:0]i; 49. reg isDClick,isSClick; 50. reg [1:0]isTag; 51. reg [27:0]C1; 52. 53. always @ ( posedge CLOCK or negedge RST_n ) 54. if( !RST_n ) 55. begin 56. i <= 4'd0; 57. isDClick <= 1'd0; 58. isSClick <= 1'b0; 59. isTag <= 2'd0; 60. C1 <= 28'd0; 61. end 62. else 63. case(i) 64. 65. 0: // Wait H2L 66. if( isH2L ) begin i <= i + 1'b1; end 67. 68. 1: // H2L debounce 69. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end 70. else C1 <= C1 + 1'b1; 71. 72. 2: // Wait L2H 73. if( isL2H ) i <= i + 1'b1; 74. 75. 3: // L2H debounce 76. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end 77. else C1 <= C1 + 1'b1; 78. 79. 4: // Key Tag Check 80. if( isH2L && C1 <= T100MS -1 ) begin isTag <= 2'd2; C1 <= 28'd0; i <= i + 1'b1; end 81. else if( C1 >= T100MS -1) begin isTag <= 2'd1; C1 <= 28'd0; i <= i + 1'b1; end 82. else C1 <= C1 + 1'b1; 83. 84. 5: // Key trigger press up 85. if( isTag == 2'd2 ) begin isDClick <= 1'b1; i <= i + 1'b1; end 86. else if( isTag == 2'd1 ) begin isSClick <= 1'b1; i <= i + 1'b1; end 87. 88. 6: // Key trigger pree down 89. begin { isSClick , isDClick } <= 2'b00; i <= i + 1'b1; end 90. 91. 7: // L2H deounce check 92. if( isTag == 2'd1 ) begin isTag <= 2'd0; i <= 4'd0; end 93. else if( isTag == 2'd2 ) begin isTag <= 2'd0; i <= i + 1'b1; end 94. 95. 8: // Wait L2H 96. if( isL2H ) begin i <= i + 1'b1; end 97. 98. 9: // L2H debounce 99. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= 4'd0; end 100. else C1 <= C1 + 1'b1; 101. 102. endcase 103. 104. /***************************/ 105. 106. assign Pin_Out = { isSClick,isDClick }; 107. 108.endmodule |
其他模块就不再赘述,按照图5‑13进行整个模块的连接,整个模块的RTL如下所示:
图5‑14 点击与长按设计RTL
和图5‑13一样,综合下载后,当我们双击 建 LED[1] 就会发亮,然后再双击建 LED[1] 则会消灭,发生双击的前提条件是 ... 第一次按下时间的 100ms 之内必须发生第二次按下时间才能成立。换之,如果我们单击 建 LED[0] 便会发亮,再单击建LED[0]则会消灭。
5.1.2.4 轻触开关的单击、双击与长点击
最后将轻触开关的单击、双击与长点击进行组合,与上两节不同的是,两个组合检测在使用的时候检测条件比较简单,但是三个组合比较麻烦,核心操作会优先判断按键是否“长点击”,然后再来判断“点击”还是“双点击”,期间绝对不能搞错判断的次序。下面简单讲解下实现思路。
接下来看下整个模块结构:
图5‑15 轻触开关的单击、双击与长点击组合框图
表5‑7 轻触开关的单击、双击与长点(SClickDClick_module.v)模块间信号定义
模块间信号 |
||
管脚 |
传输方向 |
作用/说明 |
Pin_Out[1:0] |
keyfuncmod_module.v模块传输给led_module.v |
Pin_Out[1]-点击isSClick, Pin_Out[0]-长点击 isDClick |
时序 |
没有特殊要求。 |
|
表5‑8 轻触开关的单击、双击与长点(SClicKLClick_module.v)顶层模块信号定义
顶层模块 |
|||
输入管脚 |
作用/说明 |
输出管脚 |
作用/说明 |
KEY |
待检测电平输入引脚 |
LED[1:0] |
KEY的输出信号 |
时序 |
没有特殊要求。 |
||
图5‑15上是一个简单的点击与长按模块。设计的方法主要是由“按键功能模块”和“LED显示模块”组合合成,“按键功能模块”主要做“点击”和“长按击”检测。
“按键功能模块”中电平检测思路详见2.3.4实用UART传输FPGA实现节介绍。设计的思路如图所示:
图5‑16 轻触开关的单击、双击与长点设计思路及框图
代码5‑5 轻触开关的单击、双击与长点设计代码
1.//****************************************************************************// 2.//# @Author: 碎碎思 3.//# @Date: 2019-06-09 03:17:04 4.//# @Last Modified by: zlk 5.//# @WeChat Official Account: OpenFPGA 6.//# @Last Modified time: 2019-06-09 03:22:34 7.//# Description: 8.//# @Modification History: 2019-06-09 03:22:34 9.//# Date By Version Change Description: 10.//# ========================================================================= # 11.//# 2019-06-09 03:22:34 12.//# ========================================================================= # 13.//# | | # 14.//# | OpenFPGA | # 15.//****************************************************************************// 16.module keyfuncmod_module 17.( 18. CLOCK, RST_n, 19. KEY, 20. Pin_Out 21.); 22. 23. input CLOCK, RST_n; 24. input KEY; 25. output [2:0]Pin_Out; 26. 27. /*****************************************/ //sub 28. parameter T10MS = 28'd500_000; 29. parameter T100MS = 28'd5_000_000; 30. parameter T200MS = 28'd10_000_000; 31. parameter T300MS = 28'd15_000_000; 32. parameter T400MS = 28'd20_000_000; 33. parameter T500MS = 28'd25_000_000; 34. parameter T3S = 28'd150_000_000; 35. /*****************************************/ //sub 36. 37. reg F2,F1; 38. 39. always @ ( posedge CLOCK or negedge RST_n ) 40. if( !RST_n ) 41. { F2, F1 } <= 2'b11; 42. else 43. { F2, F1 } <= { F1, KEY }; 44. 45. /*****************************************/ //core 46. 47. wire isH2L = ( F2 == 1 && F1 == 0 ); 48. wire isL2H = ( F2 == 0 && F1 == 1 ); 49. reg [3:0]i; 50. reg isLClick, isDClick,isSClick; 51. reg [1:0]isTag; 52. reg [27:0]C1; 53. 54. always @ ( posedge CLOCK or negedge RST_n ) 55. if( !RST_n) 56. begin 57. i <= 4'd0; 58. isLClick <= 1'b0; 59. isDClick <= 1'b0; 60. isSClick <= 1'b0; 61. isTag <= 2'd0; 62. C1 <= 28'd0; 63. end 64. else 65. case(i) 66. 67. 0: // Wait H2L 68. if( isH2L ) begin i <= i + 1'b1; end 69. 70. 1: // H2L debounce 71. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end 72. else C1 <= C1 + 1'b1; 73. 74. 2: // Key Tag Check 1 75. if( isL2H ) begin C1 <= 28'd0; i <= i="" span="" style="font-size:9.0pt; font-family:Consolas; color:#0000FF; mso-font-kerning:0.0pt" end="" p="" b00="" c1="">= T3S -1 ) begin isTag <= 2'd3; C1 <= 28'd0; i <= 4'd5; end 77. else C1 <= C1 + 1'b1; 78. 79. 3: // L2H debounce 80. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= i + 1'b1; end 81. else C1 <= C1 + 1'b1; 82. 83. 4: // Key Tag Check 2 84. if( isH2L && C1 <= T100MS -1 ) begin isTag <= 2'd2; C1 <= 28'd0; i <= i + 1'b1; end 85. else if( C1 >= T100MS -1) begin isTag <= 2'd1; C1 <= 28'd0; i <= i + 1'b1; end 86. else C1 <= C1 + 1'b1; 87. 88. 5: // Key trigger press up 89. if( isTag == 2'd3 ) begin isLClick <= 1'b1; i <= i + 1'b1; end 90. else if( isTag == 2'd2 ) begin isDClick <= 1'b1; i <= i + 1'b1; end 91. else if( isTag == 2'd1 ) begin isSClick <= 1'b1; i <= i + 1'b1; end 92. 93. 6: // Key trigger pree down 94. begin { isLClick, isSClick, isDClick } <= 3'b000; i <= i + 1'b1; end 95. 96. 7: // L2H deounce check 97. if( isTag == 2'd1 ) begin isTag <= 2'd0; i <= i + 2'd2; end 98. else if( isTag == 2'd2 ) begin isTag <= 2'd0; i <= i + 1'b1; end 99. else if( isTag == 2'd3 ) begin isTag <= 2'd0; i <= i + 1'b1; end 100. 101. 8: // Wait L2H 102. if( isL2H ) begin i <= i + 1'b1; end 103. 104. 9: // L2H debounce 105. if( C1 == T10MS -1 ) begin C1 <= 28'd0; i <= 4'd0; end 106. else C1 <= C1 + 1'b1; 107. 108. endcase 109. 110. /***************************/ 111. 112. assign Pin_Out = { isSClick,isDClick,isLClick }; 113. 114.endmodule |
其他模块就不再赘述,按照图5‑16进行整个模块的连接,整个模块的RTL如下所示:
图5‑17 轻触开关的单击、双击与长点设计代码综合后RTL
和图5‑16一样,编译完后下载程序。如果点击一下 建,那么 LED[0] 会点亮,如果笔者双击建,结果 LED[1] 会点亮,再如果笔者长按建 3 秒不放,那么 LED[2] 则会点亮。总结说,一个按键资源可以执行 3 种功能,控制 3 位 LED 资源。