有限状态机实现按键防抖动电路

有限状态机实现按键防抖动电路

简要
由于金属弹性形变的原因,按键/开关在状态切换过程中总是会有或多或少的抖动情况,有时这种抖动会导致电路误动作,甚至无法正常工作。比如在设置参数时,按一下“加 1”可能会加 4~5 个数;生活中常见的情况是鼠标单击变成了双击等。因此,在很多时候,输入电路的信号需要经过防抖动处理之后才会送到后级电路。
按键抖动的电压波形如图 1 如示,tjitter 是抖动时间,通常在 1ms~30ms 之间。

图1

设计要求
用 Verilog HDL 设计一个按键防抖动电路,要求用有限状态机实现。防抖动电路的输入接开发板的拨动开关,输出接 1 位十进制计数译码显示电路的时钟输入,实现每拨动一次开关计数器加 1,多次测试不出现抖动现象。
状态定义

parameter IDLE = 4'b0001;//闲置状态
parameter key1 = 4'b0010;//按下按键存在抖动的状态
parameter key2 = 4'b0100;//按下按键的稳定状态
parameter key3 = 4'b1000;//松开按键存在抖动的状态

状态转化图
在这里插入图片描述

程序代码
顶层模块:

module TYXS(
input clk,//clk时钟信号,rst清零信号,低电平清零,key为输入信号
input rst,
input key,
output  key_out,//输出的防抖动之后的波形
output  [6:0]codeout
);
digital a(
key_out,
rst,
codeout);
key_sh b(
clk,
rst,
key,
key_out
);
endmodule 

十进制译码器显示模块

module digital(key_out,rst,codeout);
input key_out,rst;
output reg[6:0]codeout;
reg[3:0] keyout10;//寄存器用于十进制译码器显示,每按一次按键计数器加一
always@(posedge key_out,negedge rst)//十进制译码器显示,原有波形上升沿触发,即每按一次按键计数器加一
begin
  if(!rst) keyout10 <= 4'd0;
  else begin
    if(keyout10==4'd9) keyout10 <= 4'd0;//置零
	 else keyout10 <= keyout10 + 4'd1;
  end
end
always@(*)
  begin
  case(keyout10)
4'b0000: codeout<= 7'b1000000;  //0
4'b0001: codeout<= 7'b1111001;  //1
4'b0010: codeout<= 7'b0100100;  //2
4'b0011: codeout<= 7'b0110000;  //3
4'b0100: codeout<= 7'b0011001;  //4
4'b0101: codeout<= 7'b0010010;  //5
4'b0110: codeout<= 7'b0000010;  //6
4'b0111: codeout<= 7'b1111000;  //7
4'b1000: codeout<= 7'b0000000;  //8
4'b1001: codeout<= 7'b0010000;  //9

endcase
end
endmodule 

按键防抖动模块

module key_sh(
input clk,//clk时钟信号,rst清零信号,低电平清零,key为输入信号

input rst,
input key,
output reg key_out//输出的防抖动之后的波形
);
reg cnt_en;    //控制计数信号工作状态的使能信号
reg [18:0] cnt;//设置一个计数信号
reg [3:0] ns;  //nextstate,状态信号

parameter IDLE = 4'b0001;//闲置状态
parameter key1 = 4'b0010;//按下按键存在抖动的状态
parameter key2 = 4'b0100;//按下按键的稳定状态
parameter key3 = 4'b1000;//松开按键存在抖动的状态

always@(posedge clk,negedge rst)//计数器
begin
  if(!rst) cnt <= 0;
  else if(cnt_en) cnt <= cnt + 1'b1;//计数器使能为1时,开始计数,否则清零
  else cnt <= 0;
end
always@(posedge clk,negedge rst)
begin
  if(!rst) 
   begin
    ns <= IDLE;
	 cnt_en <= 0;
	 key_out <= 0;
	end
  else 
  begin
    case(ns)
	   IDLE:begin
		  key_out <= 1'b0;//初始输出波形为低电平
		  if(key==1&&cnt_en==0) 
		   begin
		    ns <= key1;//满足条件后进入下一状态key1
			 cnt_en <= 1'b1;//使能信号变为1,计数器开始工作
			end
		  else
		  ns <= IDLE;//不满足条件,回归初始状态
		end
		
		key1:begin
		  key_out <= 1'b0;//按下按键时输出波形为低电平
		  if(cnt >= 19'd50_0000&&key==1) //因为testbench中设置了时间单位为10ns时钟周期为20ns,而设置的抖动波形不超过10ms,所以这里计数50万次
		   begin
		    ns <= key2;//满足条件后进入下一状态key2,即延时了10ms
			 cnt_en <= 1'b0;//使能信号变为0,计数器停止工作且清零
			end
			else if (cnt >= 19'd50_0000&&key==0)//防止出现一按下又松开的情况
			begin
		    ns <= IDLE;//回归初始状态
			 cnt_en <= 1'b0;//使能信号变为0,计数器停止工作且清零
			end
		  else 
		  ns <= key1;//不满足计数条件则保持当前状态
		end
		
		key2:begin
		  key_out <= 1'b1;
		  if(key==0&&cnt_en==0) 
		   begin
		    ns <= key3;//满足条件后进入下一状态key3
			 cnt_en <= 1'b1;//使能信号变为1,计数器开始工作
			end
		  else 
		  ns <= key2;//不满足计数条件则保持当前状态
		end
		
		key3:begin
		  key_out <= 1'b1;
		  if(cnt >= 19'd50_0000&&key==0) 
		   begin
		    ns <= IDLE;//处理完成,回归初始状态
			 cnt_en <= 1'b0;//使能信号变为0,计数器停止工作且清零
			end
			else if (cnt >= 19'd50_0000&&key==1)//防止出现一松开又按下的情况
			begin
			 ns <= key2;//回归上一状态
			 cnt_en <= 1'b0;//使能信号变为0,计数器停止工作且清零
			end
		  else 
		  ns <= key3;//不满足计数条件则保持当前状态
		end
		default :ns <= IDLE;
	 endcase
  end
end
endmodule

仿真结果
在这里插入图片描述

热门文章

编程学习 ·

MySQL不完全干货教程(持续更新中)

已经有很多教程面面俱到、事无巨细,但实际上能用到的、消化的内容很少。本文聚焦于常见的使用场景,给出MySQL用法和基本原理说明。为便于实践和消化,同时提供了很多案例和脚本。 为了读者进一步深入学习、掌握自我升级的方法,提供了一些权威文档的参考。希望能帮助MySQL初中…
编程学习 ·

FFMPEG编译ffplay

关键就是要有SDL安装SDL(失败)yum install -y SDL-devel编译SDL2(成功) https://blog.csdn.net/quantum7/article/details/104173159编译参数# export is must use export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:${PKG_CONFIG_PATH}pkg-config --modversion ffnvcodecC…
编程学习 ·

都说Python容易自学,那如何系统的学习Python

做Python开发十年,给大家详细分析一下这个问题 首先告诉你的是,零基础学习开始系统学习Python肯定难,Python的专业程度本身就不简单,学习这事本来就是一件非常煎熬的事情,人都不愿意学习,可是没办法,为了生存掌握一个技能,你必须学,如果你认真的对待,你就找不到高薪水…
编程学习 ·

分布式数据存储系统之三要素

什么是分布式数据存储系统? 分布式存储系统的核心逻辑,就是将用户需要存储的数据根据某种规则存储到不同的机器上,当用户想要获取指定数据时,再按照规则到存储数据的机器里获取。 如下图所示,当用户(即应用程序)想要访问数据 D,分布式操作引擎通过一些映射方式,比如 H…
编程学习 ·

Java并发编程之深入理解volatile

个人博客请访问 http://www.x0100.top 1. 保证可见性volatile保证了不同线程对volatile修饰变量进行操作时的可见性。对一个volatile变量的读,(任意线程)总是能看到对这个volatile变量最后的写入。一个线程修改volatile变量的值时,该变量的新值会立即刷新到主内存中,…
编程学习 ·

Spring依赖注入:@Autowired,@Resource和@Inject区别与实现原理

注入实现方式@Autowired是spring框架提供的实现依赖注入的注解,主要支持在set方法,field,构造函数中完成bean注入,注入方式为通过类型查找bean,即byType的,如果存在多个同一类型的bean,则使用@Qualifier来指定注入哪个beanName的bean。与JDK的@Resource的区别:@Resourc…
编程学习 ·

UGUI获取自适应UI元素的宽高

对于使用了layout的布局元素来说,并不能直接通过rectTransfrom来获取搞元素的weight和height 不过Unity中有对应API可以帮助我们获取 通过LayoutUtility中的静态方法我们可以获取对应的一些信息 GetFlexibleHeight 返回布局元素的灵活高度。GetFlexibleSize 返回布局元素的灵活…
编程学习 ·

MyBatis 结构拆解

MyBatis 的执行流程大概可以拆分为如下几个部分:初始化配置解析 mybatis-config.xml 文件 根据 mybatis-config.xml 文件中的配置,依次解析 Mapper.xml 文件 将 Mapper.xml 与 接口 通过 xml 文件的 namespace 属性来进行绑定**【重点】**;该篇有介绍 XML 文件和 接口进行绑…
编程学习 ·

css少见的标签

font-smoothing抗锯齿-moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased;-webkit-font-smoothing它有三个属性值:none:对低像素的文本比较好subpixel-antialiased:默认值antialiased:抗锯齿很好解决光标颜色和文字颜色一致@supports (-webkit-mask…
编程学习 ·

Metasploit使用msfcli命令行接口编写shell脚本程序

问题描述使用msfcli命令行接口编写一个Shell脚本程序,实现用户只需输入目标Linux靶机IP地址作为参数,就可以使用usermap_script漏洞渗透攻击模块,获得靶机的远程Shell访问。 解决办法由于2005年时便取消了msfcli,因此使用msfconsole -x来代替msfcli。先提供脚本内容如下: …
编程学习 ·

数据库导出到excel解决科学计数法问题

用Navicat等工具导出数据到excel的时候,身份证等超过11位的数字会自动转换成科学计数法,末尾数字变成“0000”。如何解决?解决方式:给超过11位的数字末尾添加 \t查询的时候,给相关字段添加 \tSELECT name,CONCAT(idcard,\t) from lm_reg然后再将查询结果导出到excel。如…
编程学习 ·

esp8266 system_partition_table_regist fail 蓝灯闪一下就灭

在添加了如下函数后,蓝灯闪一下就灭,并且串口打印system_partition_table_regist fail: void ICACHE_FLASH_ATTR user_pre_init(void) {if(!system_partition_table_regist(at_partition_table, sizeof(at_partition_table)/sizeof(at_partition_table[0]),SPI_FLASH_SIZE_M…
编程学习 ·

从word中复制内容包含图片到百度ueditor编辑器中

1.4.2之后官方并没有做功能的改动,1.4.2在word复制这块没有bug,其他版本会出现手动无法转存的情况本文使用的后台是Java。前端为Jsp(前端都一样,后台如果语言不通得自己做 Base64编码解码)因为公司业务需要支持IE8 ,网上其实有很多富文本框,效果都很好。例如www.wangEdi…
编程学习 ·

centos自用命令备份

上传 scp -p E:\abc\requirement.txt root@132.232.10.218:/root/stock 下载 scp root@103.51.15.130:/root/project/log/2020-01-07-12-22-15.log D:\ ----------------------------------------- 启动多个脚本 python3 zmq.py & python3 open.py & python3 clos.py &…
编程学习 ·

《ES6模块化》知识点总结

以下内容纯属个人扯淡,仅供参考目录一、概述二、基本语法一、概述1、传统开发模式的问题命名冲突:多个js文件之间,不能存在同名的变量 文件依赖:js文件之间无法实现相互引用2、模块化1。概述将单独的一个功能封装到一个模块文件中,模块之间相互隔离,但可通过特定的接口公…
编程学习 ·

Talent Challenge Program:专属在校大学生的远程实习培养计划来啦!

2020 开年的一场疫情,打得我们措手不及,往年一直幻想着可以有一个不用开学的假期,现在开学也变得遥遥无期,原本准备的暑期实习也因为不能到现场,一个个泡汤。这个暑假,还能做点什么才不虚度光阴? 不如了解一下“远程实习”——面向在校大学生的 Talent Challenge Progra…
编程学习 ·

【原创】软件开发之设计模式分类

按照设计模式的目的进行划分:类别内容举例创建型模式通过抽象类所定义的接口,封装了系统中对象如何创建、组合等信息其代表有Singleton模式等结构型模式主要用于如何组合自己有的类和对象以获得更大的结构其代表有Adapter模式等行为型模式主要用于对象之间的职责及其提供服务…
编程学习 ·

HDFS中将普通用户加入到supergroup组来访问HDFS

本机是linux系统,使用远程的hadoop。程序直接访问hdfs://node1:8020 会有权限问题。比较简单的解决粗暴方式是把用户加入到supergroup组。Hadoop本身的用户和组的关系,是同步Linux系统中的用户权限,但是HDFS和Linux的超级用户组又有一点差别,HDFS中的超级用户组是supergrou…
编程学习 ·

VLP16--通过模拟GPSPPS与GPRMC信号同步时钟

1.VLP16与GPS相关的管脚:GPS-RECEIVE 接收GPS的GPRMC语句,注意是RS232电平(high 3-15V,low 1.2V以下),如TTL电平输出需要进行信号反转,没有RS232芯片的可以使用反相电路。 GPS PULSE 接收GPS的PPS信号,上升沿触发持续10us-200ms,与下一个PPS要大于300ms以上GROUND 信号地…