在电子产品中我们会经常用到按键,比如电脑的键盘,手机的按键等等,按键就是人机交互的一种工具。
1. 按键的硬件电路
2. 按键抖动
通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,电压信号小型如下图。由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,如下图。抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。这是一个很重要的时间参数,在很多场合都要用到。
我们需要对都抖动的按键信号处理后才可以再输入FPGA中,经过滤波后的信号应该如图下所示:
3. 按键消抖的状态转换图
按键没有按下时处于空闲状态:IDEL;
按下抖动时处于滤除状态:FILTER0;
按下稳定状态:DOWN;
释放抖动处于滤除状态:FILTER1
nedge:为下降沿信号、pedge为上升沿信号
en_cnt:计数器的使能信号
cnt_full_flag计时器记到20ms的标记信号。
4.代码实现
module key(
input wire sclk,
input wire rst_n,
input wire key,
output reg key_flag,
output reg key_value
);
parameter IDEL = 4'b0001;
parameter FILTER0 = 4'b0010;
parameter DOWN = 4'b0100;
parameter FILTER1 = 4'b1000;
reg key_in_sa,key_in_sb;
reg key_tmpa,key_tmpb;
wire pedge,nedge;//跳变沿信号
reg en_cnt;//使能计数器
reg cnt_full_flag;//计数满标志
reg [3:0] state;//状态
reg [19:0] cnt;//计数器
always @(posedge sclk or negedge rst_n)//将输入的异步信号进行同步处理
if(~rst_n)begin
key_in_sa<=1'b0;
key_in_sb<=1'b0;
end
else begin
key_in_sa<=key;
key_in_sb<=key_in_sa;
end
always @(posedge sclk or negedge rst_n)//使用D触发器存储两个相邻时钟上升沿时外部输入信号的电平
if(~rst_n)
begin
key_tmpa<=1'b0;
key_tmpb<=1'b0;
end
else begin
key_tmpa<=key_in_sb;
key_tmpb<=key_tmpa;
end
assign nedge=(~key_tmpa) & key_tmpb;
assign pedge=key_tmpa & (~key_tmpb);//产生跳变沿信号
always @(posedge sclk or negedge rst_n)//状态迁移
if(~rst_n)
begin
state<=IDEL;
en_cnt<=1'b0;
end
else begin
case(state)
IDEL:if(nedge==1'b1) begin
state<=FILTER0;
en_cnt<=1'b1;
end
else begin
state<=IDEL;
en_cnt<=1'b0;
end
FILTER0: if(cnt_full_flag==1'b1)
begin
state<=DOWN;
en_cnt<=1'b0;
end
else if(pedge) begin
state<=IDEL;
en_cnt<=1'b0;
end
else begin
state<=FILTER0;
en_cnt<=1'b1;
end
DOWN: if(pedge==1'b1) begin
state<=FILTER1;
en_cnt<=1'b1;
end
else begin
state<=DOWN;
en_cnt<=1'b0;
end
FILTER1: if(cnt_full_flag==1'b1)
begin
state<=IDEL;
en_cnt<=1'b0;
end
else if(nedge)
begin
state<=DOWN;
en_cnt<=1'b0;
end
else
begin
state<=FILTER1;
en_cnt<=1'b01;
end
default: begin
state<=IDEL;
en_cnt<=1'b0;
end
endcase
end
always @(posedge sclk or negedge rst_n)//key_flag的输出状态
if(~rst_n)
key_flag=1'b0;
else
if(cnt_full_flag==1&& state==FILTER0)
key_flag<=1'b1;
else
if(cnt_full_flag==1&& state==FILTER1)
key_flag<=1'b1;
else
key_flag<=1'b0;
always @(posedge sclk or negedge rst_n)//key_value的输出状态
if(~rst_n)
key_value=1'b1;
else
if(cnt_full_flag==1&& state==FILTER0)
key_value <=1'b0;
else
if(state==DOWN)
key_value <=1'b0;
else
if(state==DOWN && pedge)
key_value <=1'b0;
else
if(state==FILTER1 && nedge)
key_value <=1'b0;
else
if(state==FILTER1 && cnt_full_flag==0)
key_value<=1'b0;
else
key_value=1'b1;
always @(posedge sclk or negedge rst_n)//计数器
if(~rst_n)
cnt<='d0;
else
if(en_cnt)
cnt<=cnt+1'b1;
else
cnt<='d0;
always @(posedge sclk or negedge rst_n)//计数20ms满标志
if(~rst_n)
cnt_full_flag<=1'b0;
else
if(cnt==20'd999_999)
cnt_full_flag<=1'b1;
else
cnt_full_flag<=1'b0;
endmodule
5testbench代码
`timescale 1ns/1ps
`define clk_period 20
module key_tb1;
reg sclk;
reg rst_n;
reg key;
wire key_value;
wire key_flag;
reg [15:0] myrand;
key key2(
.sclk (sclk),
.rst_n (rst_n),
.key (key),
.key_flag (key_flag),
.key_value (key_value)
);
initial sclk=1;
always #(`clk_period/2) sclk=~sclk;
initial
begin
rst_n=0;
key=1;
#(`clk_period*10);
rst_n=1;
#(`clk_period*10+1);
press_key;
#10000;
$stop;
end
task press_key;
begin
repeat(50) begin
myrand={$random}%65536;
#myrand;
key=~key;
end
key=0;
#50_000_000;
repeat(50) begin
myrand={$random}%65536;
#myrand;
key=~key;
end
key=1;
#50_000_000;
end
endtask
endmodule