按键消抖实现

一、使用状态机实现按键消抖

在这里插入图片描述
可将按键按下整个过程看做四个状态:按键空闲状态,按下抖动状态,稳定按下状态,释放抖动状态。
代码实现:

/*
 * @Description: 状态机方式按键消抖(多按键)
 * @Author: Fu Yu
 * @Date: 2023-07-27 15:03:36
 * @LastEditTime: 2023-07-27 19:35:59
 * @LastEditors: Fu Yu
 */

module keys_filter #(
    parameter   MAX_20ms = 20'd999_999,//20ms
                WIDTH = 3
)(
    input       wire                        clk         ,
    input       wire                        rst_n       ,
    input       wire  [WIDTH-1:0]           key_in      ,

    output      wire  [WIDTH-1:0]           key_down     
);

//状态机参数定义
localparam  IDLE        =   4'b0001       ,//空闲状态
            FILTER_DOWN =   4'b0010       ,//按键按下抖动状态
            HOLD_DOWEN  =   4'b0100       ,//按下按键稳定状态
            FILTER_UP   =   4'b1000       ;//按键释放抖动状态

//状态跳转条件定义
wire        idle2filter_down       ;//IDLE -> FILTER_DOWN
wire        filter_down2hold_down   ;//FILTER_DOWN -> HOLD_DOWN
wire        hold_down2filter_up     ;//HOLE_DOWN -> FILTER_UP
wire        filter_up2idle        ;//FILTER_UP -> IDLE

reg [19:0] cnt_20ms;
reg [3:0]   state_c;//现态
reg [3:0]   state_n;//次态
reg [WIDTH-1:0]    key_r0;//寄存
reg [WIDTH-1:0]    key_r1;//打拍
reg [WIDTH-1:0]    key_r2;
reg [WIDTH-1:0]    key_down_r;//寄存key_down

wire [WIDTH-1:0]   nedge;//下降沿
wire [WIDTH-1:0]   pedge;//上升沿
wire add_cnt_20ms;
wire end_cnt_20ms;

//****************************************************************
//--状态机
//****************************************************************
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        state_c <= IDLE;
    end
    else begin
        state_c <= state_n;
    end
end

//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
    case (state_c) 
        IDLE : begin
            if(idle2filter_down)begin
                state_n = FILTER_DOWN;
            end
            else begin
                state_n = state_c;
            end
        end
        FILTER_DOWN : begin
            if(filter_down2hold_down) begin
                state_n = HOLD_DOWEN;
            end
            else begin
                state_n = state_c;
            end
        end
        HOLD_DOWEN : begin
            if(hold_down2filter_up) begin
                state_n = FILTER_UP;
            end
            else begin
                state_n = state_c;
            end
        end
        FILTER_UP : begin
            if(filter_up2idle) begin
                state_n = IDLE;
            end
            else begin
                state_n = state_c;
            end
        end
        default : state_n = IDLE;
    endcase
end

assign idle2filter_down = state_c == IDLE && nedge;
assign filter_down2hold_down = state_c == FILTER_DOWN && end_cnt_20ms;
assign hold_down2filter_up = state_c == HOLD_DOWEN && pedge;
assign filter_up2idle = state_c == FILTER_UP && end_cnt_20ms;

//第三段:描述输出,时序逻辑或组合逻辑皆可
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        key_down_r <= 'b0;
    end
    else if(filter_down2hold_down) begin
        key_down_r <= ~key_r2;
    end
    else begin
        key_down_r <= 'b0;
    end
end

assign  key_down = key_down_r;

//****************************************************************
//--上升沿下降沿检测
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        key_r0 <= {WIDTH{1'b1}};
        key_r1 <= {WIDTH{1'b1}};
        key_r2 <= {WIDTH{1'b1}};
    end
    else begin
        key_r0 <= key_in;
        key_r1 <= key_r0;
        key_r2 <= key_r1;
    end
end

assign nedge = ~key_r1 & key_r2;
assign pedge = ~key_r2 & key_r1;

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

assign add_cnt_20ms = state_c == FILTER_DOWN || state_c == FILTER_UP;
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == MAX_20ms;


endmodule //key_filte

测试文件:

`timescale 1ns/1ns
module keys_filter_tb ();

reg tb_clk;
reg tb_rst_n;
reg [2:0] tb_key_in;
wire [2:0]  tb_key_down;

parameter CYCLE = 20;
defparam u_keys_filter.MAX_20ms = 20'd99;         

always #(CYCLE) tb_clk = ~tb_clk;

integer i,j;

initial begin
    tb_key_in = 3'b111;
    tb_clk = 0;
    tb_rst_n = 0;//开始复位
    #(CYCLE*2);
    tb_rst_n = 1;
    #11;
    tb_key_in[1] = 0;
    for(j=0;j<8;j=j+1)begin
        i = {$random}%500;
        #i;
        tb_key_in[1] = i;
    end
    tb_key_in[1] = 0;
    wait(u_keys_filter.MAX_20ms);
    #10000;

    tb_key_in[1] = 1;
    for(j=0;j<8;j=j+1)begin
        i = {$random}%500;
        #i;
        tb_key_in[1] = i;
    end
    tb_key_in[1] = 1;
    #1000;
    $stop;
end


 keys_filter #(
    .WIDTH(3)
 )u_keys_filter(
    .          clk       (tb_clk)  ,
    .          rst_n      (tb_rst_n) ,
    .          key_in     (tb_key_in) ,

    .          key_down     (tb_key_down)
);

endmodule //keys_filter_tb

二、非状态机方式消抖

此方法简单,当检测到下降沿时,进行一次20ms计数,20ms计数过后直接检测稳定信号并输出。
代码实现:

/*
 * @Description: 多位按键销抖
 * @Author: Fu Yu
 * @Date: 2023-07-27 11:05:30
 * @LastEditTime: 2023-07-27 12:19:49
 * @LastEditors: Fu Yu
 */


module key_filter #(
    parameter WIDTH = 3,//WIDTH表示位宽
    parameter MAX_20ms = 20'd999_999//20ms
)(
    input       wire                    clk         ,
    input       wire                    rst_n       ,
    input       wire [WIDTH - 1:0]      key_in      ,

    output      wire [WIDTH - 1:0]      key_down
);

reg [19:0] cnt_20ms;//20ms计数器
reg [WIDTH-1:0] key_r0;//同步
reg [WIDTH-1:0] key_r1;//打两排
reg [WIDTH-1:0] key_r2;
reg [WIDTH-1:0] key_down_r;//寄存key_down信号
reg flag;//计数器计数标志

wire add_cnt_20ms;//开始计数信号
wire end_cnt_20ms;//结束计数信号
wire [WIDTH-1:0] nedge;//下降沿信号

//****************************************************************
//--同步、打两拍
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        key_r0 <= {WIDTH{1'b1}};//3'b111
        key_r1 <= {WIDTH{1'b1}};
        key_r2 <= {WIDTH{1'b1}};
    end
    else begin
        key_r0 <= key_in;
        key_r1 <= key_r0;
        key_r2 <= key_r1;
    end
end

//****************************************************************
//--下降沿检测
//****************************************************************
assign nedge = ~key_r1 & key_r2;

//****************************************************************
//--flag
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        flag <= 1'b0;
    end
    else if(nedge) begin//检测到下降沿时,开始计数
        flag <= 1'b1;
    end
    else if(end_cnt_20ms)begin//计满20ms时,停止计数
        flag <= 1'b0;
    end
    else begin
        flag <= flag;
    end
end

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

assign add_cnt_20ms = flag;
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == MAX_20ms;

//****************************************************************
//--key_down赋值
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        key_down_r <= 'b0;
    end
    else if(end_cnt_20ms) begin
        key_down_r <= ~key_r2;
    end
    else begin
        key_down_r <= 'b0;
    end
end
assign key_down=key_down_r;

endmodule //key_filter
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Verilog按键消抖实现可以通过以下步骤进行: 1. 首先,了解按键抖动的原因和消抖的原理。按键抖动是指在按下或释放按键时,由于机械性质或电气性质的原因,导致按键信号出现多次变化的现象。消抖的原理是通过延时和状态判断来确定按键的有效状态。 2. 在Verilog中,可以使用状态机的方式来实现按键消抖。首先,定义一个状态机的状态,包括按键未按下、按键按下、按键释放等状态。然后,通过检测按键信号的变化来切换状态。 3. 在状态机的每个状态中,可以设置一个延时计数器来延时一段时间,以消除按键抖动。在延时结束后,再次检测按键信号的状态,如果仍然保持一致,则认为按键有效。 4. 根据具体需求,可以在按键有效时执行相应的操作,比如控制LED的亮灭。 综上所述,Verilog按键消抖实现可以通过状态机和延时计数器来实现,具体的代码实现可以参考引用\[1\]中提供的文章结尾的示例代码。 #### 引用[.reference_title] - *1* [FPGA学习-Verilog实现独立按键消抖](https://blog.csdn.net/qq_46490027/article/details/123338108)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Verilog中按键消抖检测的实现](https://blog.csdn.net/CLL_caicai/article/details/105159165)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Verilog实现按键消抖](https://blog.csdn.net/m0_54218263/article/details/121328750)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值