verilog实现按键消抖四个按钮并分别点亮四个led灯

文章介绍了按键抖动现象及其对单片机操作的影响,主要讨论了消除按键抖动的必要性以及两种方法,特别是软件消抖的原理。通过使用VerilogHDL展示了软件消抖的实现,包括一个按键消抖模块的代码示例,以及一个按键控制点灯逻辑的模块,最后是顶层控制模块的整合。

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

一、简介

我们在进行按键的时候往往会发生抖动的现象。

通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。这样的抖动会对我们的按键操作产生一些干扰,比如:有时候按下了一次按键,但是会发生很多次的功能的变化,这就是因为抖动的存在。

在机械按键的触点闭合和断开时,都会产生抖动,为了保证系统能正确识别按键的开关,就必须对按键的抖动进行处理。

按键的抖动对于人类来说是感觉不到的,但对单片机来说,则是完全可以感应到的,而且还是一个很“漫长”的过程,因为单片机处理的速度在“微秒”级,而按键抖动的时间至少在“毫秒”级。

单片机如果在触点抖动期间检测按键的通断状态,则可能导致判断出错,即按键一次按下或释放被错误地认为是多次操作,从而引起误处理。因此,为了确保单片机对一次按键动作只作—次响应,就必须考虑如何消除按键抖动的影响。

二、消除按键抖动的方法

按键稳定闭合时间长短是由操作人员决定的,通常都会在 100ms 以上,刻意快速按的话能达到 40-50ms 左右,很难再低了。抖动时间是由按键的机械特性决定的,一般都会在 10ms以内,为了确保程序对按键的一次闭合或者一次断开只响应一次,必须进行按键的消抖处理。当检测到按键状态变化时,不是立即去响应动作,而是先等待闭合或断开稳定后再进行处理。按键消抖可分为硬件消抖和软件消抖。

我们由于是使用Verilog HDL来实现的,因此就是属于软件消抖了,因此,我们也重点讲述软件消抖的原理。

三、软件消抖原理

当按键较多时,硬件方法将导致系统硬件电路设计复杂化,硬件消抖将无法胜任,这时常采用软件方法进行消抖。常用软件方法去抖,即检测出键闭合后执行一个延时程序,5ms~10ms的延时,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。当检测到按键释放后,也要给5ms~10ms的延时,待后沿抖动消失后才能转入该键的处理程序。

软件消抖的基本原理是:在检测到有按键按下时,不是立即认定此键已被按下,而是执行一个10ms左右(具体时间应视所使用的按键进行调整)的延时程序后,再确认该键电平是否仍然保持闭合状态电平,若仍然保持,则确认该键真正被按下。

一般来说,软件消抖的方法是不断检测按键值,直到按键值稳定。实现方法:假设未按键时输入1,按键后输入为0,抖动时不定。可以做以下检测:检测到按键输入为0之后,延时5ms~10ms,再次检测,如果按键还为0,那么就认为有按键输入。延时的5ms~10ms恰好避开了抖动期,从而消除了前沿抖动的影响同理,在检测到按键释放后,再延时5~10ms,消除后沿抖动,然后再对键值进行处理。不过一般情况下,我们通常不对按键释放的后沿进行处理,实践证明,这样也能满足一定的要求。

实验代码

key_debounce.v按键消抖模块

module key_debounce(
    input   wire clk ,
    input   wire rst_n,
    input   wire [3:0] key_in,
    output  wire [3:0] key_out
);

reg              start;//稳定信号开始
reg [3:0]        key_r0;//按键信号寄存器r0
reg [3:0]        key_r1;//按键信号寄存器r1
wire             nedge;//下降沿信号
reg  [3:0]       key_r;//开启流水

parameter MAX_20ms = 20'd1_000_000;//20ms
reg [19:0] cnt_20ms;

//20ms倒计时
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cnt_20ms <= 20'd0;
    end

    else if (nedge)begin
      cnt_20ms <= MAX_20ms;
    end

    else if (start) begin
          cnt_20ms <= cnt_20ms -1'd1;
    end
    else begin
      cnt_20ms <= cnt_20ms;
    end
    
end



always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      key_r0 <= 4'b1111;
      key_r1 <= 4'b1111;
    end

    else begin
      key_r0 <= key_in;//打一拍,同步时钟域
      key_r1 <= key_r0;//打一拍,检测按键下降沿
    end
end

assign nedge = (~key_r0[0] && key_r1[0])||(~key_r0[1] && key_r1[1])||(~key_r0[2] && key_r1[2])||(~key_r0[3] && key_r1[3]);//检测下降沿

//信号约束
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)begin
      start<=1'b0;
    end
    else if (nedge)begin
      start <= 1'b1;
    end
    else if (cnt_20ms == 1'b1)begin
      start <= 1'b0;
    end
    else begin
      start <= start;
    end
end

//约束flag信号
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      key_r <= 4'b0000;
    end

    else if (cnt_20ms == 1'b1)begin
      key_r <= ~key_r0;
    end

    else begin
      key_r <= 4'd0000;
    end
end

assign  key_out = key_r;

endmodule

lock_password.v按键控制点灯逻辑模块

module lock_password(
    input wire clk,
    input wire rstn,
    input wire[3:0] key_in,

    output wire[3:0] led
);
reg[3:0] led_r;
reg[2:0] cstate;
reg[2:0] nstate;
reg[4:0] time4s;
parameter TIME_1s = 26'd50_000_000;
reg[25:0] cnt_1s;
parameter   IDLE = 3'd0,
            P1 = 3'd1,
            P2 = 3'd2,
            P3 = 3'd3,
            P4 = 3'd4;
//第一段
always @(posedge clk or negedge rstn) begin
    if(!rstn)begin
        cstate<=IDLE;
    end
    else begin
        cstate<=nstate;
    end
end
//第二段
always @(*) begin
    case (cstate)
            IDLE:begin
                if(key_in[0])begin
                    nstate=P1;
                end
                else if(key_in[1]||key_in[2]||key_in[3]) begin
                    nstate=IDLE;
                end
                else begin
                    nstate=cstate;
                end
            end
            P1:begin
                if(key_in[1])begin
                    nstate=P2;
                end
                else if(key_in[0]||key_in[2]||key_in[3]) begin
                    nstate=IDLE;
                end
                else begin
                    nstate=cstate;
                end
            end
            P2:begin
                if(key_in[2])begin
                    nstate=P3;
                end
                else if(key_in[0]||key_in[1]||key_in[3]) begin
                    nstate=IDLE;
                end
                else begin
                    nstate=cstate;
                end
            end
            P3:begin
                if(key_in[3])begin
                    nstate=P4;
                end
                else if(key_in[1]||key_in[0]||key_in[2]) begin
                    nstate=IDLE;
                end
                else begin
                    nstate=cstate;
                end
            end
            P4:begin
                if(time4s[4])begin
                    nstate=IDLE;
                end
                else begin
                    nstate=cstate;
                end
            end
            default ;      
        endcase
end
always @(posedge clk or negedge rstn) begin
    if(!rstn)begin
        led_r<=4'b0001;
    end
    else begin
        case (cstate)
            IDLE:led_r<=4'b0001;
            P1:led_r<=4'b0011;
            P2:led_r<=4'b0111;
            P3:led_r<=4'b1111;
            P4:begin
                if(cnt_1s == TIME_1s - 1)begin
                    led_r<=~led_r;
                end
                else begin
                    led_r<=led_r;
                end
            end
            default:led_r<=4'b0001;
        endcase
    end
end
always @(posedge clk or negedge rstn) begin
    if(!rstn)begin
        cnt_1s<=0;
    end
    else if(cnt_1s==TIME_1s-1)begin
       cnt_1s<=0;
    end
    else if(cstate == P4) begin
        cnt_1s<=cnt_1s+1;
    end
    else begin
        cnt_1s<=cnt_1s;
    end
end
always @(posedge clk or negedge rstn) begin
    if (!rstn) begin
        time4s<=5'b00001;
    end
    else if(cnt_1s == TIME_1s -1) begin
       time4s<={time4s[3:0],time4s[4]};
    end
    else if(cstate == IDLE) begin
        time4s<=5'b00001;
    end
    else begin
        time4s<=time4s;
    end
end
assign led=led_r;
endmodule

lock.v顶层控制模块

module lock_top(
    input wire clk,
    input wire rstn,
    input wire[3:0]key,

    output wire[3:0]led
);
wire[3:0] key_out;
key_debounce inst1(
    .clk(clk),
    .rst_n(rstn),
    .key_in(key),

    .key_out(key_out)
);
lock_password inst2(
    .clk(clk),
    .rstn(rstn),
    .key_in(key_out),

    .led(led)
);
endmodule

实验运行

管脚配置
在这里插入图片描述
运行结果
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值