0
收藏
微博
微信
复制链接

FPGA学习-按键消抖模块设计与验证A

提问于
2024-12-14 01:06

  

Fpga 学习笔记7(状态机设计实例):因内容比较简单,而且在这篇日志中也有相关的知识点,就不写了。

该集主要知识点:

1、利用状态机实现滤除物理按键所产生的抖动波形。

2、非阻塞赋值的巧妙运用

3、将状态机与计数器功能组合使用

4、在仿真代码中利用随机数产生延时随机的时间。

5、task 运用方式、以及将仿真测试代码模块化

 

按键抖动的现象与状态机对应的状态:

ea405caf486a21e45729a23eb888cd.jpg 

一、源程序

/* 实验名称:按键消抖模块设计与验证

 * 功能实现:滤除按键抖动的波形

 */  

`define  DEC_TIME_CNT    ((20 * 1000 * 1000) / 20 - 1)

module mytest(clk, rst_n, key_in, key_flag, key_state);

 

    input clk, rst_n, key_in;

    output reg key_flag, key_state;

   

    reg[3:0] state;                    // 状态机状态

    reg key_old, key_cur;            // key状态

    wire pedge, nedge;                // 边沿状态

   

    // 50MHz的时钟 = 1/50M = 0.02us = 20ns

    // 20ms = 20_000_000ns    / 20ns = 1000_000

    reg[19:0] time_cnt;            // 计数器计数值

    reg time_full;                    // 计数器已经达到指定的时间       

    reg time_en;                    // 计数器使能

   

    localparam                        // 状态机几种状态的标志

        IDLE        = 4'b0001,        // 空闲,即高电平状态,为按下状态

        DING        = 4'b0010,        // 按下时抖动状态

        DOWN        = 4'b0100,        // 可以确定是处于按下状态而不是抖动状态

        UING        = 4'b1000;        // 弹起时抖动状态

   

   

    // 边沿检测

    always@(posedge clk, negedge rst_n)

        if(!rst_n)begin

                key_cur <= 1'b0;

                key_old <= 1'b0;

        end

        else

            begin   

                key_cur <= key_in;        // 这里由于采用了非阻塞赋值

                key_old <= key_cur;        // 所以在同一个时钟内 key_old 采样 key_curr 的是旧的值

            end

           

    // 判断上升沿、下降沿

    assign pedge = !key_old & key_cur;    // 原来为低电平,现在为高电平,则表示检测到上升沿

    assign nedge = key_old & !key_cur;    // 原来为高电平,现在为低电平,则表示检测到下降沿   

   

   

    // 计数功能

    always@(posedge clk, negedge rst_n)

        if(!rst_n)begin

                time_cnt <= 20'd0;   

                //    time_en <= 1'b0;  // 这里不能再次赋值,因为在状态机的程序块中需要对该信号赋值

                // 一个信号不能在多个 always 块中赋值

        end

        else if(time_en)

            time_cnt <= time_cnt + 1'b1;

        else

            time_cnt <= 20'd0;

   

   

    // 检测时间是否已经到了 这里指定 20ms 的时间

    always@(posedge clk, negedge rst_n)

        if(!rst_n)

            time_full <= 1'b0;

        else if(time_cnt == `DEC_TIME_CNT)   

            time_full <= 1'b1;

        else

            time_full <= 1'b0;

       

       

    // 状态机

    always@(posedge clk, negedge rst_n)

        if(!rst_n)begin

            state <= IDLE;

            key_flag <= 1'b0;

            key_state <= 1'b1;

        end

        else begin

            case(state)

                IDLE: begin                        // 空闲状态:按键没有被按下

                    key_flag <= 1'b0;            // 在空闲状态 按键需要清零

                    if(nedge) begin            // 检测到下降沿

                        state <= DING;            // 设置状态为 DING,下个时钟上升沿将会进入另外一个分支

                        time_en <= 1'b1;         // 启动定时器            

                    end

                    else

                        state <= IDLE;            // 依据是高电平,设置状态为 DING

                end

               

                DING: begin                        // 滤波抖动,按下时产生的抖动状态

                    if(time_full) begin         // 如果指定的时间内没有上升沿

                        key_flag <= 1'b1;        // 则表示处于稳定状态,进入按下状态

                        key_state <= 1'b0;    // 表示按下

                        state <= DOWN;            // 设置状态为按下

                        time_en <= 1'b0;        // 关闭定时器

                    end

                    else if(pedge) begin        // 如果指定的时间内出现上升沿说明是处于抖动状态

                            state <= IDLE;        // 重新设置为空闲状态

                            time_en = 1'b0;    // 并且关闭定时器

                    end

收藏 19 0 0