差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
7._按键消抖 [2017/03/06 01:54]
zhijun
— (当前版本)
行 1: 行 1:
-=====按键消抖===== 
-在之前的实验中我们学习了如何用按键作为FPGA的输入控制,在本实验中将学习如何进行按键消抖,用按键完成更多的功能。 
-\\ 
-====硬件说明==== 
-------- 
-按键是一种常用的电子开关,电子设计中不可缺少的输入设备。当按下时使开关导通,松开时则开关断开,内部结构是靠金属弹片来实现通断。 
-\\  ​ 
-  ​ 
-**按键抖动的原理** 
-\\ 
-{{ :​抖动原理.png |抖动原理}} 
- 
-  - 抖动的产生 :通常的按键所用的开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。 
-  - 消除抖动的措施:一般我们采用软件方法消抖。即检测到按键按下动作之后进行10ms~20ms左右的延时,当前沿的抖动消失之后再一次检测按键的状态。如果仍然是按下的电平状态,则认为这是一次真正的按键按下;同样检测到按键释放,也要做10ms~20ms延时,检测到后沿抖动消失后认为是一个完整的按键弹起过程。 
-  - 消抖的好处:执行按键消抖有两个好处, 
-     - 消除误触发:我们想通过按键来翻转信号(例如按下一次led亮,在按一次led灭),如果没有进行消抖,则会产生很多误触发造成不必要的翻转。 
-     - 记录按键次数:执行按键消抖可以让我们记录按键动作的次数,在很多应用里这非常有用。 
- 
-\\  ​ 
-在[[1. 点亮LED|点亮LED]]实验中我们知道了小脚丫板子上按键的设计,当按键未被按下时,连接到FPGA管脚认为是高电平;当按键被按下时,连接到FPGA管脚认为是低电平。 
- 
-\\ 
-要消除按键的抖动,我们需要去扫描按键,也就是不断的去采集按键的状态。软件消抖时我们一般只考虑按键按下时的抖动,而放弃对释放时抖动的消除。在FPGA里有两种去抖思路: 
- 
-\\  ​ 
-1)用系统时钟(频率较高)去采集按键状态,当检测到按下时用计数器延时20ms,再去检测按键状态,如果这时仍为按下状态,确认是一次按下动作,否侧的话认为无按键按下。\\ 
-2)把系统时钟分频产生一个周期为20ms时钟,用这个时钟去检测按键状态,如果检测到按下则认为是一次按键按下动作。 
- 
-\\ 
-**脉冲边沿检测** 
- 
-\\ 
-检测按键按下时要用到脉冲边沿检测的方法,捕捉信号的突变、捕捉时钟的上升下降沿等经常会用到这种方法。简单地说就是用一个频率更高的时钟去触发要检测的信号,用两个寄存器去储存相邻两个时钟采集到的值,然后进行异或运算,如果不为零,代表发生了上升沿或者下降沿。 
- 
-\\ 
-在按键消抖的过程中,同样运用了脉冲边沿检测。用两个寄存器储存相邻时钟采集的值(例如data_pre,​data),​然后将data取反与前一个值相与(state=data_pre&​(~data)),​如果为1,则判断有下降沿既按键按下由高到低;否则无变化。 
-\\ 
-将一个信号由连续时钟采集,相邻两个钟触发的值存入两个寄存器。理解verilog实现这个过程要充分了解其中的[[非阻塞赋值]]。 
- 
-\\ 
-====Verilog代码==== 
------- 
-模块化设计是用硬件描述语言进行数字电路设计的精髓,代码可重复利用。而且模块化的设计使得程序的结构也很清晰。这里我们首先看看流水灯的模块化设计。利用了之前的3-8译码器和分频器,你需要把这两个程序也拷贝到一个工程。 
-<code verilog> 
- 
-// ******************************************************************** 
-// >>>>>>>>>>>>>>>>>>>>>>>>>​ COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<​ 
-// ******************************************************************** 
-// File name    : debounce.v 
-// Module name  : debounce 
-// Author ​      : STEP 
-// Description ​ :  
-// Web          : www.stepfpga.com 
-//  
-// -------------------------------------------------------------------- 
-// Code Revision History :  
-// -------------------------------------------------------------------- 
-// Version: |Mod. Date:   ​|Changes Made: 
-// V1.0     ​|2017/​03/​02 ​  ​|Initial ver 
-// -------------------------------------------------------------------- 
-// Module Function:​按键消抖 
- 
-module debounce (clk,​rst,​key,​key_pulse);​ 
- 
-        parameter ​      ​N ​ =  1;                      //​要消除的按键的数量 
- 
- input ​            clk; 
-        input             rst; 
-        input [N-1:​0] ​  ​key; ​                       //​输入的按键  
- output ​ [N-1:​0] ​  ​key_pulse; ​                 //​按键动作产生的脉冲  
-        ​ 
-        reg     ​[N-1:​0] ​  ​key_rst_pre; ​               //​定义一个寄存器型变量存储上一个触发时的按键值 
-        reg     ​[N-1:​0] ​  ​key_rst; ​                   //​定义一个寄存器变量储存储当前时刻触发的按键值 
-        ​ 
-        wire    [N-1:​0] ​  ​key_edge; ​                  //​检测到按键由高到低变化是产生一个高脉冲 
-        ​ 
-        //​利用非阻塞赋值特点,将两个时钟触发时按键状态存储在两个寄存器变量中 
-        always @(posedge clk  or  negedge rst) 
-          begin 
-             if (!rst) begin 
-                 ​key_rst <= {N{1'​b1}}; ​               //​初始化时给key_rst赋值全为1,{}中表示N个1 
-                 ​key_rst_pre <= {N{1'​b1}};​ 
-             end 
-             else begin 
-                 ​key_rst <= key;                     //​第一个时钟上升沿触发之后key的值赋给key_rst,​同时key_rst的值赋给key_rst_pre 
-                 ​key_rst_pre <= key_rst; ​            //​非阻塞赋值。相当于经过两个时钟触发,key_rst存储的是当前时刻key的值,key_rst_pre存储的是前一个时钟的key的值 
-             ​end ​   ​ 
-           end 
-            
-        assign ​ key_edge = key_rst_pre & (~key_rst);//​脉冲边沿检测。当key检测到下降沿时,key_edge产生一个时钟周期的高电平 
-        ​ 
-        reg [17:​0]  ​ cnt;                       //​产生延时所用的计数器,系统时钟12MHz,要延时20ms左右时间,至少需要18位计数器 ​     
-        ​ 
-        //​产生20ms延时,当检测到key_edge有效是计数器清零开始计数 
-        always @(posedge clk or negedge rst) 
-           begin 
-             ​if(!rst) 
-                cnt <= 18'h0; 
-             else if(key_edge) 
-                cnt <= 18'h0; 
-             else 
-                cnt <= cnt + 1'h1; 
-             ​end  ​ 
-        ​ 
-        reg     ​[N-1:​0] ​  ​key_sec_pre; ​               //​延时后检测电平寄存器变量 
-        reg     ​[N-1:​0] ​  ​key_sec; ​                   ​ 
-        ​ 
-                      
-        //​延时后检测key,如果按键状态变低产生一个时钟的高脉冲。如果按键状态是高的话说明按键无效 
-        always @(posedge clk  or  negedge rst) 
-          begin 
-             if (!rst) ​ 
-                 ​key_sec <= {N{1'​b1}}; ​               ​ 
-             else if (cnt==18'​h3ffff) 
-                 ​key_sec <= key;  ​ 
-          end 
-       ​always @(posedge clk  or  negedge rst) 
-          begin 
-             if (!rst) 
-                 ​key_sec_pre <= {N{1'​b1}};​ 
-             ​else ​                   
-                 ​key_sec_pre <= key_sec; ​             
-             ​end ​   ​ 
-           ​end ​     ​ 
-       ​assign ​ key_pulse = key_sec_pre & (~key_sec); ​     
-        
-endmodule ​ 
- 
-</​code>​ 
- 
-\\ 
- 
-以上就是一个N位按键的消抖程序,如果有按键按下会输出一个时钟周期的高脉冲。下面我们可以试试用这个按键消抖的输出来触发LED的显示,既按键一次LED翻转。你也可以不加按键消抖试试用按键来控制LED(按一次变亮,再按一次灭掉)。 
- 
-\\ 
-下面的程序是例化调用debounce模块来控制LED 
-\\ 
-<code verilog> 
-// ******************************************************************** 
-// >>>>>>>>>>>>>>>>>>>>>>>>>​ COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<​ 
-// ******************************************************************** 
-// File name    : top.v 
-// Module name  : top 
-// Author ​      : STEP 
-// Description ​ :  
-// Web          : www.stepfpga.com 
-//  
-// -------------------------------------------------------------------- 
-// Code Revision History :  
-// -------------------------------------------------------------------- 
-// Version: |Mod. Date:   ​|Changes Made: 
-// V1.0     ​|2017/​03/​02 ​  ​|Initial ver 
-// -------------------------------------------------------------------- 
-// Module Function:​进过按键消抖后控制led显示翻转 
- 
-module debounce (clk,​rst,​key,​led);​ 
- 
-        input             clk; 
-        input             rst; 
-        input           key;                       
- output ​  ​reg ​     led;        ​ 
- 
-        wire              key_pulse; 
-        ​ 
-        //​当按键按下时产生一个高脉冲,翻转一次led 
-        always @(posedge clk  or  negedge rst) 
-           begin 
-             if (!rst) ​ 
- led <= 1'b1; 
-      else if (key_pulse) 
- led <= ~led; 
-      else 
-                led <= led; 
-    ​end ​   ​ 
-         //​例化消抖module,这里没有传递参数N,采用了默认的N=1 ​     
-         ​debounce ​ u1 (                                
-                       .clk (clk), 
-                       .rst (rst), 
-                       .key (key), 
-                       ​.key_pulse (key_pulse) 
-                       ); 
- ​endmodule 
-</​code>​ 
- 
-\\ 
-====引脚分配==== 
-------- 
-设置好复位键可消抖的按键,编译完成后下载,通过按键就可以翻转LED。你也可以定义多个按键控制多个LED,还可以比较不加按键消抖情况下实际的效果对比如何。 
-\\ 
-^信号 ​           ^引脚 ​            ​^ ​   
-|clk             ​|C1 ​              ^ 
-|rst             ​|L14 ​             ^ 
-|key             ​|N14 ​            ^ 
-|led             ​|N13 ​             ^ 
- 
-====小结==== 
------- 
-在本实验学习了如何进行按键的消抖。在很多应用情况下我们必须采取消抖才能更好地控制逻辑。在下一个实验[[8. 计时控制|计时控制]]中我们将学习计时的显示和控制,在这里我们要用到按键的消抖以及数码管,我们甚至可以用小脚丫做一个计时器甚至电子表。