前言
利用verilog语言实现独立按键消抖,文章首先对按键抖动产生的原因、消抖原理进行简要解释;之后详细阐述各模块verilog语言实现方法;最后利用四个独立按键控制led亮灭,在vivado下进行源码设计与仿真。(完成程序代码附在文章结尾)
一、独立按键消抖原理
按键一般是机械弹性开关,由于机械触点的弹性作用,机械触点断开、闭合时会伴随着一连串的抖动,这个抖动会使得按键输出的高低电平连续变化,而这并不是真正的按下按键,如果直接作为开关控制后续电路,就会造成电路的不稳定,因此,需要采用按键消抖。 机械按键按下时候有一个不稳定的抖动期,这个时间大概在20ms以内,我们可以利用这个20ms的抖动期,当检测到按键电平变化时20ms计数器重新计数,若计数器达到20ms,证明按键电平变化以后的20ms内没有再发生电平变化,可以认为是按键真正被按下,将此时的按键状态放入寄存器进而控制后续电路。
二、按键消抖程序实现(Verilog)
1.按键触发判断
- 只要有按键状态变化,20ms计数器就应重新开始计数。而判断按键状态是否变化应该比较按键前后两个状态,由此采用移位寄存器,缓存按键前后的状态,再进行逻辑运算判断按键被按下还是松开。
注意:只要有按键被按下就要采用延时20ms消抖,同时按键全被松开也会对应按键电平的变化,仍然要采用消抖
代码如下:
wire key ;
reg[3:0] key_r ;
assign key = key_h[3]&key_h[2]&key_h[1]&key_h[0] ;
always@(posedge sys_clk_i or negedge ext_rst_n)begin
if(!ext_rst_n) key_r <= 4'b1111;
else key_r <= {key_r[2:0],key};
end
wire key_neg = key_r[3] & ~key_r[2] ;
wire key_pos = ~key_r[3] & key_r[2] ;
2.计数器模块实现
- 由于FPGA系统时钟是50MHz,需要计数到20ms 20ms/20ns得到结果为1_000_000次,表示需要每一个时钟上升沿计数,从0开始计数,计数到999_999,二十进制换算,需要采用20bit的寄存器来保存计数次数。
- 计数至20ms时应进行清零操作
reg[19:0]cnt ;
always@(posedge sys_clk_i or negedge ext_rst_n)begin
if(!ext_rst_n) cnt <= 20'd0 ;
else if(key_neg || key_pos ) cnt <= 20'd0 ;
else if(cnt == 20'd999_999) cnt <= 20'd0 ;
else cnt <= cnt + 20'd1 ;
end
3.按键状态更新
模块输入的按键值【key_h】状态表示未消抖前按键的状态,而控制后续电路的应该是消抖后的按键状态,因此需要设置一个4bit的寄存器,用于保存消抖后的状态。 同时,由于我们时利用按键前后的状态比较来判断按键是否被按下,所以需要有两个4bit的寄存器,分别保存前后状态。
reg[3:0]key_value[1:0] ;
always@(posedge sys_clk_i or negedge ext_rst_n)begin
if(!ext_rst_n)begin
key_value[0] = 4'b1111;
key_value[1] = 4'b1111;
end
else begin
if(cnt == 20'd999_999) key_value[0] = key_h ;
key_value[1] <= key_value[0];
end
end
wire[3:0}key_press ;
assign key_press = key_value[1] & ~key_value[0] ;
4.按键控制led亮灭
always@(posedge sys_clk_i or negedge ext_rst_n)begin
if(!ext_rst_n) led <= 4'b1111 ;
else begin
if(key_press[0]) led[0] <= ~led[0];
if(key_press[1]) led[1] <= ~led[1];
if(key_press[2]) led[2] <= ~led[2];
if(key_press[3]) led[3] <= ~led[3];
end
end
三、仿真测试文件编写
`timescale 1ns / 1ps
module sim_keyboard(
);
reg sys_clk_i ;
reg ext_rst_n ;
reg[3:0]key_h ;
wire[3:0]led ;
keyboard utt_keyboard(
.sys_clk_i(sys_clk_i),
.ext_rst_n(ext_rst_n),
.key_h(key_h),
.led(led)
);
initial begin
sys_clk_i = 1'b0 ;
ext_rst_n = 1'b0 ;
key_h = 4'b1111 ;
#1000
@(posedge sys_clk_i) ; #2 ;
ext_rst_n = 1'b1 ;
@(posedge sys_clk_i) ; #2 ;
key_h[0] = 1'b0 ;
#1000_000 ;
key_h[0] = 1'b1 ;
#5_000_000 ;
key_h[0] = 1'b0 ;
#3_000_000 ;
key_h[0] = 1'b1 ;
#1_000_000
key_h[0] = 1'b0 ;
#1000_000 ;
key_h[0] = 1'b1 ;
#5_000_000 ;
key_h[0] = 1'b0 ;
#3_000_000 ;
key_h[0] = 1'b1 ;
#3_000_000 ;
key_h[0] = 1'b0 ;
#50_000_000 ;
key_h[0] = 1'b1 ;
#30_000_000 ;
key_h[0] = 1'b0 ;
#50_000_000 ;
key_h[0] = 1'b1 ;
$finish ;
end
always #10 sys_clk_i = ~sys_clk_i ;
endmodule
四、编译结果
- 可以看到计数器计数到999_999,此时【key_h】的值为1110,【key_value[0]】值仍然为1111,在下一个时钟上升沿,消抖完成,【key_value[0]】会保存1110,表示按键key_h[0]被按下
- 下一个时钟上升沿计数器清零,重新开始20ms计数,key_value[0]即为消抖完成后案件的现态,key_value[1]表示按键的前一个状态,【key_press[0]】值为1,表示按键被按下。
- 测试文件模拟按键抖动过程,仿真结果可以看到当按键值【key_h】变化时,【key_pos】【key_neg】检测到边沿变化,但并不会引起【led】电平变化,说明消抖模块是有效的
代码 链接:https://pan.baidu.com/s/1euNaVxH-goOatb3lxpe4aQ 提取码:mcuh
|