差别

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

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
后一修订版 两侧同时换到之后的修订记录
7._按键消抖 [2017/03/05 23:09]
zhijun
7._按键消抖 [2021/10/03 01:25]
gongyu
行 1: 行 1:
-=====按键消抖=====+## 按键消抖
 在之前的实验中我们学习了如何用按键作为FPGA的输入控制,在本实验中将学习如何进行按键消抖,用按键完成更多的功能。 在之前的实验中我们学习了如何用按键作为FPGA的输入控制,在本实验中将学习如何进行按键消抖,用按键完成更多的功能。
 \\ \\
-====硬件说明==== + 
--------+### 1. 硬件说明 
 按键是一种常用的电子开关,电子设计中不可缺少的输入设备。当按下时使开关导通,松开时则开关断开,内部结构是靠金属弹片来实现通断。 按键是一种常用的电子开关,电子设计中不可缺少的输入设备。当按下时使开关导通,松开时则开关断开,内部结构是靠金属弹片来实现通断。
 \\  ​ \\  ​
行 21: 行 22:
  
 \\ \\
-要消除按键的抖动,我们需要去扫描按键,也就是不断的去采集按键的状态。软件消抖时我们一般只考虑按键按下时的抖动,而放弃对释放时抖动的消除。在FPGA里有两种去抖思路: +要消除按键的抖动,我们需要去扫描按键,也就是不断的去采集按键的状态。软件消抖时我们一般只考虑按键按下时的抖动,而放弃对释放时抖动的消除。用系统时钟(频率较高)去采集按键状态,当检测到按下时用计数器延时20ms,再去检测按键状态,如果这时仍为按下状态,确认是一次按下动作,否侧的话认为无按键按下。如何检测按键状态变化就需要用到脉冲边沿检测的方法
- +
-\\   +
-1)用系统时钟(频率较高)去采集按键状态,当检测到按下时用计数器延时20ms,再去检测按键状态,如果这时仍为按下状态,确认是一次按下动作,否侧的话认为无按键按下。\\ +
-2)把系统时钟分频产生一个周期为20ms时钟,用这个时钟去检测按键状态,如果检测到按下则认为是一次按键按下动作+
  
 \\ \\
行 39: 行 36:
  
 \\ \\
 +
 +### 2. Verilog代码
 +本实验主要通过按键来控制led的翻转,当按下一次led变亮,再按下一次led变暗。首先我们做个试验,对按键不做处理通过按键来控制led翻转。
 +
 +<code verilog>
 +
 +// ********************************************************************
 +// >>>>>>>>>>>>>>>>>>>>>>>>>​ COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<​
 +// ********************************************************************
 +// File name    : top.v
 +// Module name  : top
 +// Author ​      : STEP
 +// Description ​ : control led through the button
 +// Web          : www.stepfpga.com
 +// 
 +// --------------------------------------------------------------------
 +// Code Revision History : 
 +// --------------------------------------------------------------------
 +// Version: |Mod. Date:   ​|Changes Made:
 +// V1.0     ​|2017/​03/​02 ​  ​|Initial ver
 +// --------------------------------------------------------------------
 +// Module Function:​按键控制led翻转,未做消抖
 +
 +module top(
 + key, ​         //​按键输入
 + rst, ​         //​复位输入
 + led           //​led输出
 + );
 +
 + input key,rst;
 + output reg led;
 +
 + always @(key or rst)
 + if (!rst) ​            //​复位时led熄灭
 + led = 1;
 + else if(key == 0)     
 + led = ~led;   //​按键按下时led翻转
 + else
 + led = led;
 +endmodule
 +</​code>​
 +\\
 +未经过消抖的程序下载到小脚丫上会发现按键有时不能够控制led翻转,这是因为按键的抖动造成了led状态变化不可控,所以我们必须将抖动消除。下面是一种延时去抖的程序
 +
 +
 +<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 ​     ​
 +       ​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 top (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>​
 +
 +\\
 +
 +### 3. 引脚分配
 +设置好复位键可消抖的按键,编译完成后下载,通过按键就可以翻转LED。你也可以定义多个按键控制多个LED,还可以比较不加按键消抖情况下实际的效果对比如何。
 +\\
 +^信号 ​           ^引脚 ​            ​^ ​  
 +|clk             ​|C1 ​              ^
 +|rst             ​|L14 ​             ^
 +|key             ​|N14 ​            ^
 +|led             ​|N13 ​             ^
 +
 +### 4. 小结
 +在本实验学习了如何进行按键的消抖。在很多应用情况下我们必须采取消抖才能更好地控制逻辑。在下一个实验[[8. 计时控制|计时控制]]中我们将学习计时的显示和控制,在这里我们要用到按键的消抖以及数码管,我们甚至可以用小脚丫做一个计时器甚至电子表。