按键消抖——task任务和仿真平台搭建
一、按键抖动原理
按键抖动原理:按键存在一个反作用弹簧,因此当按下或者松开时均会产生额外的物理抖动,物理抖动会产生电平的抖动。
消抖方法:一般情况下,抖动的总时间会持续20ms以内,按下按键后,等20ms过去了再取键值就行了。
市面上有多种按键消抖的方法,我对比了各家的代码,发现有两种方法非常好用,其原理略微的不同。同时将小梅哥FPGA中的task任务和仿真模型的概念一并记录下来。
二、第1种按键消抖
只对按下侧的抖动进行消除,弹起的就不管了,因为我们使用按键时要的也是按下后的键值。输出为1clk的按键值。
//======================================================================
// --- 名称 : key_filter
// --- 作者 : xianyu_FPGA
// --- 日期 : 2018-11-02
// --- 描述 : 按键消抖,输出为1个clk的输入,只关注按下侧的消抖
//====================================================================== module key_filter
//---------------------<参数定义>---------------------------------------
#(
parameter TIME_20MS = , //20ms时间
parameter TIME_W = , //20ms时间位宽
parameter KEY_W = //按键个数
)
//---------------------<端口声明>---------------------------------------
(
input clk , //时钟,50Mhz
input rst_n , //复位,低电平有效
input [KEY_W-:] key , //按键输入
output reg [KEY_W-:] key_vld //按键消抖后的输出
);
//---------------------<信号定义>---------------------------------------
reg [TIME_W-:] cnt ;
wire add_cnt ;
wire end_cnt ;
reg [KEY_W -:] key_r0 ;
reg [KEY_W -:] key_r1 ;
reg flag ; //----------------------------------------------------------------------
//-- 信号同步 + 消除亚稳态
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_r0 <= ;
key_r1 <= ;
end
else begin
key_r0 <= key; //信号同步
key_r1 <= key_r0; //打拍,防亚稳态
end
end //----------------------------------------------------------------------
//-- 20ms计时
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= ;
else if(add_cnt)begin
if(end_cnt)
cnt <= ;
else
cnt <= cnt + ;
end
else
cnt <= cnt;
end assign add_cnt = flag== && key_r1!= ; //允许计数 且 按键按下
assign end_cnt = add_cnt && cnt==TIME_20MS-; //计到20ms //计满指示
always @(posedge clk or negedge rst_n)begin
if(!rst_n) //复位
flag <= ; //flag=0允许计数
else if(end_cnt) //20ms到
flag <= ; //flag=1不再计数
else if(key_r1==) //按键松开
flag <= ; //flag=0,为下次计数做准备
else //否则
flag <= flag; //维持自身
end //----------------------------------------------------------------------
//-- 按键消抖完成,输出按键有效信号
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
key_vld <= ;
else if(end_cnt) //20ms到
key_vld <= key_r1; //按键已消抖,可以使用
else
key_vld <= ;
end endmodule
现在编写仿真代码。由代码可以看到这里使用了task任务,用其定义一个完整的按下弹起的按键过程。
task的语法如下:
task <任务名>;<端口及数据类型声明语句>
<语句1>
<语句2>
.
.
.
<语句n>
endtask
task调用的语法如下:
<任务名> (端口1, 端口2 ... 端口n);
在task任务中,模拟抖动时采用了随机数发生函数来产生抖动。$random这一系统函数可以产生一个有符号的32位随机整数。一般的用法是“$random%b”,其中b > 0。这样就会生成一个范围在 [-(b-1),b-1] 中的随机数。如果只得到正数的随机数,这可采用“{$random}%b”来产生,这样就会生成一个方位在 [0,b-1] 中的随机数。
`timescale 1ns/1ps //时间精度
`define Clock //时钟周期 module key_filter_tb;
//---------------------<端口声明>---------------------------------------
reg clk ;
reg rst_n ;
reg [:] key ;
wire [:] key_vld ; //----------------------------------------------------------------------
//-- 模块例化
//----------------------------------------------------------------------
key_filter
#( //参数传递
.TIME_20MS ( )
)
u_key_filter //模块例化
(
.clk (clk ),
.rst_n (rst_n ),
.key (key ),
.key_vld (key_vld )
); //----------------------------------------------------------------------
//-- 时钟信号和复位信号
//----------------------------------------------------------------------
initial begin
clk = ;
forever
#(`Clock/) clk = ~clk;
end initial begin
rst_n = ; #(`Clock*+);
rst_n = ;
end //----------------------------------------------------------------------
//-- task函数编写,模拟按键抖动
//----------------------------------------------------------------------
reg [:] rand ; task press_key;
begin
repeat() begin //50次按下随机时间抖动
rand = {$random}%;
#rand;
key = ~key;
end
key = 'b1001;
#; repeat() begin //50次释放随机时间抖动
rand = {$random}%;
#rand;
key = ~key;
end
key = ;
#;
end
endtask //----------------------------------------------------------------------
//-- 设计输入信号
//----------------------------------------------------------------------
initial begin
#;
key = ; #(`Clock*+); //初始化完成
press_key; #;
press_key; #;
press_key; #;
$stop;
end endmodule
Modelsim仿真波形如下所示:
可以看到按键抖动被非常完美的仿真出来了。但是消抖后的按键值好像有点问题,没有变化一直为0?并非如此!是因为此设计的key_vld只维持1个clk的按键值,我们放大看看,喏,出现了!
三、第2种按键消抖
按下和弹起的抖动都消除掉
//======================================================================
// --- 名称 : key_filter
// --- 作者 : xianyu_FPGA
// --- 日期 : 2018-11-02
// --- 描述 : 按键消抖,输出为消抖后的输入,计数器一直在工作
//====================================================================== module key_filter
//---------------------<参数定义>---------------------------------------
#(
parameter TIME_20MS = , //20ms时间
parameter TIME_W = , //20ms时间位宽
parameter KEY_W = //按键个数
)
//---------------------<端口声明>---------------------------------------
(
input clk , //时钟,50Mhz
input rst_n , //复位,低电平有效
input [KEY_W-:] key , //按键输入
output reg [KEY_W-:] key_vld //消抖后的按键输出
);
//---------------------<信号定义>---------------------------------------
reg [TIME_W-:] cnt ;
wire add_cnt ;
wire end_cnt ;
reg [KEY_W -:] key_r0 ;
reg [KEY_W -:] key_r1 ;
reg [KEY_W -:] key_r2 ;
wire key_press ; //----------------------------------------------------------------------
//-- 边沿检测
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_r0 <= ;
key_r1 <= ;
key_r2 <= ;
end
else begin
key_r0 <= key; //信号同步
key_r1 <= key_r0; //打拍,防亚稳态
key_r2 <= key_r1;
end
end assign key_press = key_r1 ^ key_r2; //按键状态变化检测 //----------------------------------------------------------------------
//-- 20ms计时
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= ;
else if(add_cnt)begin
if(end_cnt)
cnt <= ;
else
cnt <= cnt + ;
end
else
cnt <= cnt;
end assign add_cnt = ; //一直处于计数状态
assign end_cnt = key_press || (cnt== TIME_20MS-); //按键仍在抖动或计到了20ms,则清0 //----------------------------------------------------------------------
//-- 按键消抖完成,输出按键有效信号
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
key_vld <= ;
else if(cnt==TIME_20MS-) //cnt计到20ms
key_vld <= key_r2; //按键已消抖,可以使用
else
key_vld <= key_vld;
end endmodule
现在编写仿真代码,和上面略微不同,我们使用一个新玩意:仿真模型。示意图如下,key_filter_tb是仿真文件,key_module模块是key_filter模块的仿真模型。
实际代码如下:
`timescale 1ns/1ps //时间精度
`define Clock //时钟周期 module key_filter_tb;
//---------------------<端口声明>---------------------------------------
reg clk ;
reg rst_n ;
wire [:] key ; //本是输入现在变成了内部信号,故改成wire型
wire [:] key_vld ; //----------------------------------------------------------------------
//-- 模块例化
//----------------------------------------------------------------------
//按键消抖仿真模型
key_module u_key_module
(
.key (key )
); //按键消抖设计文件
key_filter
#( //参数传递
.TIME_20MS ( )
)
u_key_filter //模块例化
(
.clk (clk ),
.rst_n (rst_n ),
.key (key ),
.key_vld (key_vld )
); //----------------------------------------------------------------------
//-- 时钟信号和复位信号
//----------------------------------------------------------------------
initial begin
clk = ;
forever
#(`Clock/) clk = ~clk;
end initial begin
rst_n = ; #(`Clock*+);
rst_n = ;
end endmodule
key_filter_tb
//======================================================================
//--名称 : key_module
//--作者 : xianyu_FPGA
//--日期 : 2018-11-02
//--描述 : key按键消抖模块的仿真模型
//======================================================================
`timescale 1ns/1ps module key_module
//---------------------<端口声明>---------------------------------------
(
output reg [:] key
); //----------------------------------------------------------------------
//-- task函数编写,模拟按键抖动
//----------------------------------------------------------------------
reg [:] rand ; task press_key;
begin
repeat() begin //50次按下随机时间抖动
rand = {$random}%;
#rand;
key = ~key;
end
key = 'b1001;
#; repeat() begin //50次释放随机时间抖动
rand = {$random}%;
#rand;
key = ~key;
end
key = ;
#;
end
endtask //----------------------------------------------------------------------
//-- 设计输入信号
//----------------------------------------------------------------------
initial begin
#;
key = ; #; //初始化完成
press_key; #;
press_key; #;
press_key; #;
$stop;
end endmodule
key_module
Modelsim仿真波形如下,输出波形刚好是消抖后的按键值,完美!
这两种按键消抖的本质都是一样的。第一种适用于只采1clk按键值的场景,例如按一下按键,计数器加1一次。第二种按键适用于各种场合,如果也只需要1clk的按键值,则在消抖后、使用前再用一次下降沿检测即可实现和第一种按键消抖一样的效果。
参考资料:
[1]小梅哥FPGA教程
[2]锆石科技FPGA教程
按键消抖——task任务和仿真平台搭建的更多相关文章
- 09B-独立按键消抖实验02——小梅哥FPGA设计思想与验证方法视频教程配套文档
芯航线--普利斯队长精心奉献 实验目的: 1.复习按键的设计 2.用模块化设计的方式实现每次按下按键0,4个LED显示状态以二进制加法格式加1,每次按下按键1,4个LED显示状态以二进制加法格式减 ...
- 09A-独立按键消抖实验01——小梅哥FPGA设计思想与验证方法视频教程配套文档
芯航线--普利斯队长精心奉献 实验目的: 1.复习状态机的设计思想并以此为基础实现按键消抖 2.单bit异步信号同步化以及边沿检测 3.在激励文件中学会使用随机数发生函数$random 4.仿真模 ...
- 按键消抖-----verilog
实际系统中常用的按键大部分都是轻触式按键,如下图所示.该按键内部由一个弹簧片和两个固定触点组成,当弹簧片被按下,则两个固定触点接通,按键闭合.弹簧片松开,两个触点断开,按键也就断开了.根据这种按键的机 ...
- FPGA学习笔记(八)—— 状态机设计实例之独立按键消抖
###### [该随笔中部分内容转载自小梅哥] ######### 独立按键消抖自古以来在单片机和FPGA中都是个不可避免的问题,首先,解释一下什么叫做按键抖动,如图,按键在按下和松开的那个瞬间存在大 ...
- 09自动售货机综设实验(含按键消抖,led和状态机)
一设计功能 1.上次状态机的练习 2这次自动售货机综设 (一)对比两次的售货机 上次售货机的关键是画出状态转移图.明确输入分几种,输出是啥,有哪些状态.如下图所示 (二)系统或综合设计的经验: 既然这 ...
- 强化版按键消抖Verilog实现
介绍:按键的物理结构导致了会有抖动现象的出现,判断按键是否真正按下,需要把抖动的部分滤波.根据经验可知,抖动一般在20ms内,所以常规的消抖方法是从变化沿出现时刻开始,延时20ms后判断按键的状态.这 ...
- Verilog HDL那些事_建模篇笔记(实验三:按键消抖)
实验三:按键消抖 首先将按键消抖功能分成了两个模块,电平检查模块和10ms延迟模块.电平检测模块用来检测按键信号的变化(是否被按下),10ms延迟模块用来稳定电平检查模块的输入,进而稳定按键信号,防止 ...
- 【代码】verilog之:按键消抖
此模块完美运行 /*-------------------------------------------------------------------------------------- -- ...
- 基于FPGA的数字秒表(数码管显示模块和按键消抖)实现
本文主要是学习按键消抖和数码管动态显示,秒表显示什么的,个人认为,拿FPGA做秒表真是嫌钱多. 感谢 感谢学校和至芯科技,笔者专业最近去北京至芯科技培训交流了一周.老师的经验还是可以的,优化了自己的代 ...
随机推荐
- JDK源码解析---HashMap源码解析
HashMap简介 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的,只是 ...
- ajax post data 获取不到数据,注意 content-type的设置 、post/get(转)
ajax post data 获取不到数据,注意 content-type的设置 .post/get 关于 jQuery data 传递数据.网上各种获取不到数据,乱码之类的. 好吧今天我也遇到了 ...
- Handsontable vue如何实现在线编辑excal
官网地址:https://handsontable.com/ 1.实现效果 2.安装 import { HotTable } from '@handsontable/vue' import Hands ...
- Hyperspectral Image Classification Using Similarity Measurements-Based Deep Recurrent Neural Networks
用RNN来做像素分类,输入是一系列相近的像素,长度人为指定为l,相近是利用像素相似度或是范围相似度得到的,计算个欧氏距离或是SAM. 数据是两个高光谱数据 1.Pavia University,Ref ...
- Beta冲刺(1/5)
队名:無駄無駄 组长博客 作业博客 组员情况 张越洋 过去两天完成了哪些任务 初步任务分配 提交记录(全组共用) 接下来的计划 完善接口文档 还剩下哪些任务 学习软工的理论课 学习代码评估.测试 燃尽 ...
- P5597 【XR-4】复读
枚举终点u,把路径(1,u)压起来(不考虑u的子树),并起来之后暴力
- Echarts 入门操作
Echarts具有丰富的图表,可以说是数据可视化的神器: 1.下载Echarts 到官网或者点击以下文字[下载Echarts]即可下载: ①官网下载地址:https://echarts.baidu.c ...
- SRS之安装与使用
1. 安装 1.1 github 获取源码 git clone https://github.com/ossrs/srs.git 1.2 编译 1.2.1 关闭防火墙和 selinux 先关闭防火墙, ...
- 用docker构建redis cluster
下面内容大部分借鉴自: http://louz.github.io/2016/08/11/docker-redis-cluster/ docker pull redis:3.0.7 #映射6379端口 ...
- windows正常,linux报错:'PHPExcel_Reader_excel2007' not found
原因:因为在linux下,大小写敏感 我的文件夹命名是大写,在window小写可以访问到,但是在linux就大小写敏感导致没找到文件没导入成功 导入文件的路径(错误)import('phpexcel. ...