问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

FPGA按键消抖模块设计详解

创作时间:
作者:
@小白创作中心

FPGA按键消抖模块设计详解

引用
CSDN
1.
https://blog.csdn.net/wjh123hhh/article/details/146116846

在FPGA开发中,按键消抖是一个常见的需求。本文将通过一个具体的实例,介绍如何使用状态机实现按键消抖功能。本文将详细介绍状态机的设计、边沿检测以及计数器的实现方法。

本篇文章通过使用Quartus ii 13.1进行代码编写,并编译完成,实现按键消抖功能。
在实际电路设计过程中,按键消抖可以通过添加电容的硬件操作实现,本篇文章通过按键消抖的实现来学习一段式状态机和testbench文件的编写。

状态机代码编写部分

首先,要了解我们按键消抖模块包括三个输入信号,两个输出信号。分别是时钟输入信号,系统复位信号,按键输入信号三个输入;以及按键被按下标志信号和按键状态。

input		Clk;		//系统时钟
input		Rst_n;	    //系统复位
input		key_in;	    //按键输入

output	reg	key_flag;	//按键被按下标志
output	reg	key_state;	//按键状态,不按为1,按下为0  

主要通过一段式状态机来实现功能。我定义了四个不同的状态,方便代码的编写。分别是空闲状态(IDEL),按下滤波状态(FILTER0),按下状态(DOWN)和释放滤波状态(FILTER1)。

初始时,状态设置为空闲状态,等待到按键输入产生下降沿时,进入FILTER0状态,同时发送使能信号(en_cnt)开始计数,一般认为20ms内一直保持低电平即为按下,反之为抖动;未检测到下降沿,一直在空闲状态检测。

IDEL	:
    begin
        if(nedge) begin			//检测到下降沿,跳转至下一状态,同时开始计数
            state <= FILTER0;
            en_cnt <= 1'b1;		
        end
        else begin				//其余时间,状态不变
            state <= IDEL;
        end
    end  

进入FILTER0状态后,首先判断计数是否达到20ms(通过计数器设置计数满时,产生信号cnt_full),当检测到cnt_full信号,认为按键按下,并非抖动,可进入DOWN状态,同时停止使能信号(en_cnt)。赋予key_flag(按键被按下标志)高电平、key_state(按键状态)低电平;同时判断是否产生上升沿,如果上升沿出现在cnt_full信号前,说明此次判断为抖动,不是按下,停止使能计数,返回空闲状态(IDEL)重新开始;即未检测到cnt_full,又未检测到上升沿时,保持状态FILTER0不变。

FILTER0	:
    if(cnt_full) begin		//如果计数器记满了,跳转至下一状态,记满,使能信号拉低
        key_flag <= 1'b1;	//按键按下
        key_state <= 1'b0;	//按键状态为按下,低电平
        en_cnt <= 1'b0;
        state <= DOWN;
    end
    else if(pedge) begin	//如果计数器还没记满就检测到上升沿,回到等待状态
        en_cnt <= 1'b0;		//使能信号拉低
        state <= IDEL;
    end
    else begin				//其余时间,保持状态不变
        state <= FILTER0;
    end  

在DOWN状态开始时,首先将key_flag(按键被按下标志)恢复到低电平,一般我们仅需该信号产生一个周期的脉冲即可。同时检测上升沿的到来,当检测到上升沿到来,开启使能en_cnt,并进入FILTER1状态;其余时间,保持DOWN状态不变。

DOWN	:
    begin
        key_flag <= 1'b0;		//按键按下产生一个周期的脉冲
        if(pedge) begin			//检测到上升沿,跳转至下一状态,开始计数
            en_cnt <= 1'b1;
            state <= FILTER1;
        end
        else 
            state <= DOWN;		//其余时间,状态机状态不变
    end  

进入FILTER1状态后,当检测到cnt_full信号,停止使能信号en_cnt,同时拉高key_state(按键状态),此处计数原则与上述FILTER0一致;如果在cnt_full到来前检测到下降沿,说明此次判断为抖动,返回DOWN状态重新判断;其余时间保持DOWN状态不变。

FILTER1	:
    if(cnt_full) begin		//计数器记满,跳转至等待状态,按键状态为未按下
        key_state <= 1'b1;
        state <= IDEL;
    end
    else if(nedge) begin	//检测到下降沿,返回按下状态,使能计数拉低
        en_cnt <= 1'b0;
        state <= DOWN;
    end
    else 					//其余时间,保持状态不变
        state <= FILTER1;  

边沿检测部分

利用非阻塞赋值逐级保存按键输入信号(key_in),下降沿来临时key_tempa = 0,key_tempb = 1,进行下图所示操作。反之,上升沿来临时key_tempa = 1,key_tempb = 0。

//边沿检测
    always @(posedge Clk or negedge Rst_n) begin
        if(!Rst_n) begin
            key_tempa <= 1'b0;
            key_tempb <= 1'b0;
        end
        else begin
            key_tempa <= key_in_sb;
            key_tempb <= key_tempa;
        end
    end
    
    assign	nedge = (!key_tempa) & key_tempb;	//下降沿检测
    assign	pedge = key_tempa & (!key_tempb);	//上升沿检测  

计数部分

使用50M时钟,计数到20ms需要计数到20'd999_999,当检测到使能信号(en_cnt)开始计数,记满拉高cnt_full信号。

//计数器
    always @(posedge Clk or negedge Rst_n) begin
        if(!Rst_n)
            cnt <= 20'd0;
        else if(en_cnt) 			//接收到使能信号时,开始计数
            cnt <= cnt + 1'b1;
        else 						//其余时间,计数器为0
            cnt <= 20'd0;
    end
    
    always @(posedge Clk or negedge Rst_n) begin
        if(!Rst_n)
            cnt_full	<= 1'b0;				
        else if(cnt == 20'd999_999)	    //当记满时,cnt_full拉高
            cnt_full <= 1'b1;
        else 							//其余时间,cnt_full保持低电平
            cnt_full <= 1'b0;
    end  

testbench仿真文件编写见下一篇。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号