问题与思路
使用按键控制蜂鸣器发声。 初始状态为蜂鸣器鸣叫,按下开关后蜂鸣器停止鸣叫,再次按下开关,蜂鸣器重新鸣叫
按键抖动:
按键消抖:
系统框图:
消抖方式
-
输入key的按键信号,输出一个value表示按键状态,以及一个flag表示状态是否稳定有效
-
设置一个计数器,表示按键在同一状态稳定的时间,达到这个时间阈值表示按键按下有效
- 这里的cnt设置为1_000_000个周期,每个周期20ns,也就是20_000_000ns=20ms=0.2s
-
把key存在一个寄存器key_reg里,当key_reg != 按键key 表示状态发生了变化按键按下了,但是不知是抖动还是稳定,计数器赋初值
-
否则key_reg=key表示按键没有变化处于稳定状态,计数器自减计算稳定时长,当时长达到阈值后即计数器为0后不再自减维持为0
-
如果以cnt为0作为判断条件,由于cnt在稳定后一直为0,那么key_flag和key_value会一直高电平和低电平,如果一直高电平蜂鸣器会认为一直处于按键变化状态,就会一直处于开关开关状态,所以我们只需要一个电平脉冲
-
所以在计数器为1的时候拉高,为0的时候拉低,只一个周期为高电平
key_debounce.v
注意这里是key_value = key
而不是 key_value = 1'b0
因为我们要的是按键按下持续0.02s后状态改变,而不是按键的任何状态即按下或松开持续0.02s就变
module key_debounce (
input sys_clk,
input sys_rst_n,
input key,
output reg key_value,
output reg key_flag
);
reg [19:0] cnt;
reg key_reg;
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
key_reg <= 1'b1;
cnt <= 1'b0;
end
else begin
key_reg <= key;
if(key_reg != key)
cnt <= 20'd1_000_000;
else begin
if(cnt <= 1'b0)
cnt <= 1'b0;
else
cnt <= cnt - 1'b1;
end
end
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
key_value <= 1'b1;
key_flag <= 1'b0;
end
else begin
if(cnt == 1)begin
key_value <= key;
key_flag <= 1'b1;
end
else begin
key_value <= key_value;
key_flag <= 1'b0;
end
end
end
endmodule
beep_control.v
module beep_control (
input sys_clk,
input sys_rst_n,
input key,
input key_value,
input key_flag,
output reg beep
);
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
beep <= 1'b1;
else if (key_flag && key_value == 1'b0)
beep <= ~beep;
else
beep <= beep;
end
endmodule
top_key_beep.v
顶层模块连接两个底层模块,连接两个模块之间的变量需要定义位宽,类型为wire型,相当于水管连接两个模块,位宽是水管的粗细
module top_key_beep(
input sys_clk,
input sys_rst_n,
input key,
output beep
);
wire key_value;
wire key_flag;
key_debounce u_key_debounce(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.key(key),
.key_value(key_value),
.key_flag(key_flag)
);
beep_control u_beep_control(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.key_value(key_value),
.key_flag(key_flag),
.beep(beep)
);
endmodule
tb_key_beep.v
需要模拟抖动
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2022/12/05 10:42:28
// Design Name:
// Module Name: tb_key_beep
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module tb_key_beep(
);
reg sys_clk;
reg sys_rst_n;
reg key;
wire beep;
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0;
key = 1'b1;
#60
sys_rst_n = 1'b1;
#40
key = 1'b0;
#100
key = 1'b1;
#40
key = 1'b0;
#120
key = 1'b1;
#40
key = 1'b0;
#140
key = 1'b1;
#40
key = 1'b0;
#240
key = 1'b1;
#40
key = 1'b0;
#280
key = 1'b1;
end
always #10 sys_clk = ~sys_clk;
top_key_beep u_top_key_beep(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.key(key),
.beep(beep)
);
endmodule
仿真
cnt的阈值改为10,便于观察
下载验证
注意的是zybo无复位按钮,这里将btn[1]作为sys_rst_n, btn[0]作为key,led[0]作为beep
将top函数的复位按钮前加上!取反,因为按钮是高电平有效的,所以key_control文件里的判断条件也要改为else if (key_flag && (key_value == 1'b1))
约束:
##Clock signal
set_property -dict { PACKAGE_PIN L16 IOSTANDARD LVCMOS33 } [get_ports { sys_clk }];
##Buttons
set_property -dict { PACKAGE_PIN R18 IOSTANDARD LVCMOS33 } [get_ports {key }]; #IO_L20N_T3_34 Sch=BTN0
set_property -dict { PACKAGE_PIN P16 IOSTANDARD LVCMOS33 } [get_ports { sys_rst_n }]; #IO_L24N_T3_34 Sch=BTN1
##LEDs
set_property -dict { PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports { beep }]; #IO_L23P_T3_35 Sch=LED0
标签:beep,蜂鸣器,FPGA,clk,sys,key,rst,reg
From: https://www.cnblogs.com/xiongyuqing/p/16952663.html