【黑金原创教程】【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 】实验三:按键模块② — 点击与长点击,布布扣,bubuko.com

时间: 2024-10-18 04:23:35

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验三:按键模块② — 点击与长点击的相关文章

【黑金原创教程】【FPGA那些事儿-驱动篇I 】连载导读

前言: 无数昼夜的来回轮替以后,这本<驱动篇I>终于编辑完毕了,笔者真的感动到连鼻涕也流下来.所谓驱动就是认识硬件,还有前期建模.虽然<驱动篇I>的硬件都是我们熟悉的老友记,例如UART,VGA等,但是<驱动篇I>贵就贵在建模技巧的升华,亦即低级建模II. 话说低级建模II,读过<建模篇>的朋友多少也会面熟几分,因为它是低级建模的进化形态.许久以前,笔者就有点燃低级建模II的念头,但是懒惰的性格让笔者别扭许久.某天,老大忽然说道:"让咱们大干一场吧

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验六:数码管模块

实验六:数码管模块 有关数码管的驱动,想必读者已经学烂了 ... 不过,作为学习的新仪式,再烂的东西也要温故知新,不然学习就会不健全.黑金开发板上的数码管资源,由始至终都没有改变过,笔者因此由身怀念.为了点亮多位数码管从而显示数字,一般都会采用动态扫描,然而有关动态扫描的信息请怒笔者不再重复.在此,同样也是动态扫描,但我们却用不同的思路去理解. 图6.1 6位数码管. 如图6.1所示,哪里有一排6位数码管,其中包好8位DIG信号还有6位SEL信号.DIG为digit,即俗称的数码管码,如果数码管

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二:按键模块① - 消抖

实验二:按键模块① - 消抖 按键消抖实验可谓是经典中的经典,按键消抖实验虽曾在<建模篇>出现过,而且还惹来一堆麻烦.事实上,笔者这是在刁难各位同学,好让对方的惯性思维短路一下,但是惨遭口水攻击 ... 面对它,笔者宛如被甩的男人,对它又爱又恨.不管怎么样,如今 I'll be back,笔者再也不会重复一样的悲剧. 按键消抖说傻不傻说难不难.所谓傻,它因为原理不仅简单(就是延迟几下下而已),而且顺序语言(C语言)也有无数不尽的例子.所谓难,那是因为人们很难从单片机的思维跳出来 ... 此外,

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

实验四:按键模块③ — 单击与双击 实验三我们创建了“点击”还有“长点击”等有效按键的多功能按键模块.在此,实验四同样也是创建多功能按键模块,不过却有不同的有效按键.实验四的按键功能模块有以下两项有效按键: l 单击(按下有效): l 双击(连续按下两下有效). 图4.1 单击有效按键,时序示意图. 实验四的“单击”基本上与实验三的“点击”一模一样,既按键被按下,经过消抖以后isSClick信号被拉高一个时钟,结果如图4.1所示,过程非常单调.反之,“双击”实现起来,会比较麻烦一些,因为我们还要

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验五:按键模块④ — 点击,长点击,双击

实验五:按键模块④ — 点击,长点击,双击 实验二至实验四,我们一共完成如下有效按键: l 点击(按下有效) l 点击(释放有效) l 长击(长按下有效) l 双击(连续按下有效) 然而,不管哪个实验都是只有两项“功能”的按键模块而已,如今我们要创建三项“功能”的按键模块,亦即点击(按下有效),长击,还有双击.实验继续之前,让我们先来复习一下各种有效按键. 图5.1 点击(按下有效). 如图5.1所示,所谓点击(按下有效)就是按键按下以后,isSClick信号(Single Click) 产生一

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验九:PS/2模块③ — 键盘与多组合键

实验九:PS/2模块③ — 键盘与多组合键 笔者曾经说过,通码除了单字节以外,也有双字节通码,而且双字节通码都是 8’hE0开头,别名又是 E0按键.常见的的E0按键有,<↑>,<↓>,<←>,<→>,<HOME>,<PRTSC> 等编辑键.除此之外,一些组合键也是E0按键,例如 <RCtrl> 或者 <RAlt> .所以说,当我们设计组合键的时候,除了考虑“左边”的组合键以外,我们也要考虑“右边”的组合键.&

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验七:PS/2模块① — 键盘

实验七:PS/2模块① — 键盘 实验七依然也是熟烂的PS/2键盘.相较<建模篇>的PS/2键盘实验,实验七实除了实现基本的驱动以外,我们还要深入解PS/2时序,还有PS/2键盘的行为.不过,为了节省珍贵的页数,怒笔者不再重复有关PS/2的基础内容,那些不晓得的读者请复习<建模篇>或者自行谷歌一下. 市场上常见的键盘都是应用第二套扫描码,各种扫描码如图7.2所示.<建模篇>之际,笔者也只是擦边一下PS/2键盘,简单读取单字节通码与断码而已.所谓单字节通码,就是有效的按下

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验八:PS/2模块② — 键盘与组合键

实验八:PS/2模块② — 键盘与组合键 实验七之际,我们学习如何读取PS/2键盘发送过来的通码与断码,不过实验内容也是一键按下然后释放,简单按键行为而已.然而,实验八的实验内容却是学习组合键的按键行为. 不知读者是否有类似的经历?当我们使用键盘的时候,如果5~6按键同时按下,电脑随之便会发出“哔哔”的警报声,键盘立即失效.这是键盘限制设计,不同产品也有不同限制的按键数量.默认下,最大按键数量是5~7个.所谓组合键就是两个以上的按键所产生的有效按键.举例而言,按下按键 <A> 输出“字符a”,

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十:PS/2模块④ &mdash; 普通鼠标

实验十:PS/2模块④ - 普通鼠标 学习PS/2键盘以后,接下来就要学习 PS/2 鼠标.PS/2鼠标相较PS/2键盘,驱动难度稍微高了一点点,因为FPGA(从机)不仅仅是从PS/2鼠标哪里读取数据,FPGA还要往鼠标里写数据 ... 反之,FPGA只要对PS/2键盘读取数据即可.然而,最伤脑筋的地方就在于PS/2传输协议有奇怪的写时序. 图10.1 从机视角,从机读数据. 为了方便理解,余下我们经由从机的视角去观察PS/2的读写时序.图10.1是从机视角的读时序,从机都是皆由 PS2_CLK