04 初探按键消抖与仿真模型

我是先把计数器拆分:加法和减法计数器。

先做加法计数器,再把加法计数器拆成按键控制和led控制部分,在本子上写下他们的大致原理再,写下他们的输入输出端口模块,最终按照模块端口和工作原理及各小功能模块,照图施工就行。

总体——拆分——各模块(仿真验证)(对于一个模块先写模块的端口,和它的工作原理,大概由那几个部分构成,及各部分的联系)

一 设计定义

第一部分设计功能:实现按键的按下是真正的按下,滤除掉机械抖动。(原因是按键动作会有一段稳定的状态,而机械抖动会给出错误的电平信号且不稳定)

第二部分设计原理:按键按下可以分为,下降沿和上升沿。而完整的按键按下与释放,由四个状态组成,分别是空闲状态,按下滤波状态(下降沿),按下稳定状态(低电平),按键释放滤波状态(上升沿)。状态如下图:

通过上面对按键按下与释放的过程,分析设计方法可以为状态机,各个状态转移的关系。如下图:状态触发条件----从抖动到稳定需要10ms的延时,才能进入下一个状态。

 

总结:按键按下与释放,首先得把按键分为下降沿和上升沿,下降沿与上升沿都得维持20ms/10ms, 才能看作真正的按下与释放。否则为抖动。

二  设计输入

整体模块框图及设计代码:

 

module key_filter

(

    clk,

    rst_n,

    key_in,

    pin_out

);

    input clk;

    input rst_n;

    input key_in;            //

    output pin_out;          //the output of key,when the key_in==1(means press)

    //the output of key,when the key_in==0(means release)

//此处省略下面的边沿检测和状态机部分

endmodule

按键消抖设计,分为两部分,一是按键边沿检测模块(下降沿和上升沿),二是状态机模块实现整个按下与释放过程。

第一部分边沿检测:边沿检测就是检测按键的下降沿和上升沿,而实现按键的按下与释放的原理:使用两级D触发器,记忆按键当前状态的电平值和上一个状态的电平值,通过比较这两个状态的电平,就能判断下降沿和上升沿。电路结构图如下图:

 

第二部分边沿检测代码:

reg H2L_F1;                 //first reg(store the value of key from 1 to 0,press)

    reg H2L_F2;

    reg L2H_F1;                 //first reg(store the value of key from 0 to 1,press)

    reg L2H_F2;  

    //the clasic edge detection(边沿检测)

    //注意小梅哥的边缘检测,优良的不同

//他只用了两个寄存器,并且定义了按键的上升沿和下降沿(assign)

//

assign nedge=!H2L_F1 & H2L_F2

assign podge= L2H_F1 & !L2H_F2

 

always@(posedge clk or negedge rst_n)

    if(!rst_n)

       begin

           H2L_F1<=1'b1;

           H2L_F2<=1'b1;

          L2H_F1<=1'b0;

           L2H_F2<=1'b0;

        end

    else   begin

           H2L_F1<=key_in;

           H2L_F2<=H2L_F1;

          L2H_F1<=key_in;

           L2H_F2<=L2H_F1;

       end

第二部分按键按下与释放状态:

设计原理:照图施工,是按键按下与释放,就如设计定义的状态原理图和状态转移图所示。分为四个状态,未按下的空闲状态,按下的滤波状态,按下稳定状态,释放滤波状态。触发条件,分别是,下降沿到来,满足10ms的延迟,上升沿到来,满足10ms的延迟。思路如下面代码所示。

//be careful the binary(二进制)

    localparam

       IDEL = 4'b0001,      //the idel state

       FILTER0 = 4'b0010,   //first filter(key from 1 to 0)

       DOWN = 4'b0100,      //when the key_in==0

       FILTER1 = 4'b1000;   //second filter(key from 0 to 1)

parameter T10MS = 20'd500_000-1;

       reg [19:0]delay_cnt;

       reg [3:0]state;

       reg pin_out;

       //他把计数器的10MS单独写在一个模块

//小梅哥滤波的原理:按键的下降沿与上升沿的间隔时间大于10MS

//还有按键的输出信号,即表示按键按下或释放的信号,只有保持一个时钟周期

       always@(posedge clk or negedge rst_n)

       if(!rst_n)begin

       state<=IDEL;

       delay_cnt<=20'd0;

       pin_out<=1'b0;

       end

       else   begin

           case(state)

              IDEL:begin

                  if(!H2L_F1 & H2L_F2)

                     state<=FILTER0;

                  else

                     state<=IDEL;

              end

              FILTER0:begin

                  if(key_in==1'b0)begin

                     if((delay_cnt==T10MS) )begin

                        state<=DOWN;

                         pin_out<=1'b1;

                         delay_cnt<=20'd0;

                     end

                     else

                     delay_cnt<=delay_cnt+1'b1;

                     end

                  else

                         state<=IDEL;

              end

              DOWN:begin

                  if(L2H_F1 & !L2H_F2)

                     state<=FILTER1;

                  else

                     state<=DOWN;

              end

              FILTER1:begin

                  if(key_in==1'b1)begin

                     if((delay_cnt==T10MS) )begin

                         state<=IDEL;

                         pin_out<=1'b0;

                         delay_cnt<=20'd0;

                     end

                     else

                     delay_cnt<=delay_cnt+1'b1;

                     end

                  else

                         state<=DOWN;

                  end

             

              default:state<=IDEL;

           endcase

          

       end

三  仿真验证

我对仿真的理解就是,给一个系统,加激励,然后观察输出的波形。作用有两个:一是验证设计是否实现功能。二是定位问题。

仿真模型:我的理解就是在tb文件里产生时钟与复位信号,在仿真模型文件里产生(除时钟和复位信号的)其他激励如按键信号key,最终在tb文件调用仿真文件key_model即可。(类似与顶层模块与各个功能模块之间的调用)

 

第一步部分 Task函数调用与随机函数的用法:

抖动是人为的设计了一些固定值抖动,不具有随机性且编写出来的文件太长, 这里可以采用随机数发生函数来产生抖动

Task函数格式:

task press_key;
begin

//中间部分为仿真具体实现功能,自己设计
end

endtask

//在仿真主程序中,直接调用Task函数名press_key;

A:下面表格为仿真模型(key_model)的代码:

注,这是单独的一个设计模块,类似于功能模块(部分)。

//Task函数与随机函数

reg [15:0]myrand;
task press_key;
begin
repeat
(50) begin //五十次随机时间按下抖动
myrand = {$random}%65536;
#
myrand key_in = ~key_in;
end
key_in = 0;
#
50_000_000; //按下稳定
repeat(50) begin //50 次随机时间释放抖动
myrand = {$random}%65536;
#
myrand key_in = ~key_in;
end
key_in = 1;
#
50_000_000; //释放稳定
end
endtask

B:下面表格为仿真顶层(key_ filter_tb)的代码:

注:这个仿真设计相当于顶层,可以直接调用仿真模型。

//仿真主程序调用Task函数

initial begin
Rst_n = 1'b0;

key_in = 1'b1;
#(`clk_period*10) Rst_n = 1'b1;
#30000;
press_key; #10000;
press_key; #10000;
press_key; #10000;
$stop;
end

C: 收获有三:

A写好一个部分的程序,其他的在此基础上复制修改(如把按键按下的写好,写按键释放时,复制一段只需要把按键的值改成(0-1即可))

B 模拟真实仿真时,若时间较长可以把时钟信号删除掉,再

进行仿真(缩小仿真时间)

key_filter#(.T10MS(999))//表示仿真时间参数T10MS为计数到999,即1000个时钟周期,也就是20_000ns等效于10MS.

C对于信号输入的值很多情况,最好调用一个随机函数$random。。且把仿真程序分成几个部分,在一个主要的仿真顶层程序直接调用。比如再key_filter_tb 仿真程序中调用key_model程序(类似于模块划分,设置顶层程序与子程序。)

D学习语法可以看IEEE—2005语法标准

 

从仿真波形看出,能够实现把小于十毫秒的抖动滤除,实现按键消抖。

我学到了,边沿检测,仿真模型,随机函数等等。至此,一个按键消抖模块完成勒,很开心。

原文地址:https://www.cnblogs.com/Xwangzi66/p/12621999.html