【FPGA】按键消抖

一、简介

按键消抖通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点 的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。按键消抖就是在按键按下抖动最后一个下降沿延时20ms。

在这里插入图片描述

二、时序图和状态图

这里按键消抖可以用计数器做和状态机做时序图我只画了一个
按键消抖时序图
在这里插入图片描述

状态机状态转换图

在这里插入图片描述

三、程序

计数器消抖

module key_filter #(parameter KEY_W = 2,DELAY_TIME = 1_000_000)( 
    input				      clk		,
    input				      rst_n	    ,
    input	   [KEY_W-1:0]	  key_in	,
    output reg [KEY_W-1:0]    key_down
);

reg   [19:0]   cnt;
wire           add_cnt;
wire           end_cnt;

reg            filter_flag;

reg   [KEY_W-1:0]  key_r0;
reg   [KEY_W-1:0]  key_r1;
reg   [KEY_W-1:0]  key_r2;     

wire               n_edge;
wire               p_edge;

//对输入按键进行打拍,异步信号同步并且检测边沿
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        key_r0 <= -1;     //负数以补码形式存放,对源码取反再加1
        key_r1 <= -1;     
        key_r2 <= -1;
    end 
    else begin 
        key_r0 <= key_in;		//同步
        key_r1 <= key_r0;
        key_r2 <= key_r1;
    end 
end

//三目运算符 检测上下沿
assign    n_edge = ~key_r1 & key_r2?1'b1:1'b0;
assign    p_edge = key_r1 & ~key_r2?1'b1:1'b0;

//计数器开始的标志信号
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        filter_flag <= 1'b0;
    end 
    else if(n_edge)begin 
        filter_flag <= 1'b1; 
    end 
    else if(end_cnt || p_edge)begin 
        filter_flag <= 1'b0; 
    end 
    else  begin 
       filter_flag <= filter_flag;  
    end 
end

//按键消抖延时20ms计数器
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt <= 0;
    end 
    else if(add_cnt)begin 
            if(end_cnt || p_edge)begin 
                cnt <= 0;
            end
            else begin 
                cnt <= cnt + 1;
            end 
    end
   else  begin
       cnt <= cnt;
    end
end 

assign add_cnt = filter_flag;
assign end_cnt = add_cnt && cnt == DELAY_TIME-1;

//按键输出
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        key_down <= 0;
    end 
    else if(end_cnt)begin 
        key_down <= ~key_r2;
    end 
    else begin 
        key_down <= 0;
    end 
end

endmodule

状态机消抖

/****************************************
-----------------------------------------
Component name:key_filter
Author        :lcyyds 
time          :2021/12/7 
Description   :Mealy型三段式状态机按键消抖
src           :
-----------------------------------------
****************************************/
module key_filter # (parameter KEY_W = 3,TIME_20MS = 1000000)(
    input 			            clk		,
    input 			            rst_n	,
    input 		[KEY_W - 1:0]	key_in	,
    output 		[KEY_W - 1:0]	key_out	         
);
    // 参数定义
    localparam IDLE  = 4'b0001;     //初始状态 
    localparam DOWN  = 4'b0010;     //按键按下抖动
    localparam HOLD  = 4'b0100;     //按键按下后稳定
    localparam UP    = 4'b1000;     //按键上升抖动
    // 信号定义
    reg     [3:0]           state_c         ;   //现态
    reg     [3:0]           state_n         ;   //次态

    // 状态转移条件定义
    wire                    idle2down       ;
    wire                    down2idle       ;
    wire                    down2hold       ;
    wire                    hold2up         ;
    wire                    up2idle         ;

    reg     [KEY_W - 1:0]   key_r0          ;   //同步
    reg     [KEY_W - 1:0]   key_r1          ;   //打拍
    wire    [KEY_W - 1:0]   nedge           ;   //下降沿
    wire    [KEY_W - 1:0]   pedge           ;   //上升沿

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

    reg [KEY_W - 1:0] key_out_r;    // 输出寄存

    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end

//状态转换条件
    assign idle2down = (state_c == IDLE) && nedge;                      // 检测到下降沿
    assign down2idle = (state_c == DOWN) && (pedge && ~end_cnt_20ms);   // 计时未到20ms时且出现上升沿表示按键意外抖动,回到初始态
    assign down2hold = (state_c == DOWN) && (~pedge && end_cnt_20ms);   // 计时到20ms时没有出现上升沿标志按键按下后保持稳定
    assign hold2up   = (state_c == HOLD) && (pedge);                    // 检测到上升沿跳转到上升态
    assign up2idle   = (state_c == UP)   && end_cnt_20ms;               // 计数器计数到20ms跳转到初始态

//次状态
    always@(*)begin
        case(state_c)
            IDLE: begin
                if(idle2down)begin
                    state_n = DOWN;
                end
                else begin
                    state_n = state_c;
                end
            end
            DOWN: begin
                if(down2idle)begin
                    state_n = IDLE;
                end
                else if(down2hold)begin
                    state_n = HOLD;
                end
                else begin
                    state_n = state_c;
                end
            end
            HOLD: begin
                if(hold2up)begin
                    state_n = UP;
                end
                else begin
                    state_n = state_c;
                end
            end
            UP: begin
                if(up2idle)begin
                    state_n = IDLE;
                end
                else begin
                    state_n = state_c;
                end
            end
            default:state_n = state_c;
        endcase
    end


    // 20ms计数器
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_20ms <= 0;
        end
        else if(add_cnt_20ms)begin
            if(pedge || end_cnt_20ms)begin
                cnt_20ms <= 0;
            end
            else begin
                cnt_20ms <= cnt_20ms + 1'b1;
            end
        end
    end
    assign add_cnt_20ms = state_c == DOWN || state_c == UP;             // 当按键按下或上弹时开始计数
    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_r0 <= {KEY_W{1'b1}};
            key_r1 <= {KEY_W{1'b1}};
        end
        else begin
            key_r0 <= key_in;
            key_r1 <= key_r0;
        end
    end

    assign nedge = ~key_r0 &  key_r1;   // 检测下降沿
    assign pedge =  key_r0 & ~key_r1;   // 检测上升沿


    // 按键赋值
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            key_out_r <= {KEY_W{1'b0}};
        end
        else if(hold2up) begin
            key_out_r <= ~key_r1;
        end
        else begin
            key_out_r <= {KEY_W{1'b0}};
        end
    end

    assign key_out = key_out_r;

endmodule

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值