按键的结构图如图所示
理想状态下,按键的波形图如图所示,按下即变为低电平,释放即变为高电平
然而实际中,由于弹簧的存在,再按下按键和释放按键时会有一个抖动状态,如图所示:
如果直接判断下降沿的产生就为按下,那么系统就会判定为按下了多次,这样就会出大问题!例如一个计数器,当按键按下一次就计数加1,如果下降沿判断,系统就会判定为按下了多次,从而计数器多次加1!但是明明操作者只按下了一次按键!
那么如何解决这个问题呢?经过研究发现,按键的抖动时间不会超过20ms!那么我们就对低电平/高电平检测持续时间,当持续时间大于20ms时,则判定经过了抖动状态,已经稳定了!
引入状态机:
按键一共存在四种状态:
状态1:空闲状态:此时等待按下操作的来临,当按下操做的来临,当按下操作来临后,检测到下降沿即即开始计时,转移到状态2,否则保持状态1不变。
状态2:按下抖动状态:20ms的计时状态,当计数小于20ms时检测到了上升沿,则返回状态1,当20ms内没有检测到上升沿时,则转到状态3。
状态3:等待释放状态:等待释放操作来临,当释放操作来临后,检测到上升沿即开始计数,转移到状态4,否则保持状态3不变。
状态4:释放抖动状态:20ms的计时状态,当计数小于20ms时检测到了下降沿,则返回状态3,当20ms内没有检测到下降沿时,则转到状态1。
状态转移图如图所示:
程序要求:需要一个按下完成标志(Key_P_flag)和释放完成标志(Key_R_flag)
module key(
input clk,
input key,
input rst,
output reg key_p_flag, //按下标志
output reg key_r_flag //释放标志
);
reg [1:0] state;
//定义状态机
parameter state_1 = 2'd0;
parameter state_2 = 2'd1;
parameter state_3 = 2'd2;
parameter state_4 = 2'd3;
//定义20ms计数器
parameter MCNT_20ms = 20'hF4240;
reg [19:0] counter_20ms;
//检测按键下降沿以及上升沿
reg [1:0] Detect;
always@(posedge clk)
begin
Detect[0] <=key;
Detect[1] <= Detect[0];
end
wire posedge_detect; //上升沿
wire negedge_detect; //下降沿
assign posedge_detect = Detect == 2'b01;
assign negedge_detect = Detect == 2'b10;
//状态机循环
always@(posedge clk or negedge rst)
if(!rst)
begin
state <= state_1;
counter_20ms <= 0;
key_p_flag<=0;
key_r_flag<=0;
end
else if (state == state_1)
begin
key_r_flag <=0;
if(negedge_detect)
begin
state<=state_2;
counter_20ms<=counter_20ms+1;
end
else state <= state;
end
else if(state == state_2)
begin
if(posedge_detect==1 && counter_20ms < (MCNT_20ms-1))
begin
state<=state_1;
counter_20ms <= 0;
end
else if(counter_20ms >= (MCNT_20ms-1))
begin
state<=state_3;
counter_20ms <= 0;
key_p_flag <=1;
end
else
begin
state <= state;
counter_20ms<=counter_20ms+1;
end
end
else if ( state == state_3 )
begin
key_p_flag <=0;
if(posedge_detect)
begin
state<=state_4;
counter_20ms<=counter_20ms+1;
end
else state<=state;
end
else if(state == state_4)
begin
if(negedge_detect && counter_20ms < (MCNT_20ms-1))
begin
state<=state_3;
counter_20ms <= 0;
end
else if(counter_20ms > (MCNT_20ms-1))
begin
state<=state_1;
counter_20ms <= 0;
key_r_flag <=1;
end
else
begin
state <= state;
counter_20ms<=counter_20ms+1;
end
end
endmodule