1,按键物理结构以及电路设计
普通按键的硬件示意图如下图所示。
上图是两脚贴片按键,原理图如下图所示。由原理图可以看出,按键未按下时IO口为高电平,按键按下时则变为低电平, 因此系统即可通过检测IO的电平来判断按键的状态。
按键结构示意图中可以看到按键存在一个反作用弹簧,因此当按下或者松开时均会产生 额外的物理抖动,物理抖动便会产生电平的抖动。在按键从按下再到松开的过程中,其电平 变化如下图所示,上为理想波形输出,下为实际波形输出。
上图中,产生的抖动次数以及间隔时间均是不可预期的,这就需要通过滤波来消除抖动可能对外部其他设备造成的影响。一般情况下抖动的总时间会持续 20ms 以内。
下图是按键工作状态:
2,如何滤除按键抖动,正确判断按键按下还是释放?
思路一
这种方法只能适用于一般情况,在工作状态比较差的情况下结果就有可能不可靠。
思路2
对按键消抖状态分析:
3,绘制状态转移图
对以上分析绘制状态转移图:
IDIE : 空闲状态
P_FILTER :按键是否按下检测状态
WAIT_R : 按键按下等待释放状态
R_FILTER:按键是否释放检测状态
4,编写代码
系统框图如下:
输入 : Clk 时钟信号 Reset_n 复位信号 key 按键输入信号
输出 : Key_P_Flag 按键按下信号 Key_R_Flag 按键释放信号
注意:由于按键信号是由外部输入得到,属于异步信号,如果直接使用会引起亚稳态,所以需要将其转为同步信号。
代码如下:
module key_filter(
Clk,
Reset_n,
Key,
Key_P_Flag,
Key_R_Flag
);
input Clk;
input Reset_n;
input Key;
output reg Key_P_Flag;
output reg Key_R_Flag;
reg sync_temp0_key;
reg sync_temp1_key;
wire nedge_key;
wire pedge_key;
reg r_key;
reg [1:0]state;
reg [19:0]cnt;
parameter IDLE = 0,P_FILTER = 1,WAIT_R = 2, R_FILTER = 3;
parameter MCNT = 20000000/20-1;
//异步信号转同步信号
always@(posedge Clk)
sync_temp0_key <= Key;
always@(posedge Clk)
sync_temp1_key <= sync_temp0_key;
always@(posedge Clk)
r_key <= sync_temp1_key;
//上升沿 下降沿
assign nedge_key = ((r_key == 1)&&(sync_temp1_key ==0));
assign pedge_key = ((r_key == 0)&&(sync_temp1_key ==1));
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)begin
state <= 0;
Key_P_Flag <= 0;
Key_R_Flag <= 0;
cnt <= 0;
end
else begin
case(state)
IDLE :begin
Key_R_Flag <= 0;
if(nedge_key)
state <= P_FILTER;
else
state <= IDLE;
end
P_FILTER : begin
if((pedge_key)&&(cnt < MCNT))begin
state <= IDLE;
cnt <= 0;
end
else if(cnt >= MCNT)begin
Key_P_Flag <= 1;
cnt <= 0;
state <= WAIT_R;
end
else begin
state <= P_FILTER;
cnt <= cnt + 1'b1;
end
end
WAIT_R : begin
Key_P_Flag <= 0;
if(pedge_key)
state <= R_FILTER;
else
state <= WAIT_R;
end
R_FILTER : begin
if((nedge_key)&&(cnt < MCNT)) begin
state <= WAIT_R;
cnt <= 0;
end
else if(cnt >= MCNT) begin
Key_R_Flag <= 1;
cnt <= 0;
state <= IDLE;
end
else begin
cnt <= cnt + 1;
state <= R_FILTER;
end
end
endcase
end
endmodule
测试代码testbench如下:
`timescale 1ns / 1ps
module key_filter_tb();
reg Clk;
reg Reset_n;
reg Key;
wire Key_P_Flag;
wire Key_R_Flag;
key_filter key_filter0(
.Clk(Clk),
.Reset_n(Reset_n),
.Key(Key),
.Key_P_Flag(Key_P_Flag),
.Key_R_Flag(Key_R_Flag)
);
initial Clk = 1;
always #10 Clk = ~Clk;
initial begin
Reset_n = 0;
Key = 1;
#201
Reset_n <= 1;
#3000
Key = 0;
#20000;
Key = 1;
#30000;
Key = 0;
#20000;
Key = 1;
#30000;
Key = 0;
#30000000;
Key = 1;
#30000;
Key = 0;
#20000;
Key = 1;
#30000;
Key = 0;
#20000;
Key = 1;
#30000000;
$stop;
end
endmodule
仿真波形:
至此,按键消抖学习到此结束,之后在使用按键中就可以将此代码嵌入!