一、实验任务
本节实验任务是使用开发板上的 KEY0 按键来控制蜂鸣器发声。初始状态为蜂鸣器鸣叫,按下按键后蜂鸣器停止鸣叫,再次按下开关,蜂鸣器重新鸣叫。(重点是按键消抖)以下是蜂鸣器原理图。
二、程序设计
2.1 整体模块设计
本次实验需要 3 个输入的端口,分别为系统时钟、系统复位和按键输入,输出为蜂鸣器 beep 端口,模块框图如下图所示:
2.2 按键消抖模块设计
按键消抖模块我们的输入信号主要有系统时钟信号、系统复位信号与按键输入,输出为按键消抖后的值。模块框图如下图所示:
**绘制波形图:**我们消抖的过程就是滤除按键值保持时间小于 20ms 的值,那么我们需要做的就是在按键被按下或者被释放导致按键值产生变化时,从 20ms 开始倒计时,如果 20ms 的倒计时还没有完成按键值就再次产生变化,此时需要从头开始 20ms 倒计时,前一次导致按键值变化的操作视为无效操作,将该次变化视为按键抖动消除,否者保留。(50MHZ时钟,20ms需要50000000*0.02=1000000个时钟周期,)。
判断按键值变化,我们可以给按键值打两拍(一般外部输入信号我们都使用打拍处理消除亚稳态),然后比较按键两次的打拍值,第一次打拍值与第二次打拍值一致说明按键值没有变化,第一次打拍值与第二次打拍值不一致说明按键值产生了变化。
RTL代码key_debounce.v如下:
module key_debounce(
input sys_clk,
input sys_rst_n,
input key,
output reg key_filter
);
parameter CNT_MAX = 20'd100_0000;
reg [19:0] cnt;
reg key_d0;
reg key_d1;
//打两拍处理/
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n) begin
key_d0 <= 1'd1;
key_d1 <= 1'd1;
end
else begin
key_d0 <= key;
key_d1 <= key_d0;
end
end
//消抖处理
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt <= 20'd0;
else if(key_d0 != key_d1)
cnt <= 20'd0;
else if(cnt == CNT_MAX - 20'd1)
key_filter <= key_d0;
else if(cnt >= CNT_MAX - 20'd1)
cnt <= CNT_MAX - 20'd1;
else
cnt <= cnt + 20'd1;
end
endmodule
2.3按键控制蜂鸣器模块设计
按键控制蜂鸣器模块我们的输入信号主要有系统时钟信号、系统复位信号与消抖后的按键输入,输出
为蜂鸣器的值。模块框图如图所示:
绘制波形图: 本节实验任务是按下按键蜂鸣器响起,再次按下按键后蜂鸣器关闭。所以,我们可以通过打一拍实现。波形图如下:
RTL代码key_beep.v如下:
module key_beep(
input sys_clk,
input sys_rst_n,
input key_filter,
output reg beep
);
reg key_filter_d0;
wire neg_key_filter;
assign neg_key_filter = ~key_filter & key_filter_d0;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_filter_d0 = 1'b1;
else
key_filter_d0 = key_filter;
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
beep <= 1'b1;
else if(neg_key_filter)
beep <= ~beep;
else
beep <= beep;
end
endmodule
2.4 顶层模块
module top_key_beep(
input sys_clk,
input sys_rst_n,
input key,
output beep
);
parameter CNT_MAX = 20'd100_0000; //消抖时间 20ms
wire key_filter;
key_debounce #(
.CNT_MAX (CNT_MAX)
) u_key_debounce(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.key (key),
.key_filter (key_filter)
);
key_beep u_key_beep(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.key_filter (key_filter),
.beep (beep)
);
endmodule
2.5 仿真验证
仿真代码tb_flow_led.v如下:
`timescale 1ns/1ns
module tb_top_key_beep();
parameter CNT_MAX = 20'd10;
parameter CLK_PERIOD = 20;
reg sys_clk;
reg sys_rst_n;
reg key;
wire beep;
initial begin
sys_rst_n <= 1'b0;
sys_clk <= 1'b0;
key = 1'b1;
#200 sys_rst_n <= 1'b1;
#100 key <= 1'b0;
#20 key <= 1'b1;
#20 key <= 1'b0;
#600 key <= 1'b1;
#50 key <= 1'b0;
#800 key <= 1'b1;
end
always #(CLK_PERIOD/2) sys_clk = ~sys_clk;
top_key_beep #(
.CNT_MAX (CNT_MAX)
)
u_top_key_beep(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.key(key),
.beep(beep)
);
endmodule
仿真结果如下图: