文章目录
- 基于FPGA的按键消抖
- 一、按键消抖原理
- 二、按键消抖代码
- 三、仿真代码编写
- 四:总结
基于FPGA的按键消抖
一、按键消抖原理
按键抖动:按键抖动通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。当按下一次按键,可能在A点检测到一次低电平,在B点检测到一次高电平,在C点又检测到一次低电平。同时抖动是随机,不可测的。那么按下一次按键,抖动可能会误以为按下多次按键。
按键原理图:
按键默认为低电平有效,高电平无效
1.按键消抖目的:消除按键抖动对我们程序的影响。
2.按键消抖解决方案1:延迟采样。延迟一定时间取样,去除没有抖动时的稳定值
3.按键消抖解决方案2:信号变化频率平稳后并且持续20ms则采样。
这里我们采用第二种方式,第二种方式相对于第一种方式更加稳定,效果也会更好。
二、按键消抖代码
key_debounce.v
// -----------------------------------------------------------------------------
// Copyright (c) 2014-2023 All rights reserved
// -----------------------------------------------------------------------------
// Author : 辣子鸡味的橘子,331197689@qq.com
// File : key_debounce.v
// Create : 2023-07-14 10:36:44
// Revise : 2023-07-14 10:36:44
// Editor : sublime text4, tab size (4)
// -----------------------------------------------------------------------------
module key_debounce(input wire clk,input wire rst_n,input wire[3:0] key_in,//四个按键信号输入output reg[3:0] key_out//四个按键信号消抖输出
);parameter TIME_20MS = 1000_000;
reg[19:0] cnt;//20ms计数器
wire add_cnt;//计数开始
wire ent_cnt;//计数终止
wire nedge;//下降沿检测reg[3:0] key_in_r0;//同步key_in输入信号
reg[3:0] key_in_r1;//延迟一个周期
reg[3:0] key_in_r2;//延迟两个周期reg flag;//消抖开始标志信号//计数器模块,当addent满足时开始计数,检测到下降沿重新计数,end_ent满足时停止计数,消抖完成
always @(posedge clk or negedge rst_n) beginif(~rst_n) begincnt<=20'd0;endelse if(add_cnt)beginif(ent_cnt)begincnt<=20'd0;endelse if(nedge)begincnt<=20'd0;endelse begincnt<=cnt+1;endendelse begincnt<=cnt;end
endassign add_cnt = flag;//计数开始条件
assign end_cnt = (cnt == TIME_20MS - 1)&&add_cnt;//终止结束条件,当满足计时到20ms,且满足计时条件时成立//信号延时模块
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginkey_in_r0<=4'b1111;key_in_r1<=4'b1111;key_in_r2<=4'b1111;endelse beginkey_in_r0<=key_in;key_in_r1<=key_in_r0;key_in_r2<=key_in_r1;end
end//检测下降沿,当任意一个按键出现下降沿都会被检测到
assign nedge = (~key_in_r1[0]&key_in_r2[0])||(~key_in_r1[1]&key_in_r2[1])||(~key_in_r1[2]&key_in_r2[2])||(~key_in_r1[3]&key_in_r2[3]);//消抖开始模块
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginflag<=1'b0;endelse if(nedge)begin//当出现下降沿开始消抖flag<=1'b1;endelse if(end_cnt)begin//当end_cnt满足时停止消抖flag<=1'b0;endelse beginflag<=flag;end
end//输出信号赋值模块,当消抖完毕标志按键按下,出现一个脉冲信号表示按键按下
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginkey_out<=4'b1111;//默认为高电平endelse if(end_cnt)beginkey_out<=key_in;//稳定信号赋值endelse beginkey_out<=4'b1111;//其他信号默认为高电平end
endendmodule
三、仿真代码编写
`timescale 1ns/1ns
module key_debounce_tb();
reg clk;
reg rst_n;
reg[3:0] key;wire[3:0] key_r;
parameter SYS_CLK = 20;
parameter TIME = 100;
always #(SYS_CLK/2) clk = ~clk;
initial beginclk=1'b0;rst_n=1'b0;#(2*SYS_CLK);rst_n=1'b1;key = 4'b1111;#(2*SYS_CLK+10);repeat (39) beginkey[0] = ~key[0];#(2*SYS_CLK);endkey[0] = 1'b0;#(1100*SYS_CLK);$stop;
end
key_debounce #(.TIME_20MS(TIME)) inst_key_debounce (.clk (clk),.rst_n (rst_n),.key_in (key),.key_out (key_r));endmodule
仿真时序图:
四:总结
其实别小看这个按键消抖,里面的细节有很多,自己之前尝试书写但是却没能达到想要的效果。特别是如何重新计数哪里确实困扰住了我,后面重新学习后完成了自己的效果,希望对你有所帮助。