FPGA开发——按键的使用及其消抖方法

news2024/9/20 16:24:56

一、概述

我们在进行各种硬件开发时通常都会实现多效果的综合和实现,而在实际的开发中,每个时刻只会显示单个效果,这就需要涉及到效果之间的切换了,而要实现状态切换最好的就是使用按键进行按键,所以按键在我们的日常硬件开发中占据很大的作用。

二、相关理论

我们在使用按键之前,首先应该考虑到的就是按键的抖动的问题,所以在使用按键的过程中按键消抖的成功与否是确保最终结果的最关键的过程。以下是按键抖动过程中波形的具体示意图:

三、实现方法

在FPGA的按键使用过程中我们有两种方法对按键进行消抖,一种是直接采用计数器搭配打拍的方式进行按键的消抖,最后使用一个输出电平标志按键消抖的完成。另外一种是采用状态机的方式进行按键消抖的实现。我们这里分别从两种实现方法进行讨论。

1、计数器的方法

基本思路

最初波形图构造

其中的key_in就是按键的状态,代表按键按下与否,key_in_r是key_in打一拍之后的波形图,与key_in相比相位相差了一个周期。nedge就是下降沿触发的标志,也就是按键抖动的标志位,key_out是输出一个电平代表按键消抖成功。

2、状态机的方法

状态机的实现方法就是在计数器的转移条件之间增加了下降沿以及计数器进行搭配检测,实现状态机状态之间的转移 ,从而达到按键消抖的目的

最初波形图构造

这里就是比前面计数器多了一个现态和次态,以及上升沿标志,将整个按键从按下到释放划分成四个状态,通过各个条件转换成状态机状态转移的条件,通过状态的切换达到按键消抖。

四、代码编写

1、计数器实现

设计文件

//对一位按键输入进行消抖,最终输出消抖过后的按键信号,输出一个周期高电平表示按键按下一次
module key(
    input clk,
    input rst_n,
    input key_in,
    output reg key_out
);
//参数定义
parameter  TIME_20MS = 1_000_000;

//内部信号定义
reg              key_in_r;
reg   [23:0]     cnt;
wire             add_cnt;
wire             end_cnt;
wire             nedge;
reg              add_flag;


//检测抖动,对信号打一拍
always @(posedge clk or negedge rst_n )begin
    if(!rst_n)
        key_in_r<=1'b1;
    else
        key_in_r<=key_in;
end 
assign nedge = !key_in && key_in_r;

//从抖动的地方开始计数,这里实现计数标志位的书写
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
       add_flag<=0;
    else if(nedge)
        add_flag<=1;
    else if(end_cnt)
        add_flag<=0;
    else
        add_flag<=add_flag;
end 

//开始计数,在计数结束时采样点平
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)
        cnt<=0;
    else if(add_cnt)begin
        if(end_cnt)
            cnt<=0;
        else
            cnt<=cnt+1;
    end 
    else
        cnt<=0;
end 
assign add_cnt=add_flag;
assign end_cnt= (add_cnt) && (cnt==TIME_20MS-1);

//电平输出
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)
        key_out <= 0;
    else if(end_cnt)
        key_out <= 1'b1;
    else
        key_out <= 0;
end 
endmodule 

2、状态机实现

//状态机实现
module key (
    input           clk     ,
    input           rst_n   ,
    input        [   key_in  ,   //输入原始的按键信号
    output  reg     key_out     //输出处理之后的按键信号
);
//参数定义
    localparam  IDLE        = 4'b0001,//空闲
                JITTLE0     = 4'b0010,//滤除第一次抖动
                DOWN        = 4'b0100,//稳定
                JITTLE1     = 4'b1000;//滤除第二次抖动

    parameter   TIME_20MS = 1_000_000;//需要计数的值,20ms

//内部信号
    reg  [3:0]   state_c;//现态
    reg  [3:0]   state_n;//次态

    reg  [19:0] cnt_20ms;//计数20ms
    wire        add_cnt_20ms;
    wire        end_cnt_20ms;

    wire        nedge   ;//下降沿信号
    wire        pedge   ;//上升沿信号
    reg         key_in_r1  ;//打两拍  同步打拍
    reg         key_in_r2  ;


//状态转移 同步时序逻辑描述状态转移
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            state_c <= IDLE;
        else
            state_c <= state_n;
    end

//状态转移条件 组合逻辑
    always @(*) begin
        case (state_c)//一定是case  现态
            IDLE:begin
                if(nedge)
                    state_n = JITTLE0;
                else
                    state_n = state_c;
            end
            JITTLE0:begin
                if(end_cnt_20ms)
                    state_n = DOWN;
                else
                    state_n = state_c; 
            end
            DOWN:begin
                if(pedge)
                    state_n = JITTLE1;
                else
                    state_n = state_c;
            end
            JITTLE1  :begin
                if(end_cnt_20ms)
                    state_n = IDLE;
                else
                    state_n = state_c;  
            end   
            default: state_n = IDLE;
        endcase
    end

//20ms计数器
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        cnt_20ms <= 0;
    else if(add_cnt_20ms)begin
        if(end_cnt_20ms)
            cnt_20ms <= 0;
        else
            cnt_20ms <= cnt_20ms + 1;
    end
end

assign add_cnt_20ms = (state_c == JITTLE0) || (state_c == JITTLE1);
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == TIME_20MS - 1;

//下降沿  上升沿
//同步  打拍
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        key_in_r1 <= 1'b1;
        key_in_r2 <= 1'b1;
       
    end 
    else begin 
        key_in_r1 <= key_in;      //同步按键输入信号
        key_in_r2 <= key_in_r1;   //打拍
         
    end 
end
    //r1当前状态,r2上一个状态
assign nedge = ~key_in_r1 && key_in_r2;
assign pedge = key_in_r1 && ~key_in_r2;

//key_out
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            key_out <= 0;
        else if(end_cnt_20ms &&(state_c== JITTLE1))
            key_out <= 1'b1;//有效脉冲 20ns
        else 
            key_out <= 0;
    end

endmodule

3、测试文件

因为两种方法所需要实现的效果是一致的,所以这里我们采用同一个测试文件。

`timescale 1ns/1ns

module key_tb ;

    reg         clk     ;
    reg         rst_n   ;
    reg         key_in  ;
    wire        key_out ;

defparam key_inst.TIME_20MS = 1000;

key key_inst(
    .clk            (clk        ),
    .rst_n          (rst_n      ),
    .key_in         (key_in     ),   //输入原始的按键信号
    .key_out        (key_out    )    //输出处理之后的按键信号
);

//激励信号产生
parameter CLK_CLY = 20;
//时钟
initial clk=1;
always #(CLK_CLY/2)clk=~clk;

//复位
initial begin
    rst_n= 1'b0;
    #(CLK_CLY*3);
    #5;//复位结束避开时钟上升沿
    rst_n= 1'b1;
end

//激励
integer i;
initial repeat(5)begin
    key_in = 1;//模拟按键未按下
    i ={$random}%6;//给i赋值0-5
    #(CLK_CLY*500);//等待复位时间结束
    #3;
    repeat (3)begin 
        key_in = 0;//前按键抖动开始
        #(CLK_CLY*1);
        //一个5-10ms的抖动时间
        repeat ((i+5)*50)begin
            key_in = $random;
            #(CLK_CLY*1);
        end 
        key_in = 0;//按键稳定
        #(CLK_CLY*100*50);

        //后抖动开始
        key_in = 1;
        #(CLK_CLY*1);
        repeat ((i+5)*50)begin
            key_in = $random;
            #(CLK_CLY*1);
        end 
        key_in = 1;//按键稳定
        #(CLK_CLY*10*500);
    end
    
    //模拟意外抖动
    repeat (3)begin 
        repeat ((i+5)*50)begin
            key_in = $random;
            #(CLK_CLY*1);
        end 
        key_in = 1;//按键稳定
        #(CLK_CLY*500);

    end 
    $stop;
end
endmodule

五、仿真波形图

1、计数器方法

2、状态机方法

通过仿真结果得知两种方法实现的波形图其实都是差不多的,就是第二种使用状态机的缘故导致波形图看着比第一种的负责一点,其实两种方法基本的实现原理都是一致的,只不过就是换了一种实现方法。在图中我们可以看到两种方法的key_out输出位置不一致,这是我们设置的输出条件有点小差别,这个不会影响最终结果。

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

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

相关文章

Redis知识点总价

1 redis的数据结构 2 redis的线程模型 1&#xff09; Redis 采用单线程为什么还这么快 之所以 Redis 采用单线程&#xff08;网络 I/O 和执行命令&#xff09;那么快&#xff0c;有如下几个原因&#xff1a; Redis 的大部分操作都在内存中完成&#xff0c;并且采用了高效的…

深度学习系列69:模型部署的基础知识

参考https://mp.weixin.qq.com/s?__bizMzI4MDcxNTY2MQ&mid2247488952&idx1&sn880d3ad47a8fb3eab56514135f0e643b&chksmebb51d5adcc2944c276af19e8cff5e73c934f8811706be0a94c5f47f9e767c902939903e6b95&scene21#wechat_redirect 1. 基本流水线 1.1 介绍…

掀桌子了!原来是咱们的大屏设计太酷,吓着前端开发老铁了

掀桌子了&#xff01;原来是咱们的大屏设计太酷&#xff0c;吓着前端开发老铁了 艾斯视觉观点认为&#xff1a;在软件开发的世界里&#xff0c;有时候创意和设计的火花会擦得特别亮&#xff0c;以至于让技术实现的伙伴们感到既兴奋又紧张。这不&#xff0c;我们的设计团队刚刚…

故障诊断 | 基于Transformer故障诊断分类预测(Matlab)

文章目录 预测效果文章概述程序设计参考资料预测效果 文章概述 Transformer故障诊断/分类预测 | 基于Transformer故障诊断分类预测(Matlab) Transformer 模型本质上都是预训练语言模型,大都采用自监督学习 (Self-supervised learning) 的方式在大量生语料上进行训练,也就是…

【Django】开源前端库bootstrap,常用

文章目录 下载bootstrap源文件到本地项目引入bootstrap文件 官网&#xff1a;https://www.bootcss.com/V4版本入口&#xff1a;https://v4.bootcss.com/V5版本入口&#xff1a;https://v5.bootcss.com/ 这里使用成熟的V4版本&#xff0c;中文文档地址&#xff1a;https://v4.b…

优化mac outlook通过nginx反向代理后使用ews访问Exchange 2016邮件访问速度慢的有效方法

在nginx配置exchange的反向代理后,mac系统上通过exchange邮箱,通过nginx代理连接邮箱,发现速度很慢,通过查看日志,也存在大量的401失败日志。通过不断的优化和尝试,目前来看,基本上正常了,基本上没有出现大量访问失败的问题。以下就是优化过程中尝试过的方法。 1. 身份…

java学习--包装类

包装类 Boolean的关系图 Character关系图 其他关系图 包装类和基本数据转换 Debug进入之后可以看到底层代码如下 例题&#xff1a; 三元运算符是一个整体返回的数的类型看其中所含类型最高的那个是谁就会转成哪个 想要掌握这个这个知识&#xff0c;就要多看源码&#xff0c;直接…

深入理解计算机系统 CSAPP 家庭作业11.10

A: //home.html <form action"/cgi-bin/adder" method"GET"><ul><li><label for"n1">n1:</label><input type"text" id"n1" name"n1" /> //name的值决定页面提交后&#xf…

探索 Blockly:自定义积木实例

3.实例 3.1.基础块 无输入 , 无输出 3.1.1.json var textOneJson {"type": "sql_test_text_one","message0": " one ","colour": 30,"tooltip": 无输入 , 无输出 };javascriptGenerator.forBlock[sql_test_te…

JavaScript——常用库

文章目录 绪论jQuery选择器事件修改 css查找ajax setTimeout与setIntervalsetTimeoutsetInterval requestAnimationFrameMap与SetlocalStorageJSONDateWebSocketwindowcanvas结语 绪论 『时间是伟大的作家&#xff0c;总会写下完美的结局。』—— 「秋之回忆」 jQuery 这个是优…

特斯拉财报看点:FSD拳打华为,Robotaxi 脚踢百度

大数据产业创新服务媒体 ——聚焦数据 改变商业 特斯拉发最新财报了&#xff0c;这不仅是一份财务报告&#xff0c;更是一张未来发展的蓝图。在这份蓝图中&#xff0c;两个关键词格外耀眼——FSD&#xff08;全自动驾驶系统&#xff09;和Robotaxi&#xff08;无人驾驶出租车&…

【通俗理解】大脑网络结构理论解析——从小世界到无标度性的深度刻画

大脑网络结构理论解析——从小世界到无标度性的深度刻画 大脑网络结构的核心特性 大脑网络结构理论旨在揭示大脑神经元之间连接的复杂模式。其中&#xff0c;小世界特性和无标度性是大脑网络的两个重要特征。小世界特性意味着网络中大部分节点之间都通过较短的路径相连&#…

pycharm+pytorch+gpu开发环境搭建

一、安装anacoda 1、下载Anaconda安装包 官网下载地址 https://www.anaconda.com/distribution/ 清华镜像 Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 下载python3.8对应的版本Anaconda3-2021.04-Windows-x86_64.exe 下载完成…

.Net医院检验系统源码,lis源码,化验系统源码

系统概述&#xff1a; 医学实验室信息系统即LIS&#xff0c;系统把检验、检疫、放免、细菌微生物及科研使用的各类分析仪器&#xff0c;通过计算机联网&#xff0c;实现各类仪器数据结果的实时自动接收、自动控制及综合分析&#xff1b;与条码设备配套使用&#xff0c;自动生成…

手把手教小白Vue3(保姆式服务)

1.初识Vue3 2.Vue3组合式API 2.1认识create-vue create-vue是官方新的脚手架工具&#xff0c;vite下一代构建工具 node -v >16 npm init vuelatest npm run dev 2.2 setup <script setup>原始写法 <script> export default { //执行时机比beforeCre…

Linux(linux命令)和Window(powershell)的查找命令

目录 LinuxWindow基本操作(1)Get-ChildItem(2)Get-ChildItem模糊查找1. 使用星号(*)通配符(常用)1、第一个命令:使用 `-Filter` 参数(常用)2、第二个命令:使用管道和 `Where-Object`3、差异2. 使用问号(?)通配符(不常用)3. 结合使用星号和问号(不常用)4. 使…

6. 开发板烧录

1. 概述 采用恒玄的底板+2小板的开发板 2. 开发板资料 详见:<<BES AUDIO DEV BOARD USER MANUUAL_9v5.pdf>> 3. 硬件接线 供电:可以采用电池供电,也可以采用Type-c供电 烧录:采用Type-C口,实际上就是串口。(下图带黑色标志的)

VSCode+git的gitee仓库搭建

​ 在此之前你已经在gitee创建好了账号&#xff0c;并新建了一个仓库。 1. 安装 Visual Studio Code Visual Studio Code 是编辑 Markdown 和站点配置文件的基础&#xff0c;以下将其简称为 VSCode&#xff0c;你可以在它的 官方网站 下载到它。 如若不理解各个版本之间的区别…

编解码器架构

一、定义 0、机器翻译是序列转换模型的一个核心问题&#xff0c; 其输入和输出都是长度可变的序列。 为了处理这种类型的输入和输出&#xff0c; 我们设计一个包含两个主要组件的架构&#xff1a; 第一个组件是一个编码器&#xff08;encoder&#xff09;&#xff1a; 它接受一…