按键抖动和消抖原理
本例中,我们通过按键来触发计数器动作,按一下数字自增1,同时显示在数码管的最低位上。为了体现按键消抖的必要性,我们通过拨动开关sw0来选择消抖前后的信号来驱动计数器,观察消抖前后的计数效果。
在消抖前,有时按下一次按键后,数码管显示的16进制数并未按预期的增加1,而是增加了2甚至3,这就是按键抖动造成的结果。一般来说,按键为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,按键开关在闭合时不会马上稳定地接通,在断开时也不会立刻断开,而是在闭合和断开的瞬间都伴随有一连串的抖动,如下图所示。因此,如果不对按键抖动进行处理,就可能对按键信号造成误判,例如将按下一次按键误判为多次按下按键。
按键抖动示意图
为了消除按键抖动的影响,就需要进行按键消抖。按键消抖有多种实现方法,相关的网络资源也有很多,这里将分享其中一种按键消抖方法。由于按键抖动的时间一般在10-20毫秒,而按键稳定的时间一般为数百毫秒,所以可以设置多个寄存器,延时读取按键的电平信号,再综合判断按键是否按下。
按键消抖示意图
代码解析
下面给出了按键消抖模块的参考代码。按键消抖模块中设置了三个寄存器构成移位寄存器,每20ms进行一次移位,寄存器key_reg0寄存的是当前的按键信号电平,寄存器key_reg1寄存的是20ms前的按键信号电平,而寄存器key_reg2寄存的是40ms前的按键信号电平。消抖后的按键信号为key_deb,当key_deb为1时代表按键按下,key_deb为0时代表按键未按下,代码中通过assign语句对key_deb进行赋值。这里判定两种情况下按键被按下,一是三个按键寄存器都为低电平,这显然是按键稳定的情况,第二种是40ms前的按键信号为高电平,而20ms前的按键信号和当前的按键信号为低电平。
module key_filter(clk,rstn,key_in,key_deb);
input clk;
input rstn;
input key_in;
output key_deb;
//50M/50 = 1M
parameter DIVCLK_CNTMAX_20ms = 499999;
wire clk_20ms;
//例化时钟分频模块,得到周期为20ms的时钟
clock_division #(
.DIVCLK_CNTMAX(DIVCLK_CNTMAX_20ms)
)
my_clock_0(
.clk_in(clk),
.divclk(clk_20ms)
);
reg key_reg0;
reg key_reg1;
reg key_reg2;
always@(posedge clk_20ms or negedge rstn) begin
if(~rstn) begin
key_reg0 <= 1'b1;
key_reg1 <= 1'b1;
key_reg2 <= 1'b1;
end
else begin
key_reg0 <= key_in;
key_reg1 <= key_reg0;
key_reg2 <= key_reg1;
end
end
assign key_deb = (~key_reg0 & ~key_reg1 & ~key_reg2) | (~key_reg0 & ~key_reg1 & key_reg2);
endmodule
注意上述按键消抖模块的代码中,最后的assign语句使用的全是按位运算符,所以当需要对多个按键进行消抖时,只拓宽key_in和key_deb的位宽即可。
接下来我们将时钟分频模块、按键消抖模块、按键测试模块和数码管显示模块通过顶层文件“连接”在一起,进行上板实验。其顶层参考代码如下所示,注意消抖后的按键信号key_deb是高电平代表按键按下,低电平代表未按下,所以需要取反接入按键测试模块。
module key_filter_test(clk_50M,key_row,key_col,sw,seg_sel,seg_led);
input clk_50M;
input sw;
input key_col;
output key_row;
output [3:0] seg_sel;
output [7:0] seg_led;
//行列键盘的第三行输出低电平
assign key_row = 1'b0;
//50M/1K = 5K
parameter DIVCLK_CNTMAX_1ms = 24999;
wire clk_1ms;
//例化时钟分频模块,得到周期为1ms的时钟
clock_division #(
.DIVCLK_CNTMAX(DIVCLK_CNTMAX_1ms)
)
my_clock_0(
.clk_in(clk_50M),
.divclk(clk_1ms)
);
wire key_deb;
key_filter key_filter(
.clk(clk_50M),
.rstn(1'b1),
.key_in(key_col),
.key_deb(key_deb)
);
wire key_in;
wire[3:0] seg_reg;
//使用sw0选择消抖前后的信号进入按键测试模块
assign key_in = sw? ~key_deb : key_col;
key_test key_test(
.key_in(key_in),
.seg_reg(seg_reg)
);
//例化数码管显示模块
Digitron4bits mydigitron
(.clk_1K(clk_1ms),
.ina(seg_reg),
.inb(),
.inc(),
.ind(),
.dot(3'b000),
.seg_sel(seg_sel),
.seg_led(seg_led));
endmodule
上板验证
下面还给出了该消抖测试的具体管脚约束。
按键消抖测试的管脚约束
set_pin_assignment { clk_50M } { LOCATION = R7; }
set_pin_assignment { sw } { LOCATION = A9; }
set_pin_assignment { seg_sel[0] } { LOCATION = C9; IOSTANDARD = LVCMOS33; }
set_pin_assignment { seg_sel[1] } { LOCATION = B6; IOSTANDARD = LVCMOS33; }
set_pin_assignment { seg_sel[2] } { LOCATION = A5; IOSTANDARD = LVCMOS33; }
set_pin_assignment { seg_sel[3] } { LOCATION = A3; IOSTANDARD = LVCMOS33; }
set_pin_assignment { seg_led[0] } { LOCATION = A4; IOSTANDARD = LVCMOS33; }
set_pin_assignment { seg_led[1] } { LOCATION = A6; IOSTANDARD = LVCMOS33; }
set_pin_assignment { seg_led[2] } { LOCATION = B8; IOSTANDARD = LVCMOS33; }
set_pin_assignment { seg_led[3] } { LOCATION = E8; IOSTANDARD = LVCMOS33; }
set_pin_assignment { seg_led[4] } { LOCATION = A7; IOSTANDARD = LVCMOS33; }
set_pin_assignment { seg_led[5] } { LOCATION = B5; IOSTANDARD = LVCMOS33; }
set_pin_assignment { seg_led[6] } { LOCATION = A8; IOSTANDARD = LVCMOS33; }
set_pin_assignment { seg_led[7] } { LOCATION = C8; IOSTANDARD = LVCMOS33; }
set_pin_assignment { key_row } { LOCATION = E10; IOSTANDARD = LVCMOS33; }
set_pin_assignment { key_col } { LOCATION = E11; IOSTANDARD = LVCMOS33; }
将sw0拨向上方,可以看到,经过按键消抖后,每次按下按键,数码管显示的16进制数都只增加了1,与预期的功能一致,也证明这种按键消抖的方法是可行的。
END
文章来源:https://www.yuque.com/yingmuketang/01/ix30k4
推荐内容
- 【安路 EG4S20 版本】基础板卡信息及使用教程:芯片和板卡简介
- 【安路 EG4S20 版本】基础板卡信息及使用教程:第一工程
- 【安路 EG4S20 版本】基础板卡信息及使用教程:使用ChipWatcher
- 【安路 EG4S20 版本】基础板卡信息及使用教程:使用Modelsim
- 【安路 EG4S20 版本】基础实验设计与实现:实验1 流水灯
- 【安路 EG4S20 版本】基础实验设计与实现:实验2 集成逻辑门及其基本应用
- 【安路 EG4S20 版本】基础实验设计与实现:实验3 译码器 编码器
- 【安路 EG4S20 版本】基础实验设计与实现:实验4 数据选择器
- 【安路 EG4S20 版本】基础实验设计与实现:实验5 触发器
- 【安路 EG4S20 版本】基础实验设计与实现:实验6 加法计数器
- 【安路 EG4S20 版本】基础实验设计与实现:实验7 抢答器
- 【安路 EG4S20 版本】基础实验设计与实现:实验8 功能数字钟
- 【安路 EG4S20 版本】基础实验设计与实现:实验9 矩阵键盘
- 【安路 EG4S20 版本】基础实验设计与实现:实验10 二进制转BCD码
- 【安路 EG4S20 版本】综合性实验设计与实现:实验11 PWM
- 【安路 EG4S20 版本】综合性实验设计与实现:实验12 DA及DDS
- 【安路 EG4S20 版本】综合性实验设计与实现:实验13 FPGA内部AD多通道采样实验
- 【安路 EG4S20 版本】综合性实验设计与实现:实验14 UART串行通信
- 【安路 EG4S20 版本】综合性实验设计与实现:实验15 高速ADC和DAC实验
- 【安路 EG4S20 版本】HDMI_Ethernet_DAP_SDRAM_Camera使用指南
- 【安路 EG4S20 版本】组合逻辑基础实验
- 【安路 EG4S20 版本】组合逻辑模块化设计实验
- 【安路 EG4S20 版本】点亮LED实验
- 【安路 EG4S20 版本】数码管动态显示实验
更多内容请关注走进FPGA专栏