【FPGA零基础学习之旅#10】按键消抖模块设计与验证(一段式状态机实现)
🎉欢迎来到FPGA专栏~按键消抖模块设计与验证
- ☆* o(≧▽≦)o *☆嗨~我是小夏与酒🍹
- ✨博客主页:小夏与酒的博客
- 🎈该系列文章专栏:FPGA学习之旅
- 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
- 📜 欢迎大家关注! ❤️

🎉 目录-按键消抖模块设计与验证
- 一、效果演示
- 二、模块设计
- 三、仿真测试
- 3.1 常规编写
- 3.2 task编写
- 四、仿真模型

一、效果演示
🥝模块设计:

🥝按键消抖模块的完整代码,可直接使用:
//
//模块:按键消抖模块
//key_state:输出消抖之后按键的状态
//key_flag:按键消抖结束时产生一个时钟周期的高电平脉冲
/
module KeyFilter(input Clk,input Rst_n,input key_in,output reg key_flag,output reg key_state
);//按键的四个状态localparamIDLE = 4'b0001,FILTER1 = 4'b0010,DOWN = 4'b0100,FILTER2 = 4'b1000;//状态寄存器reg [3:0] curr_st;//边沿检测输出上升沿或下降沿wire pedge;wire nedge;//计数寄存器reg [19:0]cnt;//使能计数寄存器reg en_cnt;//计数满标志信号reg cnt_full;//计数满寄存器//------<边沿检测电路的实现>------//边沿检测电路寄存器reg key_tmp0;reg key_tmp1;//边沿检测always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)beginkey_tmp0 <= 1'b0;key_tmp1 <= 1'b0;endelse beginkey_tmp0 <= key_in;key_tmp1 <= key_tmp0;end endassign nedge = (!key_tmp0) & (key_tmp1);assign pedge = (key_tmp0) & (!key_tmp1);//------<状态机主程序>------ //状态机主程序always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endelse begincase(curr_st)IDLE:beginkey_flag <= 1'b0;if(nedge)begincurr_st <= FILTER1;en_cnt <= 1'b1;endelsecurr_st <= IDLE;endFILTER1:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b0;curr_st <= DOWN;en_cnt <= 1'b0;end else if(pedge)begincurr_st <= IDLE;en_cnt <= 1'b0;endelsecurr_st <= FILTER1;endDOWN:beginkey_flag <= 1'b0;if(pedge)begincurr_st <= FILTER2;en_cnt <= 1'b1;endelsecurr_st <= DOWN;endFILTER2:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b1;curr_st <= IDLE;en_cnt <= 1'b0;end else if(nedge)begincurr_st <= DOWN;en_cnt <= 1'b0;endelsecurr_st <= FILTER2;enddefault:begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endendcaseendend//------<20ms计数器>------ //20ms计数器//Clk 50_000_000Hz//一个时钟周期为20ns//需要计数20_000_000 / 20 = 1_000_000次always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt <= 20'd0;else if(en_cnt)cnt <= cnt + 1'b1;elsecnt <= 20'd0;endalways@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt_full <= 1'b0;else if(cnt == 999_999)cnt_full <= 1'b1;elsecnt_full <= 1'b0;endendmodule
🥝RTL视图:

🥝状态转移:

🥝仿真结果:

二、模块设计
🥝模块设计:
| 信号 | 作用 |
|---|---|
| clk | 时钟信号输入 |
| rst_n | 复位信号输入 |
| key_in | 按键信号输入 |
| key_flag | 消抖结束之后的标志位 |
| key_state | 消抖结束之后按键的状态 |

🥝上升沿检测电路:

🥝下降沿检测电路:

🥝边沿检测电路的实现:
检测到下降沿,nedge输出高电平;检测到上升沿,pedge输出高电平。
//------<边沿检测电路的实现>------
//边沿检测电路寄存器
reg key_tmp0;
reg key_tmp1;//边沿检测
always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)beginkey_tmp0 <= 1'b0;key_tmp1 <= 1'b0;endelse beginkey_tmp0 <= key_in;key_tmp1 <= key_tmp0;end
endassign nedge = (!key_tmp0) & (key_tmp1);//检测到下降沿,nedge输出高电平
assign pedge = (key_tmp0) & (!key_tmp1);//检测到上升沿,pedge输出高电平
🥝一段式状态机设计:
按键的四种状态:
//按键的四个状态
localparamIDLE = 4'b0001,FILTER1 = 4'b0010,DOWN = 4'b0100,FILTER2 = 4'b1000;
计数器:
//------<20ms计数器>------
//20ms计数器
//Clk 50_000_000Hz
//一个时钟周期为20ns
//需要计数20_000_000 / 20 = 1_000_000次//计数寄存器
reg [19:0]cnt;//使能计数寄存器
reg en_cnt;//计数满标志信号
reg cnt_full;//计数满寄存器always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt <= 20'd0;else if(en_cnt)cnt <= cnt + 1'b1;elsecnt <= 20'd0;
endalways@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt_full <= 1'b0;else if(cnt == 999_999)cnt_full <= 1'b1;elsecnt_full <= 1'b0;
end
状态机主程序:
//------<状态机主程序>------
//状态机主程序
always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endelse begincase(curr_st)IDLE:beginkey_flag <= 1'b0;if(nedge)begincurr_st <= FILTER1;en_cnt <= 1'b1;endelsecurr_st <= IDLE;endFILTER1:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b0;curr_st <= DOWN;en_cnt <= 1'b0;end else if(pedge)begincurr_st <= IDLE;en_cnt <= 1'b0;endelsecurr_st <= FILTER1;endDOWN:beginkey_flag <= 1'b0;if(pedge)begincurr_st <= FILTER2;en_cnt <= 1'b1;endelsecurr_st <= DOWN;endFILTER2:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b1;curr_st <= IDLE;en_cnt <= 1'b0;end else if(nedge)begincurr_st <= DOWN;en_cnt <= 1'b0;endelsecurr_st <= FILTER2;enddefault:begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endendcaseend
end
三、仿真测试
3.1 常规编写
`timescale 1ns/1ns
`define clock_period 20module KeyFilter_tb;reg Clk;reg Rst_n;reg key_in;wire key_flag;wire key_state;KeyFilter KeyFilter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));initial Clk = 1;always#(`clock_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;key_in = 1'b1;#(`clock_period*10);Rst_n = 1'b1;#(`clock_period*10 + 1);key_in = 0;#1000;key_in = 1;#2000;key_in = 0;#1400;key_in = 1;#2600;key_in = 0;#1300;key_in = 1;#200;key_in = 0;#20000100;#50000000;key_in = 1;#2600;key_in = 0;#1000;key_in = 1;#2000;key_in = 0;#1400;key_in = 1;#2600;key_in = 0;#1300;key_in = 1;#200;key_in = 1;#20000100;#50000000;$stop;endendmodule
仿真结果:

3.2 task编写
`timescale 1ns/1ns
`define clock_period 20module KeyFilter_tb;reg Clk;reg Rst_n;reg key_in;wire key_flag;wire key_state;KeyFilter KeyFilter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));initial Clk = 1;always#(`clock_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;key_in = 1'b1;#(`clock_period*10);Rst_n = 1'b1;#(`clock_period*10 + 1);#30000;PressKey; #10000;PressKey; #10000;PressKey; #10000;$stop;endreg [15:0]myrand;task PressKey;begin//50次随机时间按下抖动repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key_in = ~key_in;endkey_in = 0;#50_000_000;//按下稳定//50次随机时间释放抖动repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key_in = ~key_in;endkey_in = 1;#50_000_000;//释放稳定endendtaskendmodule
注意$random随机函数的用法:
$random这一系统函数可以产生一个有符号的32bit随机整数。一般的用法是$random%b,其中b>0。这样就会生成一个范围在(-b+1):(b-1)中的随机数。如果只得到正数的随机数,可采用{$random}%b来产生。
myrand = {$random}%65536;//0~65535
上述语句的作用即是产生了0~65535之间的随机数。
通过repeat语句循环50次,就产生了50次不同的延时效果:
repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key_in = ~key_in;
end
仿真结果:

四、仿真模型
编写key_model并添加到测试激励文件中:
`timescale 1ns/1nsmodule key_model(key);output reg key;reg [15:0]myrand;initial beginkey = 1'b1;PressKey; #10000;PressKey; #10000;PressKey; #10000;$stop;endtask PressKey;begin//50次随机时间按下抖动repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key = ~key;endkey = 0;#50_000_000;//按下稳定//50次随机时间释放抖动repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key = ~key;endkey = 1;#50_000_000;//释放稳定endendtask endmodule
修改KeyFilter_tb:
`timescale 1ns/1ns
`define clock_period 20module KeyFilter_tb;reg Clk;reg Rst_n;wire key_in;wire key_flag;wire key_state;KeyFilter KeyFilter0(.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#(`clock_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;#(`clock_period*10);Rst_n = 1'b1;#(`clock_period*10 + 1);endendmodule
整个激励文件的内部结构:

仿真结果:


🧸结尾
- ❤️ 感谢您的支持和鼓励! 😊🙏
- 📜您可能感兴趣的内容:
- 【FPGA】串口通信讲解-状态机判断数据值
- 【Python】串口通信-与FPGA、蓝牙模块实现串口通信(Python+FPGA)
- 【Arduino TinyGo】【最新】使用Go语言编写Arduino-环境搭建和点亮LED灯
- 【全网首发开源教程】【Labview机器人仿真与控制】Labview与Solidworks多路支配关系-四足爬行机器人仿真与控制
相关文章:
【FPGA零基础学习之旅#10】按键消抖模块设计与验证(一段式状态机实现)
🎉欢迎来到FPGA专栏~按键消抖模块设计与验证 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒🍹 ✨博客主页:小夏与酒的博客 🎈该系列文章专栏:FPGA学习之旅 文章作者技术和水平有限,如果文中出现错误,希望大…...
【EI复现】基于阶梯碳交易的含P2G-CCS耦合和燃气掺氢的虚拟电厂优化调度(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
高防cdn和高防服务器有什么不一样?
高防cdn: 相信很多看过我们文章的小伙伴对cdn已经很了解了,cdn的原理很简单,就是构建在网络上的很多个节点,为网站作内容 分发。使用户就近获取所需资源。且分配的cdn节点都是高防节点,每个节点都有防御功能。还…...
ppt怎么压缩?试试这样压缩文件
当PPT文件体积过大时,打开的速度就会很慢,演示的时候刘程度也会受到影响,其次,现在很多平台对于上传的文件是有大小限制的,比如超过100M的文件就无法上传、发送等等,那么,怎么才能压缩PPT文件呢…...
stm32 cubemx ps2无线(有线)手柄
文章目录 前言一、cubemx配置二、代码1.引入库bsp_hal_ps2.cbsp_hal_ps2.h 2.主函数 前言 本文讲解使用cubemx配置PS2手柄实现对手柄的按键和模拟值的读取。 很简单,库已经封装好了,直接就可以了。 文件 一、cubemx配置 这个很简单,不需要…...
【TI毫米波雷达笔记】sdk传参时的type避坑
【TI毫米波雷达笔记】sdk传参时的type避坑 这个函数要传一个结构体进去 然后结构体里面有个adcoutcfg结构体变量 adcoutcfg结构体里面共有三个变量 一个adcbitformat结构体 另外两保留 点开adcbitformat结构体发现是个32位段 一共四级结构体 那么请问 为什么adcoutcfg变量不直…...
【算法挨揍日记】day02——双指针算法_快乐数、盛最多水的容器
202. 快乐数 202. 快乐数https://leetcode.cn/problems/happy-number/ 题目: 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个…...
【Hilog】鸿蒙系统日志源码分析
【Hilog】鸿蒙系统日志源码分析 Hilog采用C/S结构,Hilogd作为服务端提供日志功能。Client端通过API调用(最终通过socket通讯)与HiLogd打交道。简易Block图如下。 这里主要分析一下。Hilog的读、写、压缩落盘,以及higlog与android…...
keil下载程序具体过程4:flash下载算法
引言 本篇文章将介绍flash算法文件,阐述从jlink如何下载镜像文件写入到内部的falsh。 一、XIP 在谈flash下载算法文件时,先说明XIP是什么。 芯片的启动方式有很多种:可以从RAM中启动、内部的flash、外部的flash等等(还有从sd卡、…...
如何快速的让自己从月入2000变成月入两万?
从月入2000变成月入两万 前言我们可以这么做:1.提升自己的技能:2.寻找更好的工作机会:寻找更好的工作机会是一个重要的目标,以下是几个建议: 3.开展副业或兼职工作:4.创业或投资:5.构建个人品牌…...
使用 CycleGAN 进行图像到图像转换
介绍 在人工智能和计算机视觉领域,CycleGAN 是一项非凡的创新,它重新定义了我们感知和操作图像的方式。这种尖端技术彻底改变了图像到图像的转换,实现了领域之间的无缝转换,例如将马变成斑马或将夏日风景变成雪景。在本文中,我们将揭开 CycleGAN 的魔力,并探索其在各个领…...
Svg使用和注册components文件夹内部全部为全局组件
1.安装SVG依赖插件 pnpm install vite-plugin-svg-icons -D 2. 封装SvgIcon <template><div><svg :style"{ width: width, height: height }"><use :xlink:href"prefix name" :fill"color"></use></svg>…...
解决idea编辑application.yml文件或properties文件没有提示问题
注意:这里说的没有提示,是针对application.properties和application.yml文件 解决办法:在idea的插件面板中,禁用或卸载 wl Spring Assistant插件即可解决问题。...
前端懒加载
懒加载的概念 懒加载也叫做延迟加载、按需加载,指的是在长网页中延迟加载图片数据,是一种较好的网页性能优化的方式。在比较长的网页或应用中,如果图片很多,所有的图片都被加载出来,而用户只能看到可视窗口的那一部分…...
【手动配置ip地址后,电脑仍自动分配ip的问题】
现象 手动给电脑分配了一个ipv4地址,但是电脑会自动分配一个169开头的ipv4,导致虽然可以上网,但访问不了局域网内其他的设备(我配置的另一个网关,所以可以上网) 原因 ip地址冲突了,把电脑的i…...
移远RM500U-CN模块直连嵌入式ubuntu实现拨号上网
目录 1 平台: 2 需要准备的资料 3 参考文档 4 编译环境与驱动移植 4.1 内核驱动添加厂家ID和产品ID 4. 2.添加零包处理 4.3 增加复位恢复机制 4.4 增加批量输出 批量输出 URB 的数量和容量 的数量和容量 4.5 内核配置与编译 5 QM500U-CN拨号(在开…...
【JavaWeb】MySQL基础操作
1 通用语法规则 SQL语句可以单行或者多行书写,以分号结尾SQL语句不区分大小写,关键字建议使用大写单行注释 --注释内容(通用) # 注释内容(MySQL独有)多行注释 /* 注释内容 */ 2 语句 数据库 -- 查…...
【Tool】虚拟机安装与调试与设置与主机共享文件
前言 安装了vm17,实现了与主机文件共享, 步骤 下载虚拟机(试用版) Download VMware Workstation Pro 双击安装 暂不激活或者 使用如下激活码 KRNJX-22GXY-HCW46-MWYHY-YWRDB RDHTN-YFFKY-8YVR7-Q996Y-K74X3 N2XRH-GCH84-MV…...
Spring中的接口使用
技术主题 在我们的项目中,经常会使用一些注解,注解带给我们代码简洁,本质是用于在代码中添加元数据信息,从而实现更加灵活、高效和可维护的代码结构。 技术原理 注解一@Target(ElementType.TYPE) 这个注解表示被它修饰的注解可以应用在类、接口、枚举等类型上。换句话说…...
爬虫017_urllib库_get请求的quote方法_urlencode方法_---python工作笔记036
按行来看get请求方式 比如这个地址 上面这个地址复制粘贴过来以后 可以看到周杰伦变成了一堆的Unicode编码了 所以这个时候我们看,我们说https这里,用了UA反爬,所以这里 我们构建一个自定义的Request对象,里面要包含Us...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

