4*4按键消抖(野火升腾拓展)

解释:

1、扫描控制:
    •     scan_cnt  从 0 计数到 49999(共 50000 个周期)
    •    每 1ms(1kHz)产生一个  valid_tick  脉冲
    2、行切换:
    •     valid_tick  触发  row_sel  状态机,依次激活 4 行
    3、消抖与缓存:
    •    每列信号独立消抖( col_flag )
    •    在  valid_tick  上升沿将消抖结果写入  key_state  对应行

 

代码

// 按键消抖模块(单路)
module key_filter #(
    parameter CNT_MAX = 20'd999_999  // 约20ms@50MHz
)(
    input wire sys_clk,
    input wire sys_rst_n,
    input wire key_in,
    output reg key_flag
);

    reg [19:0] cnt_20ms;

    always @(posedge sys_clk or negedge sys_rst_n) begin
        if (!sys_rst_n)
            cnt_20ms <= 20'd0;
        else if (key_in)
            cnt_20ms <= 20'd0;
        else if (cnt_20ms == CNT_MAX)
            cnt_20ms <= cnt_20ms;
        else
            cnt_20ms <= cnt_20ms + 1'b1;
    end

    always @(posedge sys_clk or negedge sys_rst_n) begin
        if (!sys_rst_n)
            key_flag <= 1'b0;
        else
            key_flag <= (cnt_20ms == CNT_MAX - 1'b1);
    end
endmodule


// 4x4矩阵键盘扫描与消抖顶层模块
module matrix_keypad #(
    parameter CNT_MAX = 20'd999_999,     // 消抖计数最大值
    parameter SCAN_DIV = 16'd50000       // 扫描分频系数 (50MHz -> 1kHz)
)(
    input wire sys_clk,           // 50MHz系统时钟
    input wire sys_rst_n,         // 复位,低有效
    input wire [3:0] col_in,      // 4列输入(外部上拉)
    output wire [3:0] row_out,    // 4行输出
    output reg [15:0] key_state   // 按键状态:1表示按下
);

    // 扫描控制信号
    reg [15:0] scan_cnt;          // 扫描分频计数器 (16位)
    reg valid_tick;               // 采样有效脉冲
    reg [3:0] row_sel;            // 当前行选择

    // 消抖后列信号
    wire [3:0] col_flag;

    //===== 扫描控制器 =====//
    // 生成1kHz扫描时钟 (50MHz / 50000 = 1kHz)
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if (!sys_rst_n) begin
            scan_cnt <= 16'd0;
            valid_tick <= 1'b0;
        end else if (scan_cnt == SCAN_DIV - 1) begin
            scan_cnt <= 16'd0;
            valid_tick <= 1'b1;
        end else begin
            scan_cnt <= scan_cnt + 1'b1;
            valid_tick <= 1'b0;
        end
    end

    //===== 行选择状态机 =====//
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if (!sys_rst_n)
            row_sel <= 4'b1110;  // 默认选中第0行
        else if (valid_tick) begin  // 每1ms切换一行
            case (row_sel)
                4'b1110: row_sel <= 4'b1101; // 第0行 → 第1行
                4'b1101: row_sel <= 4'b1011; // 第1行 → 第2行
                4'b1011: row_sel <= 4'b0111; // 第2行 → 第3行
                4'b0111: row_sel <= 4'b1110; // 第3行 → 第0行
                default: row_sel <= 4'b1110;
            endcase
        end
    end

    assign row_out = row_sel;

    //===== 列消抖(复用4个单路消抖模块)=====//
    genvar i;
    generate
        for (i=0; i<4; i=i+1) begin : debounce_inst
            key_filter #(.CNT_MAX(CNT_MAX))
            u_debounce (
                .sys_clk(sys_clk),
                .sys_rst_n(sys_rst_n),
                .key_in(col_in[i]),
                .key_flag(col_flag[i])
            );
        end
    endgenerate

    //===== 状态缓存 =====//
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if (!sys_rst_n)
            key_state <= 16'd0;
        else if (valid_tick) begin  // 仅在行切换时更新状态
            case (row_sel)
                4'b1110: key_state[3:0] <= col_flag;   // 第0行状态
                4'b1101: key_state[7:4] <= col_flag;   // 第1行状态
                4'b1011: key_state[11:8] <= col_flag;  // 第2行状态
                4'b0111: key_state[15:12] <= col_flag; // 第3行状态
            endcase
        end
    end

endmodule

### Verilog 实现按键方法 在FPGA设计中,按键是一个常见的需求。由于机械开关的物理特性,在按下或释放瞬间会产生多个瞬态信号,这会影响系统的稳定性[^1]。 为了除这些瞬态干扰并获得稳定的输入信号,通常采用硬件去电路或者软件延时算法来解决这个问题。下面介绍一种基于计数器的时间延迟法: #### 时间延迟法实现按键 通过引入一个较大的时间窗口(例如50ms),在这个时间段内忽略所有的跳变沿变化,只有当连续保持高电平或低电平时才认为是一次有效的按压事件。具体做法如下所示: ```verilog module key_debounce( input wire clk, // 时钟信号 input wire rst_n, // 复位信号(低有效) input wire key_in, // 原始按键输入 output reg key_out // 除毛刺后的按键输出 ); parameter DEBOUNCE_CNT_MAX = 25'd250000; // 计数值对应约50ms (假设clk=50MHz) reg [24:0] debounce_cnt; wire cnt_max; assign cnt_max = (debounce_cnt >= DEBOUNCE_CNT_MAX); always @(posedge clk or negedge rst_n) begin if (!rst_n) begin debounce_cnt <= 25'b0; key_out <= 1'b0; end else if(!cnt_max && key_in != key_out) begin debounce_cnt <= debounce_cnt + 1'b1; end else if(cnt_max) begin debounce_cnt <= 25'b0; key_out <= key_in; end end endmodule ``` 此模块接收原始按键信号`key_in`作为输入,并经过内部逻辑处理后给出稳定化的输出`key_out`。每当检测到输入发生变化时启动定时器;一旦超过设定阈值,则更新最终结果并重置计数器等待下一次触发[^3]。 上述代码实现了较为简单的按键功能,适用于大多数应用场景下的IO口操作。对于更复杂的要求还可以考虑加入更多优化措施,比如双稳态触发电路等高级技术[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值