状态机实现N位按键消抖

news2024/11/18 15:24:21

状态机实现N位按键消抖

1、原理

利用状态机实现按键的消抖,具体的原理可参考

(50条消息) 基于FPGA的按键消抖_fpga 按键消抖_辣子鸡味的橘子的博客-CSDN博客

状态机简介:

状态机分类可以主要分为两类:moore和mealy

根据三段式状态机最后一段的组合逻辑,根据状态机的输出是否与输出条件有关可以用来区分moore状态机和mealy状态机

若输出只与当前状态机有关,则为moore状态机

 always @*
    begin
        if(current_state == s4) dout = 1;
        else dout = 0;
    end
 

Moore状态机仅仅和当前状态有关

img

Mealy状态机:输出不仅取决于当前状态,还和输入有关;

同样是三段式描述,最后的输出为:

always @(*)
	begin
		if(reset) dout = 1'b0;
		else if( (current_state == s3)&&(din == 1'b1) ) dout = 1'b1;
		else dout = 1'b0;
	end
    

img

可见,输出不仅和当前状态和输入都有关系。

最后,Moore状态机和Mealy状态机可以相互转换。上述两个状态转移图实际上实现的是同一个功能,就是检测序列1101.

状态机按照段式分类,可分为:一段式、二段式、三段式

可参考:

(50条消息) 状态机详解(一段式、二段式、三段式)_状态机一段式二段式三段式_CuteBaBaKiller的博客-CSDN博客

image-20230727171608721

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) begin
    if(~rst_n)begin
      	key_r0 <= {N{1'b1}};
      	key_r1 <= {N{1'b1}};
      	key_r2 <= {N{1'b1}};
    end
    else begin
        key_r0<=key_in;
        key_r1<=key_r0;
        key_r2<=key_r1;
    end
end

//****************************************************************
//--"flag信号约束
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
      	flag<=1'b0;
    end
    else if(nedge || podge)begin
        flag<=1;
    end
    else if(end_cnt_20ms)begin
    	flag<=0;
    end
    else begin
    	flag<=flag;
    end
end
//****************************************************************
//--"20ms计数
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
       cnt_20ms<='d0;
    end
    else if(add_cnt_20ms)begin
        if(end_cnt_20ms)begin
            cnt_20ms <='d0;
        end
        else if(nedge)begin
            cnt_20ms <= 0;
        end
        else begin
        	cnt_20ms <= cnt_20ms + 1'b1;
        end
    end
    else begin
       cnt_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) begin
    if(!rst_n)begin
        cstate<=IDLE;//初始当前状态为空闲
    end
    else begin
        cstate<=nstate;//次态赋值给现态
    end
end


//****************************************************************
//--"三段式状态机,第二段,组合逻辑,状态转移
//****************************************************************
always @(*) begin
    case(cstate)
        IDLE:begin
        	if(idle2filter_down)begin
        		nstate = FILTER_DOWN;
        	end
        	else begin
        		nstate = cstate;
        	end
        end
        FILTER_DOWN:
        begin
        	if(filter_down2down)begin
        	    nstate = DOWN;		
        	end
        	else begin
        	    nstate = cstate;	
        	end    
        end
        		
        DOWN:begin
        	if(down2filter_up)begin
        	    nstate = FILTER_UP;	
        	end
        	else begin
        	    nstate = cstate;		
        	end
        end

        FILTER_UP:begin
        	if(filter_up2idle)begin
        	    nstate = IDLE;		
        	end
        	else begin
        	    nstate = cstate;		
        	end
        end
        default:
            nstate = cstate;
    endcase
end

//****************************************************************
//--"有限状态机,第三段,时序逻辑
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
       	key_out_r<={N{1'b0}};
    end
    else if(filter_down2down)begin
        key_out_r<=~key_r1;
    end
    else begin
        key_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;
	begin
		clk=1'b0;
		rst_n=1'b0;
		#(2*SYS_CLK);
		rst_n=1'b1;
		key = 4'b1111;
		#(2*SYS_CLK);
	end
endtask

task task_key;
	input[3:0] key_in;
	output[3:0] key_out;
	begin
		key_out[0] = ~key_in[0];
		key_out[2] = ~key_in[2];
		key_out[3] = key_in[3];
		key_out[1] = key_in[1];
	end
endtask

initial begin
	task_init();

	repeat(10)begin
		repeat (20) begin
			task_key(key,key);
	   		// key[0] = ~key[0];
	   		// key[2] = ~key[2];
	   		delay = {$random()}%4;
	   		#(SYS_CLK*delay);
		end
		task_key(key,key);
		// key[0] = ~key[0];
		// key[2] = ~key[2];
		//wait(inst_fsm_key.end_cnt_20ms);
		#(30*SYS_CLK);
	end
	$stop;
end

fsm_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、仿真结果

image-20230727171418129

5、总结

使用状态机进行按键消抖,可以经消抖分为四个部分,空闲状态、下降沿状态、按下状态、上升沿状态,这几个状态使用状态机进行按键消抖,可以更好的理解消抖的原理和过程。状态机的规范编写也是提升自己理解时序,理解逻辑的好的方式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/799800.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《MySQL45讲》笔记—一条SQL查询语句是如何执行的、一条SQL更新语句是如何执行的

整体架构 server层包括连接器、查询缓存、分析器、优化器、执行器&#xff1b;存储引擎层负责数据的存储和提取&#xff0c;支持InnoDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是InnoDB&#xff0c;它从MySQL 5.5.5版本开始成为了默认存储引擎&#xff0c;如果在…

python-16-线程池和进程池python并发编程

Python ThreadPoolExecutor线程池 线程池的基本原理是什么&#xff1f; 利用Python快速实现一个线程池&#xff0c;非常简单 Python并发编程专题 1 并发编程 1.1 并发编程概念 一、为什么要引入并发编程&#xff1f; 场景1&#xff1a;一个网络爬虫&#xff0c;按顺序爬取花…

在Windows server 2012上使用virtualBox运行CentOS7虚拟机,被强制休眠(二)

问题场景 本月7月10日处理了一个虚拟机被强制暂停的问题&#xff0c;详见&#xff1a;在Windows server 2012上使用virtualBox运行CentOS7虚拟机&#xff0c;被强制暂停当时是由于C盘存储空间不足&#xff0c;导致虚拟机被强制暂停&#xff0c;将虚拟机迁移后&#xff0c;问题…

哈工大计算机网络课程局域网详解之:交换机概念

哈工大计算机网络课程局域网详解之&#xff1a;交换机概念 文章目录 哈工大计算机网络课程局域网详解之&#xff1a;交换机概念以太网交换机&#xff08;switch&#xff09;交换机&#xff1a;多端口间同时传输交换机转发表&#xff1a;交换表交换机&#xff1a;自学习交换机互…

Spire.Office for.NET Crack

Spire.Office for.NET Crack Spire.Office for.NET是E-iceblue提供的企业级Office.NET API的组合。它包括Spire.Doc、Spire.XLS、Spire.Seadsheet、Spire.Presentation、Spire_PDF、Spire.DataExport、SpireOfficeViewer、Spire-PDFViewer、Spire.DocViewer、Spire.Barcode和Sp…

模板(简单介绍C++)

模板 引入模板函数模板概念语法函数模板的原理函数模板实列化隐式实例化显示实例化 模板参数匹配原则 类模板类模板的定义格式类模板的实列化 泛型编程补充知识 引入模板 当我们想实现一个通用的交换函数时&#xff0c;我们能否告诉编译器一个模子&#xff0c;让编译器根据不同…

基于Java+SpringBoot+vue前后端分离墙绘产品展示交易平台设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Ansible 自动化运维

目录 ansible 环境安装部署ansible 命令行模块inventory 主机清单 Ansible是一个基于Python开发的配置管理和应用部署工具&#xff0c;现在也在自动化管理领域大放异彩。它融合了众多老牌运维工具的优点&#xff0c;Pubbet和Saltstack能实现的功能&#xff0c;Ansible基本上都可…

MySQL 之 Buffer Pool

一、innoDB设计缓冲池目的 避免频繁访问磁盘&#xff0c;提高数据库读写性能。&#xff08;作用与引用Cache三级缓存类似。&#xff09; 二、缓冲池工作模式 读取数据&#xff1a;当Buffer Pool存在目标数据&#xff0c;就直接返回给客户端&#xff0c;没有再磁盘取数据。 修改…

1-高性能计算研究

高性能计算研究 E级计算机系统研制高性能计算应用软件研发并行编程框架应用协同开发优化平台和工具软件示例 高性能计算环境研发 E级计算机系统研制 高性能互联计算、编程、运行模型 应用驱动的新型可扩展基础算法&#xff08;适用于E级计算的可计算物理建模与新型计算方法&a…

softmax回归

模型 softmax回归是多类分类模型&#xff0c;用于获取每个分类的置信度&#xff0c;置信度计算方式如下 经过全连接层&#xff0c;得到输出O&#xff0c;将O作为softmax的输入 O是输出向量&#xff0c;每个分量表示一个类别&#xff0c;y_hat_i表示i类别的置信度&#xff0…

[语义分割] DeepLab v1网络(语义分割、信号下采样、空间上的不敏感性、LargeFOV、膨胀卷积、空洞卷积、MSc、Multi-Scale)

Semantic Image Segmentation with Deep Convolutional Nets and Fully Connected CRFs 论文地址&#xff1a;Semantic Image Segmentation with Deep Convolutional Nets and Fully Connected CRFs参考源码&#xff1a;https://github.com/TheLegendAli/DeepLab-Context DeepL…

数据库中的Hash索引以及哈希碰撞

hash索引&#xff0c;就是用过一定的hash算法&#xff0c;将键值换算成新的hash值&#xff0c;映射到对应的槽位上&#xff0c;然后存储在hash表中 就比如下面的name字段&#xff0c;经过算法的分析&#xff0c;就会对应出一张hash表 如果我的两个name字段计算出来的key相同&a…

vscode eslint配置

1. 全局安装 eslint npm install -g eslint 2. control shift p 输入 settings 打开设置进行配置 3. 添加配置 {"workbench.colorTheme": "One Dark Pro","eslint.debug": true,"eslint.execArgv": null,"eslint.alwaysShow…

多表关联查询

十七、多表关联查询 但是开发中不会使用联合主键&#xff0c;还是只会使用一个主键 多表关联查询&#xff1a; 建表&#xff1a; 交叉连接 …… …… 重复时写清楚是哪个表的&#xff0c;不然会报错&#xff1a; 2、内连接 王妍没有部门&#xff0c;查不出来。此时需要用到外连…

Python 进阶(一):PyCharm 下载、安装和使用

❤️ 博客主页&#xff1a;水滴技术 &#x1f338; 订阅专栏&#xff1a;Python 入门核心技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; 文章目录 一、下载 PyCharm二、安装 PyCharm三、创建项目四、界面汉化五、实用技巧5.1、使用快捷…

SpringMvc+Mybatis完整项目

0目录 1.SpringmybatisSpringmvc查询功能&#xff08;记录数&#xff09; 2.查询所有 3.增删改查&#xff08;根据id&#xff09; 4.增加用户注册登录功能 1.SpringmybatisSpringmvc增删改查 新建数据库 创建工程 配置web.xml 配置applicationContext.xml 实体类 My…

24考研数据结构-栈

目录 第三章 栈和队列3.1栈&#xff08;stack&#xff09;3.1.1栈的基本概念栈的基本概念知识回顾 3.1.2 栈的顺序存储上溢与下溢栈的顺序存储知识回顾 3.1.3栈的链式存储链栈的基本操作 第三章 栈和队列 3.1栈&#xff08;stack&#xff09; 3.1.1栈的基本概念 栈的定义 栈…

主流开源监控系统一览

减少故障有两个层面的意思&#xff0c;一个是做好常态预防&#xff0c;不让故障发生&#xff1b;另一个是如果故障发生&#xff0c;要能尽快止损&#xff0c;减少故障时长。而监控的典型作用&#xff0c;就是帮助我们发现及定位故障&#xff0c;这两个环节对于减少故障时长至关…

Docker挂载目录失败问题解决

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…