图1 KEY原理图
图1 KEY原理图
图2 LED原理图
图2 LED原理图
key_led.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
module key_led(
input CLOCK_50,
input Q_KEY,
input [4:1] KEY,
output reg [4:1] LED
);
//++++++++++++++++++++++++++++++++++++++
// 获取键值 开始
//++++++++++++++++++++++++++++++++++++++
wire [4:1] key_val; // 键值
key_debounce u0(
.i_clk (CLOCK_50),
.i_rst_n (Q_KEY),
.i_key (KEY),
.o_key_val (key_val) // 按下为0,松开为1
);
//--------------------------------------
// 获取键值 结束
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
// 按下键后开关LED 开始
//++++++++++++++++++++++++++++++++++++++
always @ (posedge CLOCK_50, negedge Q_KEY)
if (!Q_KEY)
LED <= 4'hF; // 0灭1亮
else
case (1'b0)
key_val[1] : LED[1] <= ~LED[1];
key_val[2] : LED[2] <= ~LED[2];
key_val[3] : LED[3] <= ~LED[3];
key_val[4] : LED[4] <= ~LED[4];
default : LED <= LED ; // 缺省亮灭情况不变
endcase
//--------------------------------------
// 按下键后开关LED 结束
//--------------------------------------
endmodule
key_debounce.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
module key_debounce(
input i_clk,
input i_rst_n,
input [4:1] i_key, // 按下为0,松开为1
output reg [4:1] o_key_val // 键值
);
//++++++++++++++++++++++++++++++++++++++
reg [4:1] key_samp1, key_samp1_locked;
// 将i_key采集至key_samp1
always @ (posedge i_clk, negedge i_rst_n)
if(!i_rst_n)
key_samp1 <= 4'hF;
else
key_samp1 <= i_key;
// 将key_samp1锁存至key_samp1_locked
always @ (posedge i_clk, negedge i_rst_n)
if(!i_rst_n)
key_samp1_locked <= 4'hF;
else
key_samp1_locked <= key_samp1;
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
wire [4:1] key_changed1;
// 当key_samp1由1变为0时
// key_changed1由0变为1,只维持一个时钟周期
assign key_changed1 = key_samp1_locked & (~key_samp1);
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
reg [19:0] cnt;
// 一旦有按键按下,cnt立即被清零
always @ (posedge i_clk, negedge i_rst_n)
if(!i_rst_n)
cnt <= 20'h0;
else if(key_changed1)
cnt <= 20'h0;
else
cnt <= cnt + 1'b1;
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
reg [4:1] key_samp2, key_samp2_locked;
// 只有当按键不变化(不抖动),且维持20ms以上时
// 才将i_key采集至key_samp2
always @ (posedge i_clk, negedge i_rst_n)
if(!i_rst_n)
key_samp2 <= 4'hF;
else if(cnt == 20'hF_FFFF) // 0xFFFFF/50M = 20.9715ms
key_samp2 <= i_key;
// 将key_samp2锁存至key_samp2_locked
always @ (posedge i_clk, negedge i_rst_n)
if(!i_rst_n)
key_samp2_locked <= 4'hF;
else
key_samp2_locked <= key_samp2;
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
wire [4:1] key_changed2;
// 当key_samp2由1变为0时
// key_changed2由0变为1,只维持一个时钟周期
assign key_changed2 = key_samp2_locked & (~key_samp2);
//--------------------------------------
//++++++++++++++++++++++++++++++++++++++
// 每次按键稳定后,输出键值
// 按下为0,松开为1
always @ (posedge i_clk, negedge i_rst_n)
if(!i_rst_n)
o_key_val <= 4'hF;
else
o_key_val <= ~key_changed2;
//--------------------------------------
endmodule
HDL思路
3cdd09ff-062d-4e68-85e7-697eab9c0e22
图3 按键抖动
如图3所示,按键在按下和释放的时候都会有一定的抖动。本代码仅消除了按下抖动,没有对释放抖动进行处理。
代码中使用了一个计数器来实现延时。只要这个计数器正常计数到20'hF_FFFF,我们就采样一个值key_samp2,并将其锁存进key_samp2_locked。一旦有下降沿来临,就输出按键改变值key_changed2。同时,该模块也在使用高频时钟i_clk不停地采集键值到key_samp1,并锁存进key_samp1_locked;一旦下降沿存在,输出按键变化值key_changed1,这样计数器就会被清零。假设没有key_changed1,我们将0xFFFFF/50M = 20.9715ms取样一次键值,并检测其下降沿,输出键值。但是由于抖动的原因,采用高频时钟i_clk采样的key_samp1就有可能导致key_changed1为一,进而使得低频的20.9715ms采样延时执行。也就是说,只有按键处于稳定的状态时,key_samp2才被正常采样,才有可能检测到key_changed2。
参考资料
1. 特权.基于verilog按键消抖设计
http://group.ednchina.com/1375/21132.aspx
文章评论(0条评论)
登录后参与讨论