一、简介
我们在进行按键的时候往往会发生抖动的现象。
通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。这样的抖动会对我们的按键操作产生一些干扰,比如:有时候按下了一次按键,但是会发生很多次的功能的变化,这就是因为抖动的存在。
在机械按键的触点闭合和断开时,都会产生抖动,为了保证系统能正确识别按键的开关,就必须对按键的抖动进行处理。
按键的抖动对于人类来说是感觉不到的,但对单片机来说,则是完全可以感应到的,而且还是一个很“漫长”的过程,因为单片机处理的速度在“微秒”级,而按键抖动的时间至少在“毫秒”级。
单片机如果在触点抖动期间检测按键的通断状态,则可能导致判断出错,即按键一次按下或释放被错误地认为是多次操作,从而引起误处理。因此,为了确保单片机对一次按键动作只作—次响应,就必须考虑如何消除按键抖动的影响。
二、消除按键抖动的方法
按键稳定闭合时间长短是由操作人员决定的,通常都会在 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
实验运行
管脚配置
运行结果