zoukankan      html  css  js  c++  java
  • 基础项目(6)基于尖峰脉冲的按键消抖程序设计讲解

     写在前面的话

    我们通常所用的按键开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会马上断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了避免这种现象造成的干扰而作的措施就是按键消抖。

    抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。按键稳定闭合时间的长短则是由操作人员的按键动作决定的,一般为零点几秒至数秒。键抖动会引起一次按键被误读多次。为确保智能单元对按键的一次闭合仅作一次处理,必须消除键抖动。在按键闭合稳定时读取键的状态,并且必须判别到按键释放稳定后再作处理。按键的消抖,可用硬件或软件两种方法,梦翼师兄这里主要介绍一下软件的消除方法。

    基于尖峰脉冲的按键消抖

    尖峰脉冲是电路设计中非常重要的一种信号,很多层次化设计中模块间的握手信号一般都会使用尖峰脉冲,正确的应用尖峰脉冲信号,可以有效的减少系统的逻辑冗余,提高系统稳定性和执行效率。本节,我们就来学习如何利用尖峰脉冲实现按键消抖。

    项目需求

    用一个按键控制数码管显示,数码管显示的数值为按键的次数,每按一次按键,数码管显示的数值加一,数值从0-F循环显示。

    误区排除

    由于数码管在之前的章节中已经有很详细的论述,所以这里我们忽略数码管驱动部分,把注意力放在按键次数的累加模块。说到这里,很多人的脑海中可能会出现下述代码:

    always @ (posedge clk or negedge rst_n) 

    begin

        if (!rst_n)

            begin

                sum <= 1'b0; //赋初值

            end

        else

            begin

                if (!key_in) //有按键按下

                    sum <= sum + 1; //计数器加1

                else

                    sum <= sum; //计数值不变

            end

    end

    这个always块的主要意思是如果检测到按键变为低电平,则说明有按键按下,然后就开始计数累加。这是最容易想到的一种错误方式,假设我们按键按下的时间特别长,那么key_in就会一直保持为低电平,我们的sum计数值也就会因此而不断累加。这与我们每按键一次,计数值只加一的目标是不相符的,而且这种方式并不能处理电路中遇到的抖动。

    那么,我们该如何是好呢?分析上述电路,我们发现计数值不断累积加的原因在于每次按键按下,key_in都会保持多个周期的低电平。那么我们是否能产生这样一个信号呢-每次按键按下,不管按键时间如何,该信号能且仅能维持一个时钟周期的高电平。如果可以产生出这样的尖峰脉冲,那么我们就可以实现每次按键,计数值加且只加一的结果。

    设计思路

    为了证实上述想法的可行性,接下来我们设计流程图如下:

    结构说明:当检测到有按键按下时,为了消除抖动,我们启动延时计数器,如果按键保持低电平的时间足够长,那么计数值一定会满足我们设置好的延时条件,否则计数清零,等待下次按键到来。如果延时计数满足条件,说明确定有按键按下,那么我们就可以输出一个尖峰脉冲,从而控制sum累加。

     系统框架

    顶层端口描述

    端口名

    端口说明

    Clk

    系统50MHz时钟输入

    rst_n

    系统低电平复位

    key_in

    系统外部按键输入

    Sum

    输出按键累加值

    6.6.8 代码解释

    /****************************************************          

     *   Engineer      :   梦翼师兄

     *   QQ             :   761664056

     *   The module function:按键消抖模块 *****************************************************/

    01  module pulse(

    02                  //系统输入

    03                  clk,//系统50MHZ时钟输入

    04                  rst_n,//低电平复位信号输入

    05                  key_in,//外部按键输入

    06                  //系统输出

    07                  sum//按键次数计数器

    08               );

    09               

    10  //-----------------系统输入---------------------

    11  input clk;//系统50MHZ时钟输入

    12  input rst_n;//低电平复位信号输入

    13  input key_in;//外部按键输入

    14

    15  //-----------------系统输出---------------------

    16  output reg [3:0]sum;//按键次数计数器

    17

    18  //------------------寄存器定义------------------

    19  reg [10:0]counter;//消抖延时计数器

    20  reg state;//状态寄存器

    21  reg pos_flag;//尖峰脉冲寄存器

    22

    23  //----------按键消抖以后产生尖峰脉冲------------

    24  always@(posedge clk or negedge rst_n)

    25      begin

    26          if(!rst_n)

    27              begin

    28                  counter<=0; //消抖延时计数器清零

    29                  state<=0;   //状态寄存器清零

    30                  pos_flag<=0;//尖峰脉冲寄存器清零

    31              end

    32          else

    33              begin

    34                  case(state)

    35                  0:begin

    36                      if(counter<10)//消抖延时计数器未开始计数

    37                          begin /*key_in==0,说明有按键按下*/

    38                              if(!key_in)

    39                                  begin

    40      /*消抖延时计数器开始计数*/       counter<=counter+1;

    41                                  end

    42          //key_in==1,说明按键放开,而此时计数值未满,

    43          //说明刚才的“按键”是抖动                  

    44                              else

    45                                  begin

    46          /*消抖延时计数器清零*/     counter<=0;

    47                                  end

    48                          end

    49                      else//计数值满,说明确定有按键按下

    50                          begin

    51                      //尖峰脉冲寄存器置为高电平  

    52                              pos_flag<=1;

    53                              counter<=0;//消抖延时计数器清零

    54                              state<=1;//跳转到下一状态

    55                          end

    56                  end

    57                  1:begin

    58                      pos_flag<=0;//尖峰脉冲寄存器置为低电平

    59          //key_in==1,说明按键放开(一次按键动作完整结束)            

    60                      if(key_in)

    61                          state<=0;//状态返回,等待下次按键到来

    62                  end

    63                  default:state<=0;//状态返回

    64                  endcase

    65              end

    66      end

    67  //-----------------------累计尖峰脉冲出现次数-----------------------

    68  always@(posedge clk or negedge rst_n)

    69      begin

    70          if(!rst_n)

    71              begin

    72                  sum<=0;//按键计数器清零

    73              end

    74          else

    75              begin

    76                  if(pos_flag)//尖峰脉冲到来,说明按键按下

    77                      sum<=sum+1;//按键计数器累加

    78              end

    79      end

    80  endmodule

    当检测到有按键按下时,延时计数器开始计数,在计数的这段时间如果检测到按键是放开的,我们认为是按键抖动,计数器清零,等待下一次的按键按下,如果按键一直按下并满足我们预先设置的最大计数值,我们认为是真正有按键按下,延时计数器清零,pos_flag 信号拉高,状态向下跳转。在下一个状态  pos_flag 信号拉低,如果检测到按键放开,状态跳转到上一个状态等待下一次按键按下。这样就产生了一个周期的尖峰脉冲。从第68行到结束,当检测到一个尖峰脉冲的时候,按键计数器加1。

    编写测试代码如下:

    /****************************************************          

     *   Engineer      :   梦翼师兄

     *   QQ             :   761664056

     *   The module function:按键消抖测试模块 *****************************************************/

    01  `timescale 1ns/1ps

    02

    03  module tb;

    04

    05  //-----------------------系统输入-----------------------

    06  reg clk;//系统50MHZ时钟输入

    07  reg rst_n;//低电平复位信号输入

    08  reg key_in;//外部按键输入

    09

    10  //-----------------------系统输出-----------------------

    11  wire [3:0]sum;//按键次数计数器

    12

    13  //-----------------------产生测试激励-------------------

    14  initial

    15      begin

    16          clk=0;

    17          rst_n=0;

    18          key_in=1;

    19          # 1000.1 rst_n=1;

    20  //-------------------模拟按键动作-------------------  

    21          # 1000 key_in=0;    

    22          # 1000 key_in=1;

    23          # 100  key_in=0;    

    24          # 100  key_in=1;

    25          # 300  key_in=0;    

    26          # 300  key_in=1;

    27          # 100  key_in=0;    

    28          # 200  key_in=1;

    29          # 1000 key_in=0;    

    30          # 900  key_in=1;

    31          # 1000 key_in=0;    

    32          # 800  key_in=1;

    33          # 1000 key_in=0;    

    34          # 1000 key_in=1;

    35  //----------------------------------------------        

    36      end

    37

    38  always #10 clk=~clk;//50MHZ晶振

    39

    40  //----------------实例化被测试模块----------------  

    41  pulse  pulse (

    42                  //系统输入

    43                  .clk(clk),//系统50MHZ时钟输入

    44                  .rst_n(rst_n),//低电平复位信号输入

    45                  .key_in(key_in),//外部按键输入

    46                  //系统输出

    47                  .sum(sum)//按键次数计数器

    48               );

    49  endmodule

     仿真分析

    我们再放大标示线区域:

    由上述波形可以看出,当按键按下以后,计数器首先开始计数,当计数值不满足时,计数器清零并等待下一次按键按下。当计数值满足以后,才会输出一个时钟周期的尖峰脉冲,而按键次数寄存器sum也会在尖峰脉冲的作用下开始累加。每次按键按下,只会出现一次尖峰脉冲,说明我们的设计是正确的。

    本设计中,我们只是消除了按键按下时候的抖动而按键放开时候的抖动我们并没有处理梦翼师兄希望大家可以自行将改代码填充完整


     

  • 相关阅读:
    精灵点点基础教程1 -- 下载与安装
    技术人员搞公司还是省省吧
    企业级自定义表单引擎解决方案(二)--架构及核心模块设计
    企业级自定义表单引擎解决方案(一)--总体介绍
    .net core quartz job作业调度管理组件
    NPOI读写Excel组件封装Excel导入导出组件
    企业级工作流解决方案(十七)--工作流--工作流插件模型
    企业级工作流解决方案(十六)--工作流--工作流实体模型
    企业级工作流解决方案(十五)--集成Abp和ng-alain--Abp其他改造
    企业级工作流解决方案(十四)--集成Abp和ng-alain--自动化脚本
  • 原文地址:https://www.cnblogs.com/mengyi1989/p/11518347.html
Copyright © 2011-2022 走看看