verilog常用模块1——按键消抖模块详解
按键消抖模块key_filter
1. 原理介绍
如图,按键未按下时keys信号为高电平,按下则为低电平;通过检测keys信号电平,就可以判断按键状态。
但反作用弹簧会导致抖动现象,电平信号出现一段不确定波形
一般情况下,抖动的电平信号为1的持续时间不会超过20ms,只要通过对抖动波形的发生时长计时(计数),当为1的时长超过20ms,则可判定 进入了发生了按键按下/释放动作,并确定按键状态。
也可以采用一些硬件电路进行消抖,如RS触发器,555定时器组成的单稳态触发器等
(原理:《小梅哥-FPGA系统设计与验证实战指南_V24》P164)
2. 模块及接口设计![](/assets/blank.gif)
![](/assets/blank.gif)
3. 状态机设计
由时序图分析可知,存在这样四种状态:
IDLE
未按下时的空闲状态FILTER0
按下抖动滤除状态DOWN
按下稳定状态FILTER1
释放抖动滤除状态
状态转移图
状态转移条件
当前状态 | 下一状态 | 转移条件 | 输出 |
---|---|---|---|
IDLE | FILTER0 | nedge==1(检测到下降沿)(随后开始计数直至20ms) |
key_state=1 key_flag=1?? |
FILTER0 | IDLE | pedge==1(在按下消抖状态中,(未计数至20ms)如果检测到上升沿,则说明是一个抖动,仍回到初始态重新等待下降沿到来) | |
FILTER0 | DOWN | Cnt_full(检测到下降沿后即开始计时,判断按键电平是否在计数值满20ms时仍为低电平,若满足则认为确实按下,进入按下状态) | |
DOWN | FILTER1 | pedge==1(按下稳定状态中,如果检测到上升沿,则启动释放消抖)(随后开始计数直至20ms) | |
FILTER1 | DOWN | nedge==1(在释放消抖状态中,如果检测到下降沿且记数值未满,说明是个抖动,回到DOWN状态重新等待上升沿到来) | |
FILTER1 | IDLE | Cnt_full(若最后一次抖动后,处于高电平的时间计数满20ms,则认为确实释放完成,回到IDLE状态) |
4. 模块
(1)单bit异步信号同步化设计(两级DFF消除亚稳态模块)
按键输入信号key_in对于FPGA内部信号而言是一个异步输入的信号,具有随机性,不依赖于系统时钟Clk,如不处理直接作为输入使用,将会引其时序违例,导致亚稳态。因此需要用两级DFF将key_in打慢两拍,依赖于Clk,消除亚稳态。
异步信号采样时出现不定态,即出现亚稳态
key_in_b即为打慢两拍同步后的信号。
两级寄存器消除亚稳态具体原理解释
解释1
亚稳态在异步电路中是不可避免的,不管用什么同步电路去同步异步信号,都会出现亚稳态。那同步电路的作用是什么呢?其实是为了不让亚稳态传播。 两级同步电路的思想其实是为了给亚稳态留出足够的时间,好让亚稳态变成稳定态。
一般情况下亚稳态持续的时间很短,所以两级DFF同步之后的信号基本上不会出现亚稳态,可以给后级使用。
这里用两级DFF的目的就是给亚稳态留出一个DFF的工作时钟周期,如果第一级DF的输出出现亚稳态,那么在一个时钟周期内,让亚稳态变成稳定态。
这样第二级DFF采样第一级DFF的输出时,第一级DFF的输出早就稳定了,因此不会出现亚稳态的传播。所以在用两级同步电路对异步信号做了处理后,降低同步电路的工作时钟,就可以给亚稳态留更多的时间让它变成稳定态,因此可以起到使电路更稳定安全的效果。
解释2
假设第一级触发器的输入不满足其建立保持时间,它在第一个脉冲沿到来后输出的数据就为亚稳态,那么在下一个脉冲沿到来之前,其输出的亚稳态数据在一段恢复时间后必须稳定下来,而且稳定的数据必须满足第二级触发器的建立时间,如果都满足了,在下一个脉冲沿到来时,第二级触发器将不会出现亚稳态,因为其输入端的数据满足其建立保持时间。同步器有效的条件:第一级触发器进入亚稳态后的恢复时间 + 第二级触发器的建立时间 < = 时钟周期。
更确切地说,输入脉冲宽度必须大于同步时钟周期与第一级触发器所需的保持时间之和。最保险的脉冲宽度是两倍同步时钟周期。所以,这样的同步电路对于从较慢的时钟域来的异步信号进入较快的时钟域比较有效,对于进入一个较慢的时钟域,可能根本采不到异步信号。
但是一级FF稳定后的out是随机的,到第二级FF后大概率没有亚稳态了,但是得到的数据0/1都有可能,仅适用于对错误不敏感的地方。对于敏感的电路,可以采用双口RAM或FIFO同步。
https://blog.csdn.net/weixin_41458037/article/details/94383906?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-4&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-4
异步信号同步化代码实现
reg key_in_a,key_in_b;//声明同步寄存器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)beginkey_in_a <= 1'b0;//第一个寄存器key_in_sb <= 1'b0;//第二个寄存器end
else beginkey_in_a <= key_in;key_in_b <= key_in_a;end
在一些高速设计中,也有使用三级触发器进行单bit信号同步的设计,此方法只是为了提高平均故障时间( Mean Time Between Failure,MTBF),其电路为在两级触发器后再接一级触发器,代码为再加一个key_in_c。
(2)边沿检测模块设计
边沿检测电路原理图
边沿检测代码
reg key_tmpa,key_tmpb;
wire pedge,nedge;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)beginkey_tmpa <= 1'b0;key_tmpb <= 1'b0;end
else beginkey_tmpa <= key_in_sb;key_tmpb <= key_tmpa;endassign nedge = (~key_tmpa) && key_tmpb;
assign pedge = key_tmpa && (~key_tmpb);
在c语言中,!和~均表示取反,这两个的区别在于:
! :代表逻辑取反,即:把非0的数值变为0,0变为1;
~ :表示按位取反,即在数值的二进制表示方式上,将0变为1,将1变为0;
按位取反“~”:按位取反1变0,0变1
逻辑非“!”:逻辑取反, false变true,true变false,在C中,只要不是0就是真
在这里,对于1bit二进制数来说,!和 ~ 都是一样的
消亚稳态模块+边沿检测模块RTL视图
(3)20ms计数器模块
一般推荐一个always块只对一个信号进行操作!!使逻辑清晰易修改
reg [19:0]cnt;
reg en_cnt; //使能计数寄存器//计数模块
always@(posedge Clk or negedge Rst_n)if(!Rst_n)cnt <= 20'd0;else if(en_cnt)cnt <= cnt + 1'b1;elsecnt <= 20'd0;//计数满信号产生模块
always@(posedge Clk or negedge Rst_n)if(!Rst_n)cnt_full <= 1'b0;else if(cnt == 20'd999_999)cnt_full <= 1'b1;elsecnt_full <= 1'b0;
(4)状态机模块
reg [3:0]state;
localparamIDEL = 4'b0001,FILTER0 = 4'b0010,DOWN = 4'b0100,FILTER1 = 4'b1000;always@(posedge Clk or negedge Rst_n)if(!Rst_n)beginen_cnt <= 1'b0;state <= IDEL;key_flag <= 1'b0;key_state <= 1'b1;endelse begincase(state)IDEL :beginkey_flag <= 1'b0;if(nedge)beginstate <= FILTER0;en_cnt <= 1'b1;endelsestate <= IDEL;endFILTER0:if(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b0;en_cnt <= 1'b0;state <= DOWN;endelse if(pedge)beginstate <= IDEL;en_cnt <= 1'b0;endelsestate <= FILTER0;DOWN: //通过判断 key_flag && !key_state 来确定按键的状态,为 1 发生按下动作beginkey_flag <= 1'b0;if(pedge)beginstate <= FILTER1;en_cnt <= 1'b1;endelsestate <= DOWN;endFILTER1:if(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b1;state <= IDEL;en_cnt <= 1'b0;endelse if(nedge)beginen_cnt <= 1'b0;state <= DOWN;endelsestate <= FILTER1;default:begin state <= IDEL; en_cnt <= 1'b0; key_flag <= 1'b0;key_state <= 1'b1;endendcase end
通过判断 (key_flag && !key_state==1) 来确定按键的状态,为 TRUE 则按下动作发生
(5)仿真
仿真方法1 定时发生型
`timescale 1ns/1ns`define clk_period 20module key_filter_tb;reg Clk;reg Rst_n;reg key_in;wire key_flag;wire key_state;key_filter key_filter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));initial Clk= 1;always#(`clk_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;key_in = 1'b1;#(`clk_period*10) Rst_n = 1'b1;#(`clk_period*10 + 1); //与时钟边沿稍微延后1ns,减少门级仿真中亚稳态的产生//模拟按下抖动 20ms 内key_in = 1'b0;#1000;key_in = 1'b1;#2000;key_in = 1'b0;#1400;key_in = 1'b1;#2600;key_in = 1'b0;#1300;key_in = 1'b1;#200;key_in = 1'b0;#2000100;#50000100;//产生一个低电平大于 20ms,代表按下稳定//模拟释放抖动 20ms 内key_in = 1'b1;#2000;key_in = 1'b0;#1000;key_in = 1'b1;#2000;key_in = 1'b0;#1400;key_in = 1'b1;#2600;key_in = 1'b0;#1300;key_in = 1'b1;#2000100;#50000100;//产生一个高电平大于 20ms,代表释放稳定//模拟按下抖动 20ms 内key_in = 1'b0;#1000;key_in = 1'b1;#2000;key_in = 1'b0;#1400;key_in = 1'b1;#2600;key_in = 1'b0;#1300;key_in = 1'b1;#200;key_in = 1'b0;#2000100;#50000100;//产生一个低电平大于 20ms,代表按下稳定//模拟释放抖动 20ms 内key_in = 1'b1;#2000;key_in = 1'b0;#1000;key_in = 1'b1;#2000;key_in = 1'b0;#1400;key_in = 1'b1;#2600;key_in = 1'b0;#1300;key_in = 1'b1;#2000100;#50000100;//产生一个高电平大于 20ms,代表释放稳定$stop; endendmodule
仿真1波形图
仿真方法2 press_task型
reg [15:0]myrand;//随机数task press_key;beginrepeat(50) begin //五十次随机时间按下抖动myrand = {$random}%65536;#myrand key_in = ~key_in;endkey_in = 0;#50_000_000; //按下稳定repeat(50) begin //50 次随机时间释放抖动myrand = {$random}%65536;#myrand key_in = ~key_in;endkey_in = 1;#50_000_000; //释放稳定end
endtaskinitial beginRst_n = 1'b0;key_in = 1'b1;#(`clk_period*10) Rst_n = 1'b1;#30000;press_key; #10000;press_key; #10000;press_key; #10000;$stop;end
仿真2波形图
仿真方法3 无外部按键型key_model1
按键设置在task press_key内部已设置好
key_filter_top_tb(1)代码
`timescale 1ns/1ns`define clk_period 20module key_filter_top_tb;reg Clk;reg Rst_n;wire key_in;wire key_flag;wire key_state;key_filter key_filter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));key_model key_model0(.key(key_in));initial Clk= 1;always#(`clk_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;#(`clk_period*10) Rst_n = 1'b1;#(`clk_period*10 + 1); endendmodule
key_model1代码
`timescale 1ns/1nsmodule key_model(key);output reg key;reg [15:0]myrand;initial beginkey = 1'b1;press_key;#10000;press_key;#10000;press_key;#10000;$stop;endtask press_key;beginrepeat(50)beginmyrand = {$random}%65536;//0~65535;#myrand key = ~key; endkey = 0;#25000000;repeat(50)beginmyrand = {$random}%65536;//0~65535;#myrand key = ~key; endkey = 1;#25000000; end endtaskendmodule
仿真3波形图
仿真方法4 有外部按键型key_model2
按键设置在task press_key内部未设置好,按键动作为外部人为输入
keu_filter_top代码
module key_filter_top;input Clk;input Rst_n;input press;reg key_in;output key_flag;output key_state;key_filter key_filter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));key_model key_model0(.press(press),.key(key_in));endmodule
key_model2代码:按键动作由外部人为键入
`timescale 1ns/1nsmodule key_model(press,key);input press;output reg key;reg [15:0]myrand;initial beginkey = 1'b1; endalways@(posedge press)press_key;task press_key;beginrepeat(50)beginmyrand = {$random}%65536;//0~65535;#myrand key = ~key; endkey = 0;#25000000;repeat(50)beginmyrand = {$random}%65536;//0~65535;#myrand key = ~key; endkey = 1;#25000000; end endtaskendmodule
5. 边沿D触发器&电平D触发器&锁存器 波形图对比
部人为键入
`timescale 1ns/1nsmodule key_model(press,key);input press;output reg key;reg [15:0]myrand;initial beginkey = 1'b1; endalways@(posedge press)press_key;task press_key;beginrepeat(50)beginmyrand = {$random}%65536;//0~65535;#myrand key = ~key; endkey = 0;#25000000;repeat(50)beginmyrand = {$random}%65536;//0~65535;#myrand key = ~key; endkey = 1;#25000000; end endtaskendmodule
5. 边沿D触发器&电平D触发器&锁存器 波形图对比![](/assets/blank.gif)
verilog常用模块1——按键消抖模块详解相关推荐
- Verilog实现4位按键消抖,分别控制一个LED
Verilog实现4位按键消抖,分别控制一个LED 代码思路(完整代码在后) 完整代码 参考:(主要是第一篇,第二篇不严谨) <按键消抖与LED控制>实验的个人思考与总结 Verilog实 ...
- 【按键消抖】基于FPGA的按键消抖模块开发
1.软件版本 QUARTUSII8.1 Modelsim6.5d 2.系统源码 module tops(i_clk, //100Mi_rst, //系统复位功能,高电平复位,如果不使用这个角,那么一直 ...
- FPGA学习-Verilog实现独立按键消抖
文章目录 前言 一.独立按键消抖原理 二.按键消抖程序实现(Verilog) 1.按键触发判断 2.计数器模块实现 3.按键状态更新 4.按键控制led亮灭 三.仿真测试文件编写 四.编译结果 前言 ...
- 关于按键消抖以及LED灯控制的一个实例
要求: 1.未按建则所有LED全黑: 2.按K1按钮,则用前8个LED灯二进制显示25: 3.按K2按钮,则12只LED合并显示流水灯效果,3个LED点亮并向右流水. 注:是HR-240B FPGA ...
- [FPGA入门笔记](十):按键消抖实验
简介 今天购买了AXLINX AX7020的开发板,从今天开始每一个例程都要做文档记录,为自己加油. 本实验,基于ALINX AX7020开发板,芯片为xc7z020clg400-2.开发板输入时钟为 ...
- FPGA VerilogHDL语言 数字钟 按键消抖
1.描述 一个简单的基于FPGA的数字钟,语言用的是VerilogHDL,可以实现以下功能: 1. 数码管显示0-59(秒表) 2. 数码管显示:时-分-秒 3. 数码管显示时分秒并且可以设置时间(小 ...
- ISE 下按键消抖实验
实验原理 按键做为基本的人机输入接口,在很多电子设计中都能见到,由于机械特性,在按键按下或 松开的时候,按键输入值是有抖动的,无论按下去是多平稳,都难以消除抖动,按键消抖方式有 很多,本实验主要是通过 ...
- 按键消抖+点亮led灯
文章目录 前言 一.按键消抖 二.任务描述 三.系统框图 四.模块描述 五.模块代码 六.项目仿真 七.管脚信息 八.运行效果 总结 前言 本课程是按键消抖的一个扩展内容,主要是通过实验观察按键消 ...
- (三) 按键消抖电路设计
由于普通物理按键存在反作用弹簧,因此当按下或者松开时均会产生额外的物理抖动,物理抖动便会产生电平的抖动.在按键从按下再到松开的过程中,其电平变化如图2.2.1所示,上为理想波形输出,下为实际波形输出. ...
最新文章
- MySQL数据库高可用集群搭建-PXC集群部署
- 欢迎参加天津PDC Party的活动
- matlab复杂网络仿真,matlab在复杂网络上的应用
- 多路测温系统C51语言,51单片机多路DS18B20温度测量程序
- java全栈(java全栈开发工程师)
- 将Excel表格中的数字文本格式转化为数字格式
- VC++ sourceforge.net中一些好的开源项目
- Windows系统IPC$共享与勒索病毒
- ffmpeg 音频合并
- SSD算法详解 及其 keras实现 (下)
- 华为 会议室分配时间最长_华为“办公室新物种”来了,智慧办公一屏到位
- Arturia Sound Explorers Collection Belledonne现已上市
- 黑马程序员武汉2019新版前端与移动开发学习路线图(视频+工具+书籍+资源)
- 斗地主的两种发牌方式
- Android 获取设备唯一标识
- 七.getchar 和 scanf
- Hadoop的原理是什么
- 何恺明暗通道去雾(阅读笔记)
- 台式机计算机怎么分割,电脑切割分区:如何将一个分区划分成两个或多个
- Python专辑-QQ机器人1:基础功能实现
热门文章
- Oracle数据库 Oracle集合
- Iris: 比ScanContext更加精确高效的激光回环检测方法(IROS 2020)
- 一刷到底 微信 服务器设置,炸屏!给我一面国旗!@微信官方,服务器被刷崩了……...
- Linux 基本命令(七)
- 历史上最有才最搞怪的情书之一
- 《ArchSummit:时代的呐喊,技术人听得到》
- 开源中国2018 年度新增开源软件排行榜之国产 TOP 50
- 都2022年了,还有人在吵硬盘分区这事。。。
- 2022前端面经--HTML
- 容器虚拟化技术Docker(一)简介、安装、常见命令、数据卷、安装常规软件