差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 后一修订版 两侧同时换到之后的修订记录 | ||
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. 计时控制|计时控制]]中我们将学习计时的显示和控制,在这里我们要用到按键的消抖以及数码管,我们甚至可以用小脚丫做一个计时器甚至电子表。 | ||