fpga4fun.com - where FPGAs are fun.
Home
Welcome
Information


FPGA projects
Music box
Pong game
R/C servos
Text LCD module
Quadrature decoder
PWM and one-bit DAC
Debouncer
LED displays
Crossing clock domains
External contributions

FPGA interface projects
RS-232
JTAG
I2C
EPP
SPI
CNC steppers

FPGA advanced projects
Graphic LCD panel
Digital oscilloscope
10BASE-T interface
PCI interface
Spoc CPU core

Hands-on
A simple oscilloscope


FPGA introduction
What are FPGAs?
How FPGAs work
FPGA pins
Clocks and global lines
Download cables
Configuration
Learn more

FPGA software
Design software
Pin assignment
Design-entry/HDL
Simulation/HDL
Synthesis and P&R

FPGA electronic
SMD technology
Crystals and oscillators

HDL info
HDL tutorials
Verilog tips
VHDL tips

Quick-start guides
ISE
Quartus

Site
News
FPGA links
HDL tutorials
Forum


Debouncer - The solution

One solution would be to add an R/C hardware filter, and use a Schmitt-trigger gate to feed the FPGA.
But there is a simpler solution.

FPGA filter

FPGAs are great at simple arithmetic. Let's use a counter in the FPGA to see how long the push-button is pushed or released. Only once the counter is maxed-out, we decide that the push-button has changed state.

PB is the push-button signal (active low in this example). It may contain glitches, and is asynchronous to any clock. So it is mostly unusable as it is.
We are going to synchronize PB to a clock (20MHz in this example) and then create three push-buttons outputs, glitch free, synchronous to the clock. Each output will be active high and indicate a different condition of the push-button (push-button state, just pushed, just released).

module PushButton_Debouncer(clk, PB, PB_state, PB_up, PB_down);
input clk;  // "clk" is the clock
input PB;  // "PB" is the glitched, asynchronous, active low push-button signal

output PB_state;  // 1 while the push-button is active (down)
output PB_down;  // 1 when the push-button goes down (just pushed)
output PB_up;  // 1 when the push-button goes up (just released)

// First use two flipflops to synchronize the PB signal the "clk" clock domain
reg PB_sync_0;  always @(posedge clk) PB_sync_0 <= ~PB;  // invert PB to make PB_sync_0 active high
reg PB_sync_1;  always @(posedge clk) PB_sync_1 <= PB_sync_0;

// Next declare a 16-bits counter
reg [15:0] PB_cnt;

// When the push-button is pushed or released, we increment the counter
// The counter has to be maxed out before we decide that the push-button state has changed

reg PB_state;  // state of the push-button (0 when up, 1 when down)
wire PB_idle = (PB_state==PB_sync_1);
wire PB_cnt_max = &PB_cnt;	// true when all bits of PB_cnt are 1's

always @(posedge clk)
if(PB_idle)
    PB_cnt <= 0;  // nothing's going on
else
begin
    PB_cnt <= PB_cnt + 1;  // something's going on, increment the counter
    if(PB_cnt_max) PB_state <= ~PB_state;  // if the counter is maxed out, PB changed!
end

wire PB_down = ~PB_state & ~PB_idle & PB_cnt_max;  // true for one clock cycle when we detect that PB went down
wire PB_up   =  PB_state & ~PB_idle & PB_cnt_max;  // true for one clock cycle when we detect that PB went up

endmodule

We used a 16-bits counter. With a 20MHz system clock, it would take 3ms to max-out. From the user's perspective, 3ms is instantaneous. But the glitches are gone. Depending on your push-button and your system clock, you might need to adjust the counter width.

Your turn to experiment!






This page was last updated on February 25 2007.