按键消抖补充

文章详细介绍了如何使用Verilog硬件描述语言实现按键消抖功能,包括基于延时的方法和状态机实现。提供了4个按键的消抖模块源码,并附带了相应的仿真文件,以验证消抖效果。通过状态机,能更灵活地处理按键的按下和松开抖动,确保稳定信号的输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.补充方法原理

  • 原理:仍然是采用延时方法

  • 时序图如下
    在这里插入图片描述

  • 该时序图实现的是一位按键的情况。在使用消抖后按键信号的时候,可以在顶层调用消抖模块多次;也可以写多位按键消抖情况

2.补充方法源码及仿真

  • 上文说到我们可能会需要多位按键消抖的情况,由于笔者常用的EP4CE6F17C8器件通常是四个按键,下文展示了4个按键的消抖模块
/**************************************功能介绍***********************************
Date	:2023年7月27日10:42:24 
Author	: Alegg xy.
Version	: 2.0
Description: 按键消抖(4个按键)
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module key_debounce ( 
    input				clk		,
    input				rst_n	,
    input		[3:0]	key_in  ,
    output  reg [3:0]   key_out //输出脉冲信号
);								 
//---------<参数定义>--------------------------------------------------------- 
    parameter TIME_20MS = 20'd1000_000;//20ms

//---------<内部信号定义>-----------------------------------------------------
    reg         [3:0]   key_r0          ;//同步
    reg         [3:0]   key_r1          ;//打两拍
    reg         [3:0]   key_r2          ;
    wire                nedge           ;//下降沿
    reg                 flag            ;//计数器计数的标志信号(按键按下抖动标志)

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

//****************************************************************
//--同步打拍
//****************************************************************
    //同步,将key_in信号,同步到clk时钟域下面
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            key_r0 <= 4'b1111;
        end 
        else begin 
            key_r0 <= key_in;
        end 
    end
    
    //打两拍
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            key_r1 <= 4'b1111;
            key_r2 <= 4'b1111;
        end 
        else begin 
            key_r1 <= key_r0;
            key_r2 <= key_r1;
        end 
    end
    
//****************************************************************
//--nedge 
//****************************************************************
    assign nedge = (~key_r1 & key_r2) != 0 ;//下降沿检测


//****************************************************************
//--cnt_20ms
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_20ms <= 'd0;
        end 
        else if(add_cnt_20ms)begin 
            if(end_cnt_20ms)begin 
                cnt_20ms <= 'd0;
            end
            else begin 
                cnt_20ms <= cnt_20ms + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_20ms = flag;
    assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == TIME_20MS - 1;
    
//****************************************************************
//--flag
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            flag <= 'd0;
        end 
        else if(nedge)begin 
            flag <= 1'b1;
        end 
        else if(end_cnt_20ms)begin
            flag <= 1'b0;
        end
    end

//****************************************************************
//--key_out
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            key_out <= 4'd0000;
        end 
        else if(end_cnt_20ms)begin 
            key_out <= ~key_r2;
        end 
        else begin 
            key_out <= 4'b0000;
        end 
    end

endmodule
  • 仿真文件
`timescale 1ns/1ns
    
module tb_key_debounce();

//激励信号定义 
    reg				tb_clk  	;
    reg				tb_rst_n	;
    reg		[3:0]   tb_key_in	;

//输出信号定义	 
    wire	[3:0]		tb_key_out	;

//时钟周期参数定义	
    parameter		CLOCK_CYCLE = 20;   

//模块例化
    key_debounce u_key_debounce(	
    .clk		(tb_clk			),
    .rst_n		(tb_rst_n		),
    .key_in		(tb_key_in		),

    .key_out	(tb_key_out		) 
    );

//产生时钟
    initial 		tb_clk = 1'b0;
    always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;

    defparam u_key_debounce.TIME_20MS = 100;
    integer i,j;
//产生激励
    initial  begin 
        tb_rst_n = 1'b1;
        tb_key_in = 4'b1111;
        #(CLOCK_CYCLE*2);
        tb_rst_n = 1'b0;
        #(CLOCK_CYCLE*20);
        tb_rst_n = 1'b1;

        //****************************************************************
        //--模拟按下key[0]
        //****************************************************************
        #1;
        tb_key_in[0] = 1'b0;
        for (j =0 ; j<8 ; j=j+1) begin//模拟抖动
            i = {$random}%500;
            #i;
            tb_key_in[0] = i;
        end
        tb_key_in[0] = 1'b0;

        wait(u_key_debounce.end_cnt_20ms);

        #1000;
        tb_key_in[0] = 1'b1;


        //****************************************************************
        //--模拟同时按下key[1]和key[2]
        //****************************************************************
        #1;
        tb_key_in = 4'b1001;
        for (j=0 ; j<8 ; j=j+1) begin
            i = {$random}%500;
            #i;
            tb_key_in[1] = i;
            tb_key_in[2] = i;
        end
        tb_key_in = 4'b1001;

        wait(u_key_debounce.end_cnt_20ms);

        #1000;
        tb_key_in = 4'b1111;
        
        #1000;
        $stop;
    end

endmodule 
  • 仿真效果
    在这里插入图片描述

3.扩展方法:状态机实现消抖

  • 状态转移图
    在这里插入图片描述

  • 代码

/**************************************功能介绍***********************************
Date	: 2023年7月27日15:52:18
Author	: Alegg xy.
Version	: 3.0
Description: 状态机实现按键消抖
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module fsm_key_debounce( 
    input				clk		,
    input				rst_n	,
    input		[3:0]	key_in	,

    output	reg	[3:0]	key_out	
);								 
//---------<参数定义>--------------------------------------------------------- 
    parameter MAX_20ms = 20'd1_000_000;
    reg         [3:0]   key_r0          ;
    reg         [3:0]   key_r1          ;
    reg         [3:0]   key_r2          ; 
    wire                nedge          ;
    wire                pedge          ;
    reg                 flag            ;     
    reg			[19:0]	cnt_20ms	   	;
    wire				add_cnt_20ms	;
    wire				end_cnt_20ms	;


//---------<内部信号定义>-----------------------------------------------------
    //同步,将key_in信号,同步到clk时钟域下面
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            key_r0 <= 4'b1111;
        end 
        else begin 
            key_r0 <= key_in;
        end 
    end
    
    //打两拍
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            key_r1 <= 4'b1111;
            key_r2 <= 4'b1111;
        end 
        else begin 
            key_r1 <= key_r0;
            key_r2 <= key_r1;
        end 
    end
    
//****************************************************************
//--nedge和pedge定义 
//****************************************************************
    assign nedge = (~key_r1 & key_r2) != 0;//下降沿检测
    assign pedge = (key_r1 & ~key_r2) != 0;//上升沿检测

//****************************************************************
//--cnt_20ms
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
       if(!rst_n)begin
            cnt_20ms <= 19'd0;
        end 
        else if(add_cnt_20ms)begin 
            if(end_cnt_20ms)begin 
                cnt_20ms <= 19'd0;
            end
            else begin 
                cnt_20ms <= cnt_20ms + 1'b1;
            end 
        end
    end 
    
    assign add_cnt_20ms = flag;
    assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == MAX_20ms - 1;
    
//****************************************************************
//--flag
//****************************************************************
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            flag <= 'd0;
        end 
        else if(nedge || pedge)begin 
            flag <= 1'b1;
        end 
        else if(end_cnt_20ms)begin
            flag <= 1'b0;
        end
    end
    
//****************************************************************
//--状态机
//****************************************************************
    //状态机参数定义
    localparam  IDLE            = 4'b0001,//初始状态
                FILTTER_DOWN    = 4'b0010,//按下抖动
                HOLD_DOWN       = 4'b0100,//稳定信号
                FILTER_UP       = 4'b1000;//松开抖动
    
    reg 	[3:0]	cstate     ;//现态
    reg	    [3:0]	nstate     ;//次态
    
    //第一段:时序逻辑描述状态转移
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            cstate <= IDLE;
        end 
        else begin 
            cstate <= nstate;
        end 
    end
    
    //第二段:组合逻辑描述状态转移规律和状态转移条件
    always @(*) begin
        case(cstate)
            IDLE            :
                if (nedge) begin
                    nstate = FILTTER_DOWN;
                end
                else begin
                    nstate = cstate;
                end 
            FILTTER_DOWN    :
                if (end_cnt_20ms) begin
                    nstate = HOLD_DOWN;
                end
                else begin
                    nstate = cstate;
                end 
            HOLD_DOWN       :
                if (pedge) begin
                    nstate = FILTER_UP;
                end
                else begin
                    nstate = cstate;
                end 
            FILTER_UP       :
                if (end_cnt_20ms) begin
                    nstate = IDLE;
                end
                else begin
                    nstate = cstate;
                end 
            default : nstate = cstate;
        endcase
    end
                
    //第三段:描述输出,时序逻辑或组合逻辑皆可
    always @(posedge clk or negedge rst_n)begin 
        if(!rst_n)begin
            key_out <= 4'b0000;
        end 
        else begin 
            case (cstate)
                IDLE        :key_out <= 4'b0000        ;
                FILTTER_DOWN:key_out <= 4'b0000        ;
                HOLD_DOWN   :key_out <= ~key_r2 && pedge;
                FILTER_UP   :key_out <= 4'b0000        ; 
                default     :key_out <= 4'b0000        ;
            endcase
        end 
    end       
                
endmodule
  • 代码理解的关键之处

    1、4位按键信号赋值需要4位位宽的二进制数

    2、上升沿和下降沿如何求值

    3、何时计数开始

    4、状态转换条件

    5、如何赋值可以实现输出的脉冲信号

  • 仿真文件

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

//激励信号定义 
    reg				tb_clk  	;
    reg				tb_rst_n	;
    reg		[3:0]	tb_key_in	;

//输出信号定义	 
    wire	[3:0]	tb_key_out  ;

//时钟周期参数定义	
    parameter		CLOCK_CYCLE = 20;   
    defparam u_fsm_key_debounce.MAX_20ms = 10;
//模块例化
    fsm_key_debounce u_fsm_key_debounce(	
    .clk		(tb_clk			),
    .rst_n		(tb_rst_n		),
    .key_in		(tb_key_in		),

    .key_out	(tb_key_out		) 
    );

//产生时钟
    initial 		tb_clk = 1'b0;
    always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;

//产生激励
    initial  begin 
        tb_rst_n = 1'b1;
        tb_key_in = 4'b1111;
        #(CLOCK_CYCLE*2);
        tb_rst_n = 1'b0;
        #(CLOCK_CYCLE*20);
        tb_rst_n = 1'b1;
        #7;
        tb_key_in = 4'b1110;
        #13;
        tb_key_in = 4'b1111;
        #8;
        tb_key_in = 4'b1110;
        #10;
        tb_key_in = 4'b1111;
        #5;
        tb_key_in = 4'b1110;
        #1000;
        tb_key_in = 4'b1111;
        #1000;
        $stop;


    end

endmodule 
  • 仿真效果
    在这里插入图片描述
标题“51单片机通过MPU6050-DMP获取姿态角例程”解析 “51单片机通过MPU6050-DMP获取姿态角例程”是一个基于51系列单片机(一种常见的8位微控制器)的程序示例,用于读取MPU6050传感器的数据,并通过其内置的数字运动处理器(DMP)计算设备的姿态角(如倾斜角度、旋转角度等)。MPU6050是一款集成三轴加速度计和三轴陀螺仪的六自由度传感器,广泛应用于运动控制和姿态检测领域。该例程利用MPU6050的DMP功能,由DMP处理复杂的运动学算法,例如姿态融合,将加速度计和陀螺仪的数据进行整合,从而提供稳定且实时的姿态估计,减轻主控MCU的计算负担。最终,姿态角数据通过LCD1602显示屏以字符形式可视化展示,为用户提供直观的反馈。 从标签“51单片机 6050”可知,该项目主要涉及51单片机和MPU6050传感器这两个关键硬件组件。51单片机基于8051内核,因编程简单、成本低而被广泛应用;MPU6050作为惯性测量单元(IMU),可测量设备的线性和角速度。文件名“51-DMP-NET”可能表示这是一个与51单片机及DMP相关的网络资源或代码库,其中可能包含C语言等适合51单片机的编程语言的源代码、配置文件、用户手册、示例程序,以及可能的调试工具或IDE项目文件。 实现该项目需以下步骤:首先是硬件连接,将51单片机与MPU6050通过I2C接口正确连接,同时将LCD1602连接到51单片机的串行数据线和控制线上;接着是初始化设置,配置51单片机的I/O端口,初始化I2C通信协议,设置MPU6050的工作模式和数据输出速率;然后是DMP配置,启用MPU6050的DMP功能,加载预编译的DMP固件,并设置DMP输出数据的中断;之后是数据读取,通过中断服务程序从DMP接收姿态角数据,数据通常以四元数或欧拉角形式呈现;再接着是数据显示,将姿态角数据转换为可读的度数格
MathorCup高校数学建模挑战赛是一项旨在提升学生数学应用、创新和团队协作能力的年度竞赛。参赛团队需在规定时间内解决实际问题,运用数学建模方法进行分析并提出解决方案。2021年第十一届比赛的D题就是一个典型例子。 MATLAB是解决这类问题的常用工具。它是一款强大的数值计算和编程软件,广泛应用于数学建模、数据分析和科学计算。MATLAB拥有丰富的函数库,涵盖线性代数、统计分析、优化算法、信号处理等多种数学操作,方便参赛者构建模型和实现算法。 在提供的文件列表中,有几个关键文件: d题论文(1).docx:这可能是参赛队伍对D题的解答报告,详细记录了他们对问题的理解、建模过程、求解方法和结果分析。 D_1.m、ratio.m、importfile.m、Untitled.m、changf.m、pailiezuhe.m、huitu.m:这些是MATLAB源代码文件,每个文件可能对应一个特定的计算步骤或功能。例如: D_1.m 可能是主要的建模代码; ratio.m 可能用于计算某种比例或比率; importfile.m 可能用于导入数据; Untitled.m 可能是未命名的脚本,包含临时或测试代码; changf.m 可能涉及函数变换; pailiezuhe.m 可能与矩阵的排列组合相关; huitu.m 可能用于绘制回路图或流程图。 matlab111.mat:这是一个MATLAB数据文件,存储了变量或矩阵等数据,可能用于后续计算或分析。 D-date.mat:这个文件可能包含与D题相关的特定日期数据,或是模拟过程中用到的时间序列数据。 从这些文件可以推测,参赛队伍可能利用MATLAB完成了数据预处理、模型构建、数值模拟和结果可视化等一系列工作。然而,具体的建模细节和解决方案需要查看解压后的文件内容才能深入了解。 在数学建模过程中,团队需深入理解问题本质,选择合适的数学模
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值