15 ABC基于状态机的按键消抖原理与状态转移图

1. 基于状态机的按键消抖

1.1 什么是按键?

从按键结构图10-1可知,按键按下时,接点(端子)与导线接通,松开时,由于弹簧的反作用力,接点(端子)与导线断开。

从原理图10-2可知,按键按下时为低电平,未按下为高电平

1.2 为什么要消抖?

1.3 基于按键消抖的状态转移图

2. 写设计代码,仿真代码并仿真(未使用随机函数的测试)

  1. 设计代码
cpp 复制代码
module key_filter(
    clk,
    rstn,
    key,
//    key_p_flag,
//    key_r_flag,
    key_flag,
    key_state
);
    
    input clk;
    input rstn;
    input key;
//   output reg key_p_flag;
//   output reg key_r_flag;
    output reg key_flag;
    output reg key_state;
    
    //边沿检测
    reg [1:0] r_key;
    always@(posedge clk)
        r_key <= {r_key[0], key}; 
//    reg [1:0] r_key;    
//    always@(posedge clk)begin
//        r_key[0] <= key;
//        r_key[1] <= r_key[0]; 
//    end
    wire nedge_key;
    wire pedge_key;
    assign nedge_key = (r_key == 2'b10);
    assign pedge_key = (r_key == 2'b01);
    
    
    reg [1:0]state;
    reg [19:0] cnt;
    always@(posedge clk or negedge rstn)
    if(!rstn)begin
        state <= 0;
        cnt <= 0;
//        key_p_flag <= 0;
//        key_r_flag <= 0;
        key_flag <= 0;
        key_state <= 1;
    end
    else
    case(state)
        0: 
        begin
//            key_r_flag <= 0;
            key_flag <= 0;
            if(nedge_key == 1) begin
                state <= 1;
            end
            else
                state <= 0;
        end 
        
        1:
        begin
            if((pedge_key == 1) && (cnt < 1000000 - 1))begin
                state <= 0;
                cnt <= 0;
            end
            else if((pedge_key == 0) && (cnt >= 1000000 - 1))begin
                state <= 2;
//               key_p_flag <= 1'd1;
                key_flag <= 1'd1;
                key_state <= 0;
                cnt <= 0;
            end
            else
                cnt <= cnt + 1'd1;
        end
        
        2:
        begin
//           key_p_flag <= 0;
            key_flag <= 0;
            if(pedge_key == 1)
                state <= 3;
            else
                state <= 2;
        end
        
        3:
        begin
            if((nedge_key == 1) && (cnt < 1000000 - 1))begin
                state <= 2;
                cnt <= 0;
            end
            else if((nedge_key == 0) && (cnt >= 1000000 - 1))begin
                state <= 0;
//                key_r_flag <= 1;
                key_flag <= 1'd1;
                key_state <= 1;
                cnt <= 0;
            end
            else
                cnt <= cnt + 1'd1;
         end
        
    endcase
    
endmodule
  1. 仿真代码
cpp 复制代码
`timescale 1ns / 1ps

module key_filter_tb();
    
    reg clk;
    reg rstn;
    reg key;
//    wire key_p_flag;
//    wire key_r_flag;
    wire key_flag;
    wire key_state;
    
    key_filter key_filter_inst(
        .clk(clk),
        .rstn(rstn),
        .key(key),
//        .key_p_flag(key_p_flag),
//       .key_r_flag(key_r_flag),
        .key_flag(key_flag),
        .key_state(key_state)
    );
    
    initial clk = 1;
    always #10 clk = ~clk;
    
    initial begin
        rstn = 0;
        key = 1;
        #201;
        rstn = 1;
        #200;
        key = 1;
        #50000000;
        key = 0;
        #30000;
        key = 1;
        #30000;
        key = 0;
        #30000;
        key = 1;
        #30000;
        key = 0;
        #50000000;
        key = 1;
        #30000;
        key = 0;
        #30000;
        key = 1;
         #30000;
        key = 0;
        #30000;
        key = 1;
        #50000000;
        $stop;
    end



endmodule
  1. 仿真波形

3. 基于verilog系统函数random的随机测试下的按键抖动(tb编写语法)

通过系统函数random产生一个随机的延迟值,来模拟真实情况下的延迟。

3.1 系统函数random的两个例子:

  1. 产生一个[-(b+1): (b-1)]的随机数:$random% b;

2.产生一个[0: b-1]的随机数:{$random}% b;;

修改后的仿真代码:

cpp 复制代码
`timescale 1ns / 1ps

module key_filter_tb();
    
    reg clk;
    reg rstn;
    reg key;
//    wire key_p_flag;
//    wire key_r_flag;
    wire key_flag;
    wire key_state;
    
    key_filter key_filter_inst(
        .clk(clk),
        .rstn(rstn),
        .key(key),
//        .key_p_flag(key_p_flag),
//       .key_r_flag(key_r_flag),
        .key_flag(key_flag),
        .key_state(key_state)
    );
    
    initial clk = 1;
    always #10 clk = ~clk;
    
    reg [19:0] rand;
    initial begin
        rstn = 0;
        key = 1;
        #201;
        rstn = 1;
        #200;
        press_key(1);
        $stop;
    end
    
    task press_key;
        input [2:0] seed;
        begin
            key = 1;
            #20000000; 
            repeat(5) begin
                rand = {$random(seed)} % 9999999; //产生0到9999999ns的延迟
                #rand key = ~key;
            end
            key = 0;
            #40000000;
            repeat(5) begin
                rand = {$random(seed)} % 9999999; //产生0到9999999ns的延迟
                #rand key = ~key;
            end
            key = 1;
            #40000000;
        end
    endtask



endmodule

4. 调试(产生多余38ns的原因)

相关推荐
知识分享小能手几秒前
Vue3 学习教程,从入门到精通,Vue 3 + Tailwind CSS 全面知识点与案例详解(31)
前端·javascript·css·vue.js·学习·typescript·vue3
茴香豆的茴131 分钟前
转码刷 LeetCode 笔记[2]:203. 移除链表元素(python)
笔记·leetcode·链表
wdfk_prog1 小时前
[Linux]学习笔记系列 -- [arm][lds]
linux·运维·arm开发·笔记·学习
范纹杉想快点毕业2 小时前
C 语言主控开发与显控开发能力体系及技术栈详解,STM32、QT、嵌入式、边缘系统显示
stm32·单片机·tcp/ip·microsoft·fpga开发·51单片机·wpf
晨非辰2 小时前
#C语言——刷题攻略:牛客编程入门训练(六):运算(三)-- 涉及 辗转相除法求最大公约数
c语言·开发语言·经验分享·学习·学习方法·visual studio
无名咸鱼2 小时前
CICD部署流程详解文档笔记
笔记·ci/cd
Gloria_niki3 小时前
爬虫与数据分析结合案例学习总结
爬虫·学习·数据分析
秋风战士3 小时前
通信算法之306:精通FPGA-笔记核心
笔记·fpga开发
一颗正在价投韭菜3 小时前
《范仲淹传》读书笔记与摘要
笔记·学习·范仲淹
微小冷4 小时前
OV5640 相机开发流程
fpga开发·verilog·ov5640·双目相机·相机开发