按键消抖-task任务和仿真平台建立

一、按键抖动原理

  按键抖动原理:按键存在一个反作用弹簧,因此当按下或者松开时均会产生额外的物理抖动,物理抖动会产生电平的抖动。

消抖方法:一般情况下,抖动的总时间会持续20ms以内,按下按键后,等20ms过去了再取键值就行了。

二、第1种按键消抖

  只对按下侧的抖动进行消除,弹起的就不管了,因为我们使用按键时要的也是按下后的键值。输出为1clk的按键值。

//======================================================================
// --- 名称 : key_filter
// --- 作者 : 木子
// --- 日期 : 2024-4-10
// --- 描述 : 按键消抖,输出为1个clk的输入,只关注按下侧的消抖
//======================================================================

module key_filter
//---------------------<参数定义>---------------------------------------
#(
parameter   TIME_20MS   = 1000000           , //20ms时间
parameter   TIME_W      = 20                , //20ms时间位宽
parameter   KEY_W       = 4                   //按键个数
)
//---------------------<端口声明>---------------------------------------
(
input                   clk                 , //时钟,50Mhz
input                   rst_n               , //复位,低电平有效
input      [KEY_W-1:0]  key                 , //按键输入
output reg [KEY_W-1:0]  key_vld               //按键消抖后的输出
);
//---------------------<信号定义>---------------------------------------
reg  [TIME_W-1:0]       cnt                 ;
wire                    add_cnt             ;
wire                    end_cnt             ;
reg  [KEY_W -1:0]       key_r0              ;
reg  [KEY_W -1:0]       key_r1              ;
reg                     flag                ;

//----------------------------------------------------------------------
//--   信号同步 + 消除亚稳态
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_r0 <= 0;
        key_r1 <= 0;
    end
    else begin
        key_r0 <= key;      //信号同步
        key_r1 <= key_r0;   //打拍,防亚稳态
    end
end

//----------------------------------------------------------------------
//--   20ms计时
//----------------------------------------------------------------------
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 <= cnt;
end

assign add_cnt = flag==0 && key_r1!=0 ;        //允许计数 且 按键按下
assign end_cnt = add_cnt && cnt==TIME_20MS-1;  //计到20ms

//计满指示
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)                  //复位
        flag <= 0;              //flag=0允许计数
    else if(end_cnt)            //20ms到
        flag <= 1;              //flag=1不再计数
    else if(key_r1==0)          //按键松开
        flag <= 0;              //flag=0,为下次计数做准备
    else                        //否则
        flag <= flag;           //维持自身
end

//----------------------------------------------------------------------
//--   按键消抖完成,输出按键有效信号
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        key_vld <= 0;
    else if(end_cnt)        //20ms到
        key_vld <= key_r1;  //按键已消抖,可以使用
    else
        key_vld <= 0;
end


endmodule

        下面是编写的仿真代码。由代码可以看到这里使用了task任务,用其定义一个完整的按下弹起的按键过程。 在task任务中,模拟抖动时采用了随机数发生函数来产生抖动。$random这一系统函数可以产生一个有符号的32位随机整数。一般的用法是“$random%b”,其中b > 0。这样就会生成一个范围在 [-(b-1),b-1] 中的随机数。如果只得到正数的随机数,这可采用“{$random}%b”来产生,这样就会生成一个方位在 [0,b-1] 中的随机数。

`timescale 1ns/1ps  //时间精度
`define    Clock 20 //时钟周期

module key_filter_tb;
//---------------------<端口声明>---------------------------------------
reg                     clk                 ;
reg                     rst_n               ;
reg  [3:0]              key                 ;
wire [3:0]              key_vld             ;

//----------------------------------------------------------------------
//--   模块例化
//----------------------------------------------------------------------
key_filter
#(                                              //参数传递
    .TIME_20MS          (100                )
)
u_key_filter                                    //模块例化
(
    .clk                (clk                ),
    .rst_n              (rst_n              ),
    .key                (key                ),
    .key_vld            (key_vld            )
);

//----------------------------------------------------------------------
//--   时钟信号和复位信号
//----------------------------------------------------------------------
initial begin
    clk = 1;
    forever
    #(`Clock/2) clk = ~clk;
end

initial begin
    rst_n = 0; #(`Clock*20+1);
    rst_n = 1;
end

//----------------------------------------------------------------------
//--   task函数编写,模拟按键抖动
//----------------------------------------------------------------------
reg  [15:0]             rand                ;

task press_key;
    begin
        repeat(50) begin            //50次按下随机时间抖动
            rand = {$random}%70;
            #rand;
            key = ~key;
        end
        key = 4'b1001;
        #10000;

        repeat(50) begin            //50次释放随机时间抖动
            rand = {$random}%70;
            #rand;
            key = ~key;
        end
        key = 0;
        #10000;
    end
endtask

//----------------------------------------------------------------------
//--   设计输入信号
//----------------------------------------------------------------------
initial begin
    #1;
    key = 0  ; #(`Clock*20+1);  //初始化完成
    press_key; #10000;
    press_key; #10000;
    press_key; #10000;
    $stop;
end



endmodule

 仿真波形如下:

三、按键消抖二

按下和弹起的抖动都消除掉。

//======================================================================
// --- 名称 : key_filter
// --- 作者 : 木子
// --- 日期 : 2024-4-11
// --- 描述 : 按键消抖,输出为消抖后的输入,计数器一直在工作
//======================================================================

module key_filter
//---------------------<参数定义>---------------------------------------
#(
parameter   TIME_20MS   = 1000000           , //20ms时间
parameter   TIME_W      = 20                , //20ms时间位宽
parameter   KEY_W       = 4                   //按键个数
)
//---------------------<端口声明>---------------------------------------
(
input                   clk                 , //时钟,50Mhz
input                   rst_n               , //复位,低电平有效
input      [KEY_W-1:0]  key                 , //按键输入
output reg [KEY_W-1:0]  key_vld               //消抖后的按键输出
);
//---------------------<信号定义>---------------------------------------
reg  [TIME_W-1:0]       cnt                 ;
wire                    add_cnt             ;
wire                    end_cnt             ;
reg  [KEY_W -1:0]       key_r0              ;
reg  [KEY_W -1:0]       key_r1              ;
reg  [KEY_W -1:0]       key_r2              ;
wire                    key_press           ;

//----------------------------------------------------------------------
//--   边沿检测
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_r0 <= 0;
        key_r1 <= 0;
        key_r2 <= 0;
    end
    else begin
        key_r0 <= key;      //信号同步
        key_r1 <= key_r0;   //打拍,防亚稳态
        key_r2 <= key_r1;
    end
end

assign key_press = key_r1 ^ key_r2; //按键状态变化检测

//----------------------------------------------------------------------
//--   20ms计时
//----------------------------------------------------------------------
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 <= cnt;
end

assign add_cnt = 1 ;                                //一直处于计数状态
assign end_cnt = key_press || (cnt== TIME_20MS-1);  //按键仍在抖动或计到了20ms,则清0

//----------------------------------------------------------------------
//--   按键消抖完成,输出按键有效信号
//----------------------------------------------------------------------
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        key_vld <= 0;
    else if(cnt==TIME_20MS-1)   //cnt计到20ms
        key_vld <= key_r2;      //按键已消抖,可以使用
    else
        key_vld <= key_vld;
end


endmodule

仿真代码与第一种略有不同。采用仿真模型进行仿真。
 

模块图

实现代码:

`timescale 1ns/1ps  //时间精度
`define    Clock 20 //时钟周期
 
 module key_filter_tb;
 //---------------------<端口声明>---------------------------------------
 reg                     clk                 ;
 reg                     rst_n               ;
 wire [3:0]              key                 ; //本是输入现在变成了内部信号,故改成wire型
 wire [3:0]              key_vld             ;
 
//----------------------------------------------------------------------
//--   模块例化
//----------------------------------------------------------------------
//按键消抖仿真模型
key_module u_key_module
(
    .key                (key                )
);

//按键消抖设计文件
key_filter
#(                                              //参数传递
    .TIME_20MS          (100                )
)
u_key_filter                                    //模块例化
(
    .clk                (clk                ),
    .rst_n              (rst_n              ),
    .key                (key                ),
    .key_vld            (key_vld            )
);

//----------------------------------------------------------------------
//--   时钟信号和复位信号
//----------------------------------------------------------------------
initial begin
    clk = 1;
    forever
    #(`Clock/2) clk = ~clk;
end

initial begin
    rst_n = 0; #(`Clock*20+1);
    rst_n = 1;
end


endmodule
//======================================================================
 //--名称 : key_module
 //--作者 : 木子
 //--日期 : 2024-4-11
 //--描述 : key按键消抖模块的仿真模型
 //======================================================================
 `timescale 1ns/1ps
 
 module key_module
 //---------------------<端口声明>---------------------------------------
 (
 output reg [15:0]       key
 );
 
 //----------------------------------------------------------------------
 //--   task函数编写,模拟按键抖动
 //----------------------------------------------------------------------
 reg  [15:0]             rand                ;
 
 task press_key;
     begin
         repeat(50) begin            //50次按下随机时间抖动
             rand = {$random}%70;
             #rand;
             key = ~key;
         end
         key = 4'b1001;
         #10000;
 
         repeat(50) begin            //50次释放随机时间抖动
             rand = {$random}%70;
             #rand;
             key = ~key;
         end
         key = 0;
         #10000;
     end
 endtask
 
 //----------------------------------------------------------------------
 //--   设计输入信号
 //----------------------------------------------------------------------
 initial begin
     #1;
     key = 0  ; #401;  //初始化完成
     press_key; #10000;
     press_key; #10000;
     press_key; #10000;
     $stop;
 end
 
 
 endmodule

仿真波形:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值