FPGA按键消抖
led、数码管、蜂鸣器都学完了。接下来就是给工程加按键,来实现更多的功能。但按键的加入必须先进行消抖,否则会出问题。这篇文章结尾有万能按键消抖模版,直接运用就行。
前言
按键抖动:按键抖动通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。当按下一次按键,可能在A点检测到一次低电平,在B点检测到一次高电平,在C点又检测到一次低电平。同时抖动是随机,不可测的。那么按下一次按键,抖动可能会误以为按下多次按键。
所以要对按键进行消抖。
一、具体方式
1.延迟采样
需要在检测到按键抖动的时刻延时20ms再采样
2.抖动稳定后采样
需要不停的检测到按键抖动,直到信号稳定之后再延时20ms,之后采样
这里推荐一个写的很好的博客:
【FPGA】按键消抖
这章博客对按键消抖讲解的非常好。
我就直接偷懒,上万用模版
二、模版
// key #(.KEY_WIDTH(4)) key_inst(
// /*input */.sys_clk (sys_clk),
// /*input */.sys_rst_n (sys_rst_n),
// /*input [KEY_WIDTH - 1:0] */.key_in (),
// /*output reg [KEY_WIDTH - 1:0] */.key_flag ()
// );
module key#(
parameter KEY_WIDTH = 3'd4
)(
input sys_clk ,
input sys_rst_n ,
input [KEY_WIDTH - 1:0] key_in ,
output reg [KEY_WIDTH - 1:0] key_flag
);
parameter CNT_MAX = 20'd1000_000; //20ms消抖时间
localparam IDLE = 4'b0001,
DONE = 4'b0010,
HOLD = 4'b0100,
UP = 4'b1000;
reg [3:0] state;
reg [KEY_WIDTH - 1:0] key_in_reg0,
key_in_reg1,
key_in_reg2;
reg [KEY_WIDTH - 1:0] key_reg;
wire key_in_negedge,
key_in_posedge;
/* 打拍 */
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n) begin
key_in_reg0 <= {KEY_WIDTH{1'd1}}; /* 同步 */
key_in_reg1 <= {KEY_WIDTH{1'd1}}; /* 同步 */
key_in_reg2 <= {KEY_WIDTH{1'd1}}; /* 边沿 */
end
else begin
key_in_reg0 <= key_in;
key_in_reg1 <= key_in_reg0;
key_in_reg2 <= key_in_reg1;
end
/* 按位与,只要有一位出现了下降沿/上升沿,就会将该信号拉高 */
assign key_in_negedge = ((key_in_reg2 & ~key_in_reg1) != 'd0)? 1'b1 : 1'b0;
assign key_in_posedge = ((~key_in_reg2 & key_in_reg1) != 'd0)? 1'b1 : 1'b0;
reg start_cnt;
reg cnt_flag;
reg [19:0] cnt;
wire add_cnt,end_cnt;
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
start_cnt <= 1'b0;
else if(end_cnt)
start_cnt <= 1'b0;
else if(key_in_negedge || key_in_posedge)
start_cnt <= 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
cnt <= 20'd0;
else if(add_cnt) begin
if(end_cnt)
cnt <= 20'd0;
else
cnt <= cnt + 1'b1;
end
else
cnt <= 20'd0;
assign add_cnt = start_cnt;
assign end_cnt = add_cnt && ((cnt == CNT_MAX - 1) || (key_in_negedge));
/* 触发缓存 */
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
key_reg <= 'd0;
else if(key_in_negedge)
key_reg <= key_in_reg1;
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
state <= IDLE;
else case(state)
IDLE : if(key_in_negedge)
state <= DONE;
DONE : if(end_cnt) begin
if(key_in == key_reg)
state <= HOLD;
else
state <= IDLE;
end
HOLD : if(key_in_posedge)
state <= UP;
UP : if(end_cnt)
state <= IDLE;
default : state <= IDLE;
endcase
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
key_flag <= 'd0;
else if(state == HOLD && key_in_posedge)
key_flag <= ~key_in_reg2;
else
key_flag <= 'd0;
endmodule
最上边就是例化模版,有工程需要用到按键消抖的话,直接分出一个模块,把这个模版复制进去,然后在顶层文件中例化这个模块(把上边的例化模版加进顶层再连线就行)。非常实用。
总结
偷懒,这个万用模版直接发在百度网盘里了,就在资源里
博客上传的工程都在百度网盘,有压缩包也有整个文件,可以自行下载。
链接:https://pan.baidu.com/s/1lQqqWZXfb3i6XHwkKf52zg
提取码:yang