1、概述         按键的消抖,是指按键在闭合或松开的瞬间伴随着一连串的抖动,这样的抖动将直接影响设计系统的稳定性,降低响应灵敏度。因此,必须对抖动进行处理,即消除抖动的影响。实际工程中,有很多消抖方案,如 RS 触发器消抖,电容充放电消抖,软件消抖。本章利用 FPGA 内部来设计消抖,即采取软件消抖。         按键的机械特性,决定着按键的抖动时间,一般抖动时间在 5ms~10ms。消抖,也意味着,每次在按键闭合或松开期间,跳过这段抖动时间,再检测按键的状态。只要通过简单的延时就可实现按键的消抖动。

2、硬件电路分析2.1 原理图         开发板底板中配套 2 个独立按键与 FPGA 相连,具体请参见底板原理图。本章使用了开发板按键和 led。各个按键独立,消抖过程相同,因此使用底板上的 SW1 按键模拟实际使用。按键每按一次,对应的 LED 灯反转一次。即检测按键是否有闭合和断开的过程,如果有,第一次则 LED 灯点亮,第二次,则 LED 灯熄灭。

2.2 IO约束

set_property PACKAGE_PIN D9 [get_ports {led_o[0]}]

set_property PACKAGE_PIN J11 [get_ports {led_o[1]}]

set_property PACKAGE_PIN B9 [get_ports {led_o[2]}]

set_property PACKAGE_PIN J10 [get_ports {led_o[3]}]

set_property IOSTANDARD LVCMOS15 [get_ports {led_o[*]}]

set_property PACKAGE_PIN C8 [get_ports clk_i]

set_property IOSTANDARD LVCMOS15 [get_ports clk_i]

set_property PACKAGE_PIN H13 [get_ports key_i]

set_property IOSTANDARD LVCMOS15 [get_ports key_i]

set_property PACKAGE_PIN V19 [get_ports rst_n_i]

set_property IOSTANDARD LVCMOS15 [get_ports rst_n_i]

2.3 key 模块的设计         由于按键滤波是比较通用的一个程序,因此我们可以把一个通用的程序设置为一个模块,方便后面重复使用。

module key #

(

parameter CLK_FREQ = 100000000

)

(

input clk_i,

input key_i,

output key_cap

);

//10ms

parameter CNT_10MS = (CLK_FREQ/100 - 1'b1);

parameter KEY_S0 = 2'd0;

parameter KEY_S1 = 2'd1;

parameter KEY_S2 = 2'd2;

parameter KEY_S3 = 2'd3;

reg [24:0] cnt10ms = 25'd0;

(*mark_debug = "true"*) reg [1:0] key_s = 2'b0;

(*mark_debug = "true"*) reg [1:0] key_s_r = 2'b0;

(*mark_debug = "true"*) wire en_10ms ;

assign en_10ms = (cnt10ms == CNT_10MS);

assign key_cap = (key_s==KEY_S2)&&(key_s_r==KEY_S1);

always @(posedge clk_i)begin

if(cnt10ms < CNT_10MS)

cnt10ms <= cnt10ms + 1'b1;

else

cnt10ms <= 25'd0;

end

always @(posedge clk_i)begin

key_s_r <= key_s;

end

always @(posedge clk_i)begin

if(en_10ms)begin

case(key_s)

KEY_S0:begin

if(!key_i)

key_s <= KEY_S1;

end

KEY_S1:begin

if(!key_i)

key_s <= KEY_S2;

else

key_s <= KEY_S0;

end

KEY_S2:begin

if(key_i)

key_s <= KEY_S3;

end

KEY_S3:begin

if(key_i)

key_s <= KEY_S0;

else

key_s <= KEY_S2;

end

endcase

end

end

endmodule

        以上代码中,首先把系统时钟做分频,产生 10ms 的分频时钟使能信号。每10ms都会判断一次是否有按键按下。在设计的状态机中,分 4 个状态:         KEY_S0:判断按键是否按下,如果是,转移到状态 KEY_S1;         KEY_S1:10ms 后再次判断按键是否按下,如果是,转移状态到 KEY_S2,否则继续回到KEY_S0;         KEY_S2:判断按键是否抬起,如果是,转移状态到 KEY_S3         KEY_S3:10ms 后再次判断按键是否抬起,如果是,转移状态到 KEY_S0,否则继续回到KEY_S2;         当状态从 KEY_S1 转到 KEY_S2 代表依次按钮按下 key_cap 输出一次高电平。

2.4 调用 key 模块         以下代码中调用了 key 模块,并且每次 key_cap 有效,都会翻转一次 LED 的输出。

`timescale 1ns / 1ps

module Key_Jitter(

input clk_i,

input rst_n_i,

input key_i,

output [3:0] led_o

);

reg [3:0] led_o;

wire key_cap;

always @(posedge clk_i)begin

if(!rst_n_i)begin

led_o <= 4'b0000;

end

else if(key_cap)begin

led_o <= ~led_o;

end

end

key#

(

.CLK_FREQ(100000000)

)

key0

(

.clk_i(clk_i),

.key_i(key_i),

.key_cap(key_cap)

);

ila_0 ila_debug (

.clk(clk_i),

.probe0({key_cap,led_o})

);

endmodule

2.5 综合布线前仿真时序 1)新建仿真文件,仿真文件源码如下所示。

module Key_Jitter_tb;

// Inputs

reg clk_i;

reg rst_n_i;

reg key_i;

wire [2:0] led_o;

// Instantiate the Unit Under Test (UUT)

Key_Jitter uut (

.clk_i(clk_i),

.rst_n_i(rst_n_i),

.key_i(key_i),

.led_o(led_o)

);

initial

begin

// Initialize Inputs

clk_i = 0;

end

always #5 clk_i=~clk_i;

initial

begin

// Initialize Inputs

rst_n_i = 0;

#100;

rst_n_i=1;

key_i = 1;

#10000;

forever

begin

key_i = 0;

// Wait 100 ns for global reset to finish

#100;

key_i=1; #1000;

key_i=0; #1000;

key_i=1; #2000;

key_i=0; #5000;

#50000000;

key_i=1;

key_i=0; #1000;

key_i=1; #2000;

key_i=0; #1000;

key_i=1; #2000;

#50000000;

key_i=0;

end

end

endmodule

2)进入仿真界面:SIMULATION->单击 Run Simulation ->单击 Run Behavioral Simulation。

Setp3:设置断点

之后再点击下图箭头所指

之后可以看到增加的内部信号,可以以这种方法去观察内部信号

右击框选的需要增加观察的信号选择添加到波形窗口,并且取消断点

3)复位仿真波形

4)重新仿真时间为 1000ms

5)观察仿真波形

6)放大波形观察毛刺

3、板级验证结果分析         将程序下载。按键 SW1 每按一次,LED 灯很好地熄灭和点亮。LED 灯响应无差错。为清晰的表示消抖的效果,可将延时参数设置很小,可以发现,按键有时候明明已经按下去了,LED 却无响应。

精彩文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。