Directly reading button states to control devices like LEDs can lead to misinterpretation due to mechanical bouncing when buttons are pressed or released. This bouncing effect causes rapid state changes that may be incorrectly interpreted as multiple button presses. This section demonstrates how to implement debouncing algorithms using a button-controlled buzzer example.
Buzzer Fundamentals
Buzzer devices fall into two categories based on their construction: piezoelectric and electromagnetic types. Piezoelectric buzzers consist of oscillator circuits, piezoelectric plates, impedance matching components, resonance chambers, and housing. Electromagnetic buzzers contain oscillator units, electromagnetic coils, permanent magnets, vibrating diaphragms, and enclosures. Piezoelectric versions operate through piezoelectric effects, generating sound when alternating voltage is applied. Electromagnetic types use audio frequency currents through coils to create magnetic fields that cause periodic vibration between the coil and magnet.
Buzzers also differ in driving methods: active and passive. Active buzzers contain internal oscillators and produce sound immediately when powered. Passive buzzers require external oscillating signals to generate sound since they lack internal oscillator circuits.
Debouncing Mechanism
Mechanical spring switches exhibit physical bouncing when actuated due to their elastic properties. During pressing or releasing actions, the contacts don't immediately settle into stable positions but rather oscillate before stabilizing. Debouncing eliminates these unwanted oscillations.
Two approaches exist: hardware and software debouncing. Hardware solutions typically employ RS flip-flops or capacitors in circuit design, suitable for systems with few buttons. Software debouncing delays 5-20ms after detecting state changes before sampling and validating button states.
Software Implementation Strategy
Both press and release actions generate bouncing. In digital systems, this appears as rapidly fluctuating high and low levels. The bouncing signal shows low levels briefly before returning high due to mechanical spring behavior. Detecting each low level would register multiple button presses instead of single operations. Therefore, filtering removes signals lasting less than 20ms. Only when button values remain stable for 20ms or longer do we consider them valid. High levels indciate release, low levels indicate press.
- Filtering short-duration values: Start a 20ms countdown. If the button state changes before completion, restart the countdown from the beginning, treating previous changes as invalid. Complete 20ms without change confirms valid press or release.
- Detecting state changes: Capture button values in two consecutive cycles (first for metastability elimination, second for comparison). Matching values indicate no change, differing values indicate transitions.
Hardware Configuration
The experiment uses an active buzzer with the following schematic:
Button schematics reference the LED control implementation from previous sections.
Functional Requirements
Use KEY0 to control buzzer operation. Initially, the buzzer sounds continuously. Pressing the button stops the sound. Another press reactivates the buzzer.
System Architecture
The system consists of two modules: the button debouncing module and the buzzer control module.
Implementation Code
Debouncing Module
`timescale 1ns / 1ns
module button_filter #(
parameter COUNTER_BITS = 20,
parameter DEBOUNCE_TIME = 20'd100_0000,
parameter IDLE_STATE = 1'b1
)(
input clk,
input reset_n,
input raw_button,
output reg filtered_button
);
reg [COUNTER_BITS-1:0] debounce_counter;
reg delayed_input;
always @(posedge clk) begin
if (!reset_n)
delayed_input <= IDLE_STATE;
else
delayed_input <= raw_button;
end
always @(posedge clk) begin
if (!reset_n)
debounce_counter <= 0;
else if (delayed_input != raw_button)
debounce_counter <= DEBOUNCE_TIME;
else if (debounce_counter > 0)
debounce_counter <= debounce_counter - 1;
else
debounce_counter <= 0;
end
always @(posedge clk) begin
if (!reset_n)
filtered_button <= IDLE_STATE;
else if (debounce_counter == 1)
filtered_button <= raw_button;
end
endmodule
Simulation Testbench for Debouncing
`timescale 1ns / 1ns
module tb_button_filter();
reg clk;
reg reset_n;
reg button_signal;
wire processed_output;
initial begin
clk = 1'b0;
reset_n = 1'b0;
button_signal <= 1;
#180
reset_n = 1'b1;
#2000
// Press sequence with bouncing
button_signal <= 0;
#8
button_signal <= 1;
#8
button_signal <= 0;
#8
button_signal <= 1;
#2
button_signal <= 0;
#2
button_signal <= 1;
#20
button_signal <= 0;
#2000
// Release sequence with bouncing
button_signal <= 1;
#8
button_signal <= 0;
#4
button_signal <= 1;
#3
button_signal <= 0;
#3
button_signal <= 1;
end
always #10 clk = ~clk;
button_filter #(
.COUNTER_BITS(20),
.DEBOUNCE_TIME(25),
.IDLE_STATE(1'b1)
) uut (
.clk(clk),
.reset_n(reset_n),
.raw_button(button_signal),
.filtered_button(processed_output)
);
endmodule
Buzzer Control Module
`timescale 1ns / 1ns
module buzzer_controller #(
parameter BUZZER_INIT_STATE = 1'b1,
parameter BUTTON_IDLE = 1'b1
)(
input clk,
input reset_n,
input clean_button,
output reg buzzer_out
);
reg prev_button_state;
wire button_fall_edge;
assign button_fall_edge = prev_button_state & (~clean_button);
always @(posedge clk) begin
if (!reset_n)
prev_button_state <= BUTTON_IDLE;
else
prev_button_state <= clean_button;
end
always @(posedge clk) begin
if (!reset_n)
buzzer_out <= BUZZER_INIT_STATE;
else if (button_fall_edge)
buzzer_out <= ~buzzer_out;
end
endmodule
Buzzer Simulation Testbench
`timescale 1ns / 1ns
module tb_buzzer_controller();
reg clk;
reg reset_n;
reg processed_button;
wire buzzer_signal;
initial begin
clk = 1'b0;
reset_n = 1'b0;
processed_button <= 1;
#200
reset_n = 1'b1;
#200
processed_button <= 0;
#2000
processed_button <= 1;
#2000
processed_button <= 0;
end
always #10 clk = ~clk;
buzzer_controller #(
.BUZZER_INIT_STATE(1'b1),
.BUTTON_IDLE(1'b1)
) uut (
.clk(clk),
.reset_n(reset_n),
.clean_button(processed_button),
.buzzer_out(buzzer_signal)
);
endmodule
Top-Level Integration
`timescale 1ns / 1ns
module main_controller #(
parameter BUZZER_INIT_STATE = 1'b1,
parameter BUTTON_IDLE = 1'b1,
parameter COUNTER_BITS = 20,
parameter DEBOUNCE_TIME = 20'd100_0000
)(
input clk,
input reset_n,
input external_button,
output buzzer_out
);
wire processed_button;
reg input_delay_1, input_delay_2;
always @(posedge clk) begin
if (!reset_n) begin
input_delay_1 <= BUTTON_IDLE;
input_delay_2 <= BUTTON_IDLE;
end
else begin
input_delay_1 <= external_button;
input_delay_2 <= input_delay_1;
end
end
button_filter #(
.COUNTER_BITS(COUNTER_BITS),
.DEBOUNCE_TIME(DEBOUNCE_TIME),
.IDLE_STATE(BUTTON_IDLE)
) debounce_inst (
.clk(clk),
.reset_n(reset_n),
.raw_button(input_delay_2),
.filtered_button(processed_button)
);
buzzer_controller #(
.BUZZER_INIT_STATE(BUZZER_INIT_STATE),
.BUTTON_IDLE(BUTTON_IDLE)
) buzzer_inst (
.clk(clk),
.reset_n(reset_n),
.clean_button(processed_button),
.buzzer_out(buzzer_out)
);
endmodule
Complete System Testbench
`timescale 1ns / 1ns
module tb_system();
reg clk;
reg reset_n;
reg input_btn;
wire buzzer_result;
initial begin
clk = 1'b0;
reset_n = 1'b0;
input_btn <= 1;
#180
reset_n = 1'b1;
#2000
// Press with bouncing
input_btn <= 0;
#8
input_btn <= 1;
#8
input_btn <= 0;
#8
input_btn <= 1;
#2
input_btn <= 0;
#2
input_btn <= 1;
#20
input_btn <= 0;
#2000
// Release with bouncing
input_btn <= 1;
#8
input_btn <= 0;
#4
input_btn <= 1;
#3
input_btn <= 0;
#3
input_btn <= 1;
#2000
// Second press with bouncing
input_btn <= 0;
#18
input_btn <= 1;
#5
input_btn <= 0;
#5
input_btn <= 1;
#2
input_btn <= 0;
#2
input_btn <= 1;
#10
input_btn <= 0;
#3000
// Final release with bouncing
input_btn <= 1;
#12
input_btn <= 0;
#4
input_btn <= 1;
#5
input_btn <= 0;
#15
input_btn <= 1;
end
always #10 clk = ~clk;
main_controller #(
.BUZZER_INIT_STATE(1'b1),
.BUTTON_IDLE(1'b1),
.COUNTER_BITS(20),
.DEBOUNCE_TIME(25)
) system_inst (
.clk(clk),
.reset_n(reset_n),
.external_button(input_btn),
.buzzer_out(buzzer_result)
);
endmodule
Pin Constraints
Pin assignments:
XDC constraint file:
# Clock constraints
create_clock -period 20.000 -name clk [get_ports clk]
# IO pin constraints
set_property -dict {PACKAGE_PIN R4 IOSTANDARD LVCMOS15} [get_ports clk]
set_property -dict {PACKAGE_PIN U7 IOSTANDARD LVCMOS15} [get_ports reset_n]
set_property -dict {PACKAGE_PIN T4 IOSTANDARD LVCMOS15} [get_ports external_button]
set_property -dict {PACKAGE_PIN V7 IOSTANDARD LVCMOS15} [get_ports buzzer_out]