状态机实现N位按键消抖
状态机实现N位按键消抖
1、原理
利用状态机实现按键的消抖,具体的原理可参考
(50条消息) 基于FPGA的按键消抖_fpga 按键消抖_辣子鸡味的橘子的博客-CSDN博客
状态机简介:
状态机分类可以主要分为两类:moore和mealy
根据三段式状态机最后一段的组合逻辑,根据状态机的输出是否与输出条件有关可以用来区分moore状态机和mealy状态机
若输出只与当前状态机有关,则为moore状态机
always @*beginif(current_state == s4) dout = 1;else dout = 0;end
Moore状态机仅仅和当前状态有关

Mealy状态机:输出不仅取决于当前状态,还和输入有关;
同样是三段式描述,最后的输出为:
always @(*)beginif(reset) dout = 1'b0;else if( (current_state == s3)&&(din == 1'b1) ) dout = 1'b1;else dout = 1'b0;end

可见,输出不仅和当前状态和输入都有关系。
最后,Moore状态机和Mealy状态机可以相互转换。上述两个状态转移图实际上实现的是同一个功能,就是检测序列1101.
状态机按照段式分类,可分为:一段式、二段式、三段式
可参考:
(50条消息) 状态机详解(一段式、二段式、三段式)_状态机一段式二段式三段式_CuteBaBaKiller的博客-CSDN博客

2、代码
module fsm_key_n#(parameter N = 4,parameter TIME_20MS = 1000_000)(input wire clk,input wire rst_n,input wire[N-1:0] key_in,output wire[N-1:0] key_out
);
reg[3:0] key_out_r;//中间信号
reg[24:0] cnt_20ms;//20ms计数器
//状态空间
parameter IDLE = 4'b0001,FILTER_DOWN = 4'b0010,DOWN = 4'b0100,FILTER_UP = 4'b1000;reg[3:0] cstate;//现态
reg[3:0] nstate;//次态
reg[N-1:0] key_r0,key_r1,key_r2;//按键延时
reg flag;//检测下降沿和上升沿,寄存
//****************************************************************
//--状态转移条件定义
//****************************************************************
wire idle2filter_down;
wire filter_down2down;
wire down2filter_up;
wire filter_up2idle;
//****************************************************************
//--"计时开始结束条件
//****************************************************************
wire add_cnt_20ms;
wire end_cnt_20ms;//****************************************************************
//--下降沿上升沿检测
//****************************************************************
assign nedge = |(~key_r1&key_r2);
assign podge = |(key_r1&key_r2);
//****************************************************************
//--状态转移条件约束
//****************************************************************
assign idle2filter_down = nedge && cstate == IDLE;
assign filter_down2down = end_cnt_20ms && cstate == FILTER_DOWN;
assign down2filter_up = podge && cstate == DOWN;
assign filter_up2idle = end_cnt_20ms && cstate == FILTER_UP;//****************************************************************
//--"信号延时
//****************************************************************
always @(posedge clk or negedge rst_n) beginif(~rst_n)beginkey_r0 <= {N{1'b1}};key_r1 <= {N{1'b1}};key_r2 <= {N{1'b1}};endelse beginkey_r0<=key_in;key_r1<=key_r0;key_r2<=key_r1;end
end//****************************************************************
//--"flag信号约束
//****************************************************************
always @(posedge clk or negedge rst_n) beginif(~rst_n)beginflag<=1'b0;endelse if(nedge || podge)beginflag<=1;endelse if(end_cnt_20ms)beginflag<=0;endelse beginflag<=flag;end
end
//****************************************************************
//--"20ms计数
//****************************************************************
always @(posedge clk or negedge rst_n) beginif(~rst_n) begincnt_20ms<='d0;endelse if(add_cnt_20ms)beginif(end_cnt_20ms)begincnt_20ms <='d0;endelse if(nedge)begincnt_20ms <= 0;endelse begincnt_20ms <= cnt_20ms + 1'b1;endendelse begincnt_20ms<=cnt_20ms;end
end
//****************************************************************
//--"20ms计数条件约束
//****************************************************************
assign add_cnt_20ms = flag;
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == TIME_20MS - 1;//****************************************************************
//--"三段式状态机,第一段,时序逻辑
//****************************************************************
always @(posedge clk or negedge rst_n) beginif(!rst_n)begincstate<=IDLE;//初始当前状态为空闲endelse begincstate<=nstate;//次态赋值给现态end
end//****************************************************************
//--"三段式状态机,第二段,组合逻辑,状态转移
//****************************************************************
always @(*) begincase(cstate)IDLE:beginif(idle2filter_down)beginnstate = FILTER_DOWN;endelse beginnstate = cstate;endendFILTER_DOWN:beginif(filter_down2down)beginnstate = DOWN; endelse beginnstate = cstate; end endDOWN:beginif(down2filter_up)beginnstate = FILTER_UP; endelse beginnstate = cstate; endendFILTER_UP:beginif(filter_up2idle)beginnstate = IDLE; endelse beginnstate = cstate; endenddefault:nstate = cstate;endcase
end//****************************************************************
//--"有限状态机,第三段,时序逻辑
//****************************************************************
always @(posedge clk or negedge rst_n) beginif(~rst_n) beginkey_out_r<={N{1'b0}};endelse if(filter_down2down)beginkey_out_r<=~key_r1;endelse beginkey_out_r<={N{1'b0}};end
end
assign key_out = key_out_r;endmodule
3、仿真代码
`timescale 1ns/1ns
module fsm_key_tb();reg clk;
reg rst_n;
reg[3:0] key;
reg[3:0] delay;wire[3:0] key_r;
parameter SYS_CLK = 20;
parameter TIME_20MS = 10;
parameter N = 4;
always #(SYS_CLK/2) clk = ~clk;task task_init;beginclk=1'b0;rst_n=1'b0;#(2*SYS_CLK);rst_n=1'b1;key = 4'b1111;#(2*SYS_CLK);end
endtasktask task_key;input[3:0] key_in;output[3:0] key_out;beginkey_out[0] = ~key_in[0];key_out[2] = ~key_in[2];key_out[3] = key_in[3];key_out[1] = key_in[1];end
endtaskinitial begintask_init();repeat(10)beginrepeat (20) begintask_key(key,key);// key[0] = ~key[0];// key[2] = ~key[2];delay = {$random()}%4;#(SYS_CLK*delay);endtask_key(key,key);// key[0] = ~key[0];// key[2] = ~key[2];//wait(inst_fsm_key.end_cnt_20ms);#(30*SYS_CLK);end$stop;
endfsm_key_n #(.N(N),.TIME_20MS(TIME_20MS)) inst_fsm_key (.clk (clk),.rst_n (rst_n),.key_in (key),.key_out (key_r));endmodule
4、仿真结果

5、总结
使用状态机进行按键消抖,可以经消抖分为四个部分,空闲状态、下降沿状态、按下状态、上升沿状态,这几个状态使用状态机进行按键消抖,可以更好的理解消抖的原理和过程。状态机的规范编写也是提升自己理解时序,理解逻辑的好的方式
相关文章:
状态机实现N位按键消抖
状态机实现N位按键消抖 1、原理 利用状态机实现按键的消抖,具体的原理可参考 (50条消息) 基于FPGA的按键消抖_fpga 按键消抖_辣子鸡味的橘子的博客-CSDN博客 状态机简介: 状态机分类可以主要分为两类:moore和mealy 根据三段式状态机最后…...
uniapp自定义消息语音
需求是后端推送的消息APP要响自定义语音,利用官方插件,总结下整体流程 uniapp后台配置 因为2.0只支持uniapp自己的后台发送消息,所以要自己的后台发送消息只能用1.0 插件地址和代码 插件地址: link let isIos (plus.os.name "iOS&qu…...
k8s安装Jenkins
目录 编辑 一、环境准备 1.1 环境说明 二、安装nfs 2.1 安装NFS 2.2 创建NFS共享文件夹 2.3 配置共享文件夹 2.4 使配置生效 2.5 查看所有共享目录 2.6 启动nfs 2.7 其他节点安装nfs-utils 三、创建PVC卷 3.1 创建namespace 3.2 创建nfs 客户端sa授权 3.3 创建…...
共筑开源新长城 龙蜥社区走进开放原子校源行-清华大学站
6 月 28 日,以“聚缘于校,开源共行”为主题的 2023 年开放原子校源行活动在清华大学成功举行。本次活动由开放原子开源基金会和清华大学共同主办,来自各行业的 22 位大咖共聚校园共话开源。龙蜥社区技术专家边子政受邀进行技术分享࿰…...
Jgit 工具类 (代码检出、删除分支(本地、远程)、新建分支、切换分支、代码提交)
https://blog.csdn.net/qq_37203082/article/details/120327084 Jgit 工具类 (代码检出、删除分支(本地、远程)、新建分支、切换分支、代码提交)_jgit删除远程分支_CJ点的博客-CSDN博客 <!--JAVA操作GIT--><dependency><groupId>org.…...
什么是redux?如何在react 项目中使用redux?
redux 概念 redux是一种用于管理JavaScript应用程序的状态管理库。它可以与React、Augular、Vue等前端框架结合使用,但也可以纯在JavaScript应用程序中独立使用。redux遵循单项数据流的原则,通过一个全局的状态树来管理应用程序的状态,从而使…...
mysql的json处理
写在前面 需要注意,5.7以上版本才支持,但如果是生产环境需要使用的话,尽量使用8.0版本,因为8.0版本对json处理做了比较大的性能优化。你你可以使用select version();来查看版本信息。 本文看下MySQL的json处理。在正式开始让我们先…...
前端学习——Vue (Day8)
Vue3 create-vue搭建Vue3项目 注意要使用nodejs16.0版本以上,windows升级node可以西安使用where node查看本地node位置,然后到官网下载msi文件,在本地路径下安装即可 安装完可以使用node -v检查版本信息 项目目录和关键文件 组合式API - s…...
Windows环境下安装及部署Nginx
一、安装Nginx教程 1、官网下载地址:https://nginx.org/en/download.html 2、下载教程:选择Stable version版本下载到本地 3、下载完成后,解压放入本地非中文的文件夹中: 4、启动nginx:双击nginx.exe,若双击…...
使用AOP切面对返回的数据进行脱敏的问题
1.注解类 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/*** Author: xiaoxin* Date: 2023/7/21 17:15*/ Retention(RetentionPolicy.RUNTIME) Targe…...
TDengine时区设置
一般来说,时序数据就是带有时间序列属性的数据。在处理时序数据时,TDengine有着自己独特的方式。但是如果没有正确理解TDengine在写入和查询上的行为,极可能会因为配置了错误的时区(timezone),而导致写入和…...
站外引流效果差?一文带你搞懂解海外主流社交媒体算法!
在流量成本越来越高的当下,无论是平台卖家还是独立站卖家都在努力拓展流量渠道。站外引流是推动业务增长的关键策略,很多卖家会把重点放在内容营销上,但其实除了做好内容之前,了解社交媒体的算法才能让营销效果最大化。 01.Faceb…...
css 动画之旋转视差
序:网上看到的一个例子,做一下 效果图: 代码: <style>.content{width: 300px;height: 300px;margin: 139px auto;display: grid;grid-template-columns: repeat(3,1fr);grid-template-rows: repeat(3,1fr);grid-template:…...
maven项目、springboot项目复制文件进来后没反应、不编译解决方法
问题如下 把文件复制进springboot项目后,没反应,不编译。 解决 在maven工具框中选择compile工具,运行即可。...
android jetpack App Startup 应用启动时初始化组件(java)
有什么用? 应用启动时初始化组件。 怎么用 添加依赖 dependencies {implementation "androidx.startup:startup-runtime:1.1.1" }创建类,继承Initializer。 public class AppInit implements Initializer<String> {NonNullOverride…...
【设计模式|行为型】命令模式(Command Pattern)
说明 命令模式(Command Pattern)是一种行为设计模式,它将请求封装为一个对象,以便在不同的请求者和接收者之间进行解耦、参数化和操作的队列化。命令模式允许你将具体的请求封装为对象,这些对象之间彼此独立ÿ…...
SqlServer 批量删除表
SqlServer 批量删除表 直接上SQL脚本吧 SELECT row_number()over(order by Name) as FID,Name into #temp FROM SysObjects Where XTypeU --类型,U为实体表 and name like TMP% --表名过滤(自定义就好) ORDER BY Namedeclare count int 0…...
[Linux]线程基本知识
概念 进程 一个正在执行的程序,它是资源分配的最小单位 进程中的事情需要按照一定的顺序逐个进行 进程出现了很多弊端: 一是由于进程是资源拥有者,创建、撤消与切换存在较大的时空开销,因此需要引入轻型进程; 二是由于对称多…...
STM32 串口基础知识学习
串行/并行通信 串行通信:数据逐位按顺序依次传输。 并行通信:数据各位通过多条线同时传输。 对比 传输速率:串行通信较低,并行通信较高。抗干扰能力:串行通信较强,并行通信较弱。通信距离:串…...
页面滚动时隐藏element-ui下拉框/时间弹框
场景 在系统中,当(有垂直滚动时)点击下拉框后滚动页面,会发现下拉项会遮盖住页面中的元素,不会隐藏 解决:(以vue为例) 在页面滚动或者缩放时隐藏下拉项即可(借助点击目标元素,下…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...
