一、按键原理
硬件原理图
- 当按键KEY1按下时,D3V3(也就是电源)通过电阻R(原理图上折线的那一段)然后再通过按键KEY1最终进入GND形成一条通路,那么这条线路的全部电压都加到了R这个电阻上,KEY1(最左边四个IO口)这个引脚就是个低电平。
- 当松开按键后,线路断开,就不会有电流通过,那么KEY1和D3V3就应该是等电位,是一个高电平。我们就可以通过KEY1这个IO口的高低电平来判断是否有按键按下。
二、按键消抖
2.1 原理
按键抖动通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。当按下一次按键,可能在A点检测到一次低电平,在B点检测到一次高电平,在C点又检测到一次低电平。同时抖动是随机,不可测的。那么按下一次按键,抖动可能会误以为按下多次按键。
所以我们在使用按键时往往需要消抖,以确保按键被按下一次只检测到一次低电平
2.2 解决方法
延迟采样
2.2.1 设计思路
通常我们认为按键的抖动时间存在于20ms之间,因此可以设计在第一次检测到有下降沿时开始计时,到计满20ms后,我们认为此时的按键信号保持稳定,则输出一个脉冲信号表示按键确实按下。
首先需要一个模块来检测按键是否抖动,如果抖动,→ 给计时模块一个标志位开始计时,记满20ms,→ 再给输出消抖后按键信号模块一个标志位进行采样。
2.2.2 时序图分析
- clock:我们的整个程序都是在时钟的控制下运行的,所有的always模块都对时钟的上升沿敏感,本人使用的开发板时钟频率是50MHz,也就是一秒震动50_000_000次,一个周期就是20ns
- key_in:这是按键输入信号,低电平有效
- key_r0:同步打拍后的信号,为了滤除掉小于一个周期的抖动,对key_in推迟一个周期进行同步
- key_r1 :这个信号是把同步信号再延时一个周期,主要是为了保存key_r0上一个周期的值(打一拍),来判断是否出现下降沿
- key_r2:与key_r1同理,再延时一个时钟周期
- nedge:检测key_r0是否出现下降沿,若出现,则将标志位flag设为1,也是计时器开启的条件
- add_cnt:计时器开启条件,用flag表示
- end_cnt:计时器结束条件,记满20ms,也是采样模块的开启条件
- key_down:计时器记满,则开始采样,key_down拉高一个周期(脉冲信号),代表按键按下
2.2.3 代码设计
key_debounce.v
/**************************************功能介绍***********************************
Date : 2023年7月27日11:07:19
Author : Ruminating.
Version : 1.1
Description: 按键消抖模块(任意位宽实现)
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module key_debounce #(parameter WIDTH = 4,//WIDTH为按键位宽(以4位宽为例)
TIME_20MS = 20'd1000_000//20ms计数器
)(
input wire clk ,//时钟信号
input wire rst_n ,//复位信号
input wire [WIDTH-1:0] key_in ,//输入按键信号
output wire [WIDTH-1:0] key_out //输出脉冲信号
);
//---------<参数定义>---------------------------------------------------------
// parameter TIME_20MS = 1000_000;//20ms
//---------<内部信号定义>-----------------------------------------------------
reg [WIDTH-1:0] key_down;//输出信号寄存器
reg [WIDTH-1:0] key_r0 ;//同步打拍
reg [WIDTH-1:0] key_r1 ;//打一拍
reg [WIDTH-1:0] key_r2 ;//打两拍
wire [WIDTH-1:0] n_edge ;//下降沿
reg flag ;//计数器计数的标志信号(按键按下抖动标志)
reg [19:0] cnt_20ms ;//20ms计数器
wire add_cnt_20ms;//开始计数条件
wire end_cnt_20ms;//结束计数条件
//****************************************************************