一. 按键是什么?
按键消抖_百度百科 (baidu.com)
按钮抖动通常使用的按钮开关是机械弹性开关。当机械触点断开和关闭时,由于机械触点的弹性,按钮开关不会立即稳定地关闭,也不会在断开时立即断开。因此,在关闭和关闭的时刻伴随着一系列的抖动,如下图所示,为了不产生这种现象而采取的措施是按钮抖动。
根据按钮对应的数字电平,按钮的抖动过程如下图所示。在抖动过程中,按钮的输入电平不稳定。抖动的目的是消除抖动的影响,获得能够表示按钮过程的稳定的按钮输入电平。
二. 按键消抖的方案
2.1 硬件消抖
通常的做法是在按钮两端并联一个贴片电容器,利用电容器两端电压不能突变的特性来消除抖动。
2.2 软件消抖
使用按钮按下的特性来抖动:
1.按键和弹起时会抖动。抖动的持续时间与按键的类型和质量有关,一般为5~10ms
2.按下按钮后保持稳定电平的时间一般不小于120ms
三. Verilog按键抖动
3.1 模块框图
3.2 参数列表 与 接口列表
参数名 | 说明 |
---|---|
KEY_DOWN_OUTPUT_HIGH_LEVEL | 按键输出电平, 默认高电平 |
CLKFREQ | 时钟频率, 注意修改 |
INTI_MS | 初始检测未按下电平, 需要持续多少MS视为检测成功, 默认50ms, 通常不需要修改 |
KEEP_MS | 检测到按钮按下/松开需要多长时间MS才视为有效, 默认40ms, 通常不需要修改 |
接口名 | 说明 |
---|---|
key_in | 按键输入 |
clk | 工作时钟 |
key_down | 按键状态颤抖 |
key_down_one_time | 按下按钮 |
key_up_one_time | 按钮松开一次 |
3.3 模块IP框图 与 使用说明
请参考使用说明3.2 参数列表和接口列表 以及 3.4 模块源码。
3.4 模块源码
/* * @Author : Xu Dakang * @Email : xudaKang_up@qq.com * @Date : 2021-12-20 15:13:10 * @LastEditors : Xu xiaokang * @LastEditTime : 2022-04-14 21:03:31 * @Filename : * @Description : */ /* ! 模块功能: 消除按钮抖动, 按键输入正确的电平 * 思路: 1.检测按钮未按下时的电平 复位完成后必须保证50ms(通过参数INTI_MS修改)内,按钮未按下,否则未按下电平检测会出错 2.按键电平为按下电平时,开始一个40ms(通过参数KEEP_MS修改)计数器,计数到最大值时,按下按钮 3.当按键电平松开时,开始40ms(通过参数KEEP_MS修改)计数器,计数到最大值时,按钮松开 */ module myKeyEliminateJitter #( parameter KEY_DOWN_OUTPUT_HIGH_LEVEL = 1, // 按键输出电平 parameter CLKFREQ = 100, // 时钟频率, 注意修改 parameter INTI_MS = 50, // 初始检测未按下电平, 要持续多少?MS视为检测成功, 默认50ms, 通常不需要修改 parameter KEEP_MS = 40 // 检测到按钮按下/松开需要多长时间MS才视为有效, 默认40ms, 通常不需要修改 )( output logic key_down, // 按键按下 output logic key_down_one_time, // 按下按钮 output logic key_up_one_time, // 按键松开一次 input logic key_in, // 按键输入 input logic clk ); // 按键输入同步 logic key_in_r1; logic key; always_ff @(posedge clk) begin key_in_r1 <= key_in;
key <= key_in_r1;
end
//-- 按键输入同步 ------------------------------------------------------------
//++ 按键未按下时的电平检测 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
logic key_r1;
always_ff @(posedge clk) begin
key_r1 <= key;
end
logic auto_detection_success;
// 50ms计数器
localparam CNT_50MS_MAX = CLKFREQ * 1000 * INTI_MS;
logic [$clog2(CNT_50MS_MAX+1)-1 : 0] cnt_50ms;
always_ff @(posedge clk) begin
// 按键信号一改变就重新计数, 初始情况下这不应该发生, 只是为了应对电磁干扰或机械振动等极端条件
if (auto_detection_success)
cnt_50ms <= cnt_50ms;
else if (key_r1 != key)
cnt_50ms <= '0;
else if (cnt_50ms < CNT_50MS_MAX)
cnt_50ms <= cnt_50ms + 1'b1;
else
cnt_50ms <= '0;
end
always_comb begin
if (cnt_50ms == CNT_50MS_MAX) // 按键信号从复位开始50ms内输入未发生变化
auto_detection_success = 1'b1;
else
auto_detection_success = 1'b0;
end
logic auto_detection_success_r1;
always_ff @(posedge clk) begin
auto_detection_success_r1 <= auto_detection_success;
end
assign auto_detection_success_pedge = auto_detection_success && ~auto_detection_success_r1;
logic key_up_value; // 存储按键未按下时的引脚电平
always_ff @(posedge clk) begin
if (auto_detection_success_pedge)
key_up_value <= key;
else
key_up_value <= key_up_value;
end
//-- 按键未按下时的电平检测 ------------------------------------------------------------
//++ 按键消抖 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 检测按下电平, 如果按下保持40ms, 则认为键正常按下
localparam CNT_40MS_MAX = CLKFREQ * 1000 * KEEP_MS;
logic [$clog2(CNT_40MS_MAX+1)-1 : 0] key_down_cnt_40ms;
always_ff @(posedge clk) begin
if (auto_detection_success_r1 && key == ~key_up_value) // 按键按下了
if (key_down_cnt_40ms < CNT_40MS_MAX)
key_down_cnt_40ms <= key_down_cnt_40ms + 1'b1;
else
key_down_cnt_40ms <= key_down_cnt_40ms;
else
key_down_cnt_40ms <= '0;
end
// 检测按键松开, 如果松开保持40ms, 则认为键正常松开
logic [$clog2(CNT_40MS_MAX+1)-1 : 0] key_up_cnt_40ms;
always_ff @(posedge clk) begin
if (auto_detection_success_r1 && key == key_up_value) // 按键松开了
if (key_up_cnt_40ms < CNT_40MS_MAX)
key_up_cnt_40ms <= key_up_cnt_40ms + 1'b1;
else
key_up_cnt_40ms <= key_up_cnt_40ms;
else
key_up_cnt_40ms <= '0;
end
always_ff @(posedge clk) begin
if (key_down_cnt_40ms == CNT_40MS_MAX) // 按下计数值保持最大值不变说明键处于按下的状态
key_down <= KEY_DOWN_OUTPUT_HIGH_LEVEL;
else if (key_up_cnt_40ms == CNT_40MS_MAX) // 松开计数值保持最大值不变说明键处于松开的状态
key_down <= ~KEY_DOWN_OUTPUT_HIGH_LEVEL;
else
key_down <= key_down;
end
logic key_down_r1;
always_ff @(posedge clk) begin
key_down_r1 <= key_down;
end
assign key_down_pedge = key_down && ~key_down_r1;
assign key_down_nedge = ~key_down && key_down_r1;
assign key_down_one_time = key_down_pedge;
assign key_up_one_time = key_down_nedge;
//-- 按键消抖 ------------------------------------------------------------
endmodule
3.5 工程分享
Verilog功能模块 —— 按键消抖 Vivado 2021.2工程 基于正点原子ZYNQ领航者V1-20220414。
欢迎大家关注我的公众号:徐晓康的博客,回复以下代码获取。
下载|2352
建议复制过去不会码错字!