基于FPGA的按键消抖
应用场景及实现原理
在电路中的按键由于按键的材料原因,在按键按下或弹开的时候会有抖动,而这种抖动在FPGA中不被允许存在。因为抖动就会产生按键的误判,这在电子电路中是非常常见的现象,抖动的去除可以用硬件电路来实现,想知道方法的可以参考阎石老师的数字电路技术基础这本书,在触发器的章节有讲。本次实验主要是利用软件代码实现按键消抖,实现的主要原理是,按键抖动时两个抖动的时间间隔不超过20ms。
状态转移图设计
即使再简单的代码,我们也要按照标准的流程来实现。因为按键消抖模块中含有状态机,所以我们画出状态机的状态转移图,如下:
由上面的状态转移图,我们可以很容易的写出代码,这里不多加解释。我的传统,直接上代码。
按键消抖模块代码
按键消抖模块的代码如下:
`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : key.v
// Create Time : 2020-01-05 13:49:36
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module key(
input sclk ,
input rst_n ,
input key ,
output reg key_o
);
//========================================================================================\
//**************Define Parameter and Internal Signals**********************************
//========================================================================================/
parameter IDLE = 4'b0001 ;
parameter S1 = 4'b0010 ;
parameter S2 = 4'b0100 ;
parameter S3 = 4'b1000 ;
reg [ 3:0] state ;
reg [ 9:0] cnt ;
reg key_r1 ;
reg key_r2 ;
reg key_r3 ;
reg nege_flag ;
reg pose_flag ;
//========================================================================================\
//************** Main Code **********************************
//========================================================================================/
always @(posedge sclk)
key_r1 <= key;
always @(posedge sclk)
key_r2 <= key_r1;
always @(posedge sclk)
key_r3 <= key_r2;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
nege_flag <= 1'b0;
else if(key_r3 == 1'b1 && key_r2 == 1'b0)
nege_flag <= 1'b1;
else
nege_flag <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
pose_flag <= 1'b0;
else if(key_r3 == 1'b0 && key_r2 == 1'b1)
pose_flag <= 1'b1;
else
pose_flag <= 1'b0;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
state <= IDLE;
else case(state)
IDLE : if(nege_flag == 1'b1)
state <= S1;
else
state <= IDLE;
S1 : if(cnt == 10'd999)
state <= S2;
else if(pose_flag == 1'b1)
state <= IDLE;
else
state <= S1;
S2 : if(pose_flag == 1'b1)
state <= S3;
else
state <= S2;
S3 : if(cnt == 10'd999)
state <= IDLE;
else if(nege_flag == 1'b1)
state <= S2;
else
state <= S3;
default : state <= IDLE;
endcase
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
cnt <= 10'd0;
else if(state != S1 && state != S3)
cnt <= 10'd0;
else
cnt <= cnt + 1'b1;
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
key_o <= 1'b0;
else if(state == S1 && cnt == 10'd999)
key_o <= 1'b1;
else
key_o <= 1'b0;
endmodule
相信经常看我文章的同学已经对上面的代码风格很熟悉,观察状态转移图与逻辑代码很容易可以搞懂代码实现。为了证明程序的正确性,我进行了程序仿真。
按键消抖模块测试代码
这里的测试代码同样使用了task语句,而且使用了task语句中嵌套task语句,经过这节课的练习,相信大家对测试模块的写法已经了如指掌。
`timescale 1ns / 1ps
`define CLOCK 20
// *********************************************************************************
// Project Name : OSXXXX
// Author : zhangningning
// Email : [email protected]
// Website :
// Module Name : key_tb.v
// Create Time : 2020-01-05 14:09:55
// Editor : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// XXXX zhangningning 1.0 Original
//
// *********************************************************************************
module key_tb();
reg sclk ;
reg rst_n ;
reg key ;
wire key_o ;
initial begin
sclk <= 1'b0;
rst_n = 1'b0;
key = 1'b1;
#(100*`CLOCK)
rst_n = 1'b1;
#(100*`CLOCK)
key_pulse;
#(100*`CLOCK)
key_pulse;
#(100*`CLOCK)
key_pulse;
#(100*`CLOCK)
key_pulse;
#(100*`CLOCK)
key_pulse;
#(100*`CLOCK)
key_pulse;
#(100*`CLOCK)
key_pulse;
#(100*`CLOCK)
key_pulse;
end
always #(`CLOCK/2) sclk <= ~sclk;
task key_pulse;
begin
key <= 1'b1;
shake(1000);
key <= 1'b0;
#(5000*`CLOCK)
shake(1000);
key <= 1'b1;
#(5000*`CLOCK);
end
endtask
task shake ;
input [10:0] times ;
integer i ;
begin
for(i = 0; i<times; i = i+1)begin
key <= {$random} % 2;
#(`CLOCK);
end
end
endtask
key key_inst(
.sclk (sclk ),
.rst_n (rst_n ),
.key (key ),
.key_o (key_o )
);
endmodule
这里给出modelsim的仿真图,如下:
从仿真图中可以看出我们实验的准确性。
结束语
我博客中的代码是可以直接跑的程序,没有进行任何精简。创作不易,认为文章有帮助的同学们可以收藏点赞支持。对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: