1、实验目的
实验按键与拨码开关对LED灯的控制,掌握按键消抖。
2、实验要求
拨码开关控制LED灯的亮灭,按键控制流水灯的流水模式。
3、实验原理
逻辑派Z1开发板配置了8个拨码开关,电路设计上,当拨码开关拨上时,IO为低电平,未拨上时IO默认高电平。
逻辑派Z1开发板配置了4个用户按键,IO默认高电平,按键按下后IO为低电平。
由于机械式弹片按键在按下或松开时会有机械抖动,导致在按下或松开时按键的状态不稳定,在按键信号状态快速的变化时,使用按键作为输入信号,如果采集了按键抖动时的状态,会导致工程运行出现不可控的变化,故而我们需要将这段时间的抖动信号给滤除掉,此过程叫做按键消抖。
当按键按下时前后抖动时间约为5~10ms,前后抖动共在20ms。
当检测到按键状态变化时,对按键信号进行20ms的锁存,就可以避开前抖动与后抖动这段区间。
设计一个信号当按键输入信号状态变化时进行拉高操作,此时计数器开始计时,当计数满20ms时,在将此信号拉低,在此信号拉高时,对按键信号进行锁存,从而避免按键抖动。
4、实验源码设计
使用计数器计时0.5s,以0.5s为间隔使LED灯交替闪烁,并且计数按键按下的次数切换LED灯的流水模式。顶层代码如下所示:
verilog
`timescale 1ns / 1ps
`define UD #1
module key_sw_led(
input clk,
input rstn,
input [7:0] sw ,
input [2:0] key ,
output [7:0] led
);
//------------------------------------------------------------------------------
wire [7:0] sw_bed /* synthesis PAP_MARK_DEBUG="true" */;
wire [2:0] key_bed /* synthesis PAP_MARK_DEBUG="true" */;
reg [2:0] key_reg /* synthesis PAP_MARK_DEBUG="true" */;
reg [2:0] key_fall /* synthesis PAP_MARK_DEBUG="true" */;
reg [1:0] cnt /* synthesis PAP_MARK_DEBUG="true" */;
btn_deb#(
.BTN_WIDTH (4'd8),
.BTN_DELAY (20'd800_000)
) btn_deb (
.clk (clk ) ,
.btn_in (sw ) ,
.btn_deb (sw_bed )
);
btn_deb#(
.BTN_WIDTH (4'd3),
.BTN_DELAY (20'd800_000)
) btn_deb_1 (
.clk (clk ) ,
.btn_in (key ) ,
.btn_deb (key_bed )
);
always @(posedge clk) begin
key_reg <= key_bed ;
key_fall <= ((~key_bed) & (key_reg));
end
always @(posedge clk) begin
cnt <= 2'd0 ;
if (|key_fall)
cnt <= cnt + 2'b1 ;
else
cnt <= cnt ;
end
//=====================================================================
// reg and wire
reg [25:0] led_light_cnt /* synthesis PAP_MARK_DEBUG="true" */;
reg [7:0] led_status /* synthesis PAP_MARK_DEBUG="true" */;
reg tran_flag /* synthesis PAP_MARK_DEBUG="true" */;
// time counter
always @(posedge clk) begin
if (!rstn)
led_light_cnt <= `UD 26'd0;
else if (led_light_cnt == 26'd24_999_999)
led_light_cnt <= `UD 26'd0;
else
led_light_cnt <= `UD led_light_cnt + 26'd1;
end
always @(posedge clk) begin
tran_flag <= 1'b0;
if (led_light_cnt == 25'd24_999_999)
tran_flag <= 1'b1;
else
tran_flag <= 1'b0;
end
// led status change
always @(posedge clk) begin
if (!rstn)
led_status <= `UD 8'b0000_0001;
else if (tran_flag) begin
case (cnt)
0 : led_status <= `UD {led_status[6:0], led_status[7]};
1 : led_status <= `UD {led_status[0], led_status[7:1]};
2 : led_status <= `UD {led_status[5:0], led_status[7:6]};
3 : led_status <= `UD {led_status[1:0], led_status[7:2]};
default: led_status <= `UD 8'b0000_0001;
endcase
end
end
assign led = (led_status & (~sw_bed));
endmodule
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
88
89
90
91
92
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
88
89
90
91
92
按键消抖模块代码解释:当检测到按键输入信号有状态变化时,对按键信号进行锁存20ms,以此避免按键按下的抖动状态。
verilog
`timescale 1ns / 1ps
`define UD #1
module btn_deb#(
parameter BTN_WIDTH = 4'd8,
parameter BTN_DELAY = 20'hF423F
)
(
input clk, //
input [BTN_WIDTH-1:0] btn_in,
output reg [BTN_WIDTH-1:0] btn_deb
);
reg [19:0] cnt[BTN_WIDTH-1:0];
reg [BTN_WIDTH-1:0] flag;
reg [BTN_WIDTH-1:0] btn_in_reg;
always @(posedge clk)
begin
btn_in_reg <= `UD btn_in;
end
genvar i;
generate
begin
for(i=0; i<BTN_WIDTH; i=i+1)
begin
always @(posedge clk)
begin
if (btn_in_reg[i] ^ btn_in[i]) // The interval at which the edge of a key begins to jitter is marked
flag[i] <= `UD 1'b1;
else if (cnt[i] == BTN_DELAY) // Last for 20ms and then return to zero
flag[i] <= `UD 1'b0;
else
flag[i] <= `UD flag[i];
end
always @(posedge clk)
begin
if (cnt[i] == BTN_DELAY) // Last for 20ms and then return to zero
cnt[i] <= `UD 20'd0;
else if (flag[i]) // Count when the jitter interval is valid
cnt[i] <= `UD cnt[i] + 1'b1;
else // The non-jitter interval remains 0
cnt[i] <= `UD 20'd0;
end
always @(posedge clk)
begin
if (flag[i]) // Jitter interval, anti-jitter output maintained
btn_deb[i] <= `UD btn_deb[i];
else // In the non-jitter interval, the key state is transferred to the anti-jitter output
btn_deb[i] <= `UD btn_in[i];
end
end
end
endgenerate
endmodule
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
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
5、实验现象
打开烧录程序界面,设备连接好后连接JTAG,选择要下载的程序的sbit,如下所示:
拨码开关拨上时对应LED灯亮,按键KEY0 为复位按键,按下后返回初始状s态LED0亮,KEY1、KEY2、KEY3按键控制LED灯流水模式,LED灯流水状态共4中:逐个向右流水、逐个向左流水,每次跳一个LED灯向右流水,每次跳一个LED灯向左流水。