UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

cancel
Showing results for 
Search instead for 
Did you mean: 
Teacher eteam00
Teacher
7,304 Views
Registered: ‎07-21-2009

bouncing switches and debouncers - principles and implementations

For some reason, a number of new users are coming to these forums without an understanding of switches which bounce, and without a notion of how to debounce.  Everyone does debouncing a little bit differently, and there are many ways to do this correctly -- and even more ways to do this incorrectly.

 

So here is yet another debouncer thread...

 

OK, first things first:  Mechanical switches (e.g. pushbuttons) bounce.  For every button push, there will be several (or even many) logic transitions on the FPGA's input pin as the switch makes contact, loses contact, and makes contact again.

 

The typical classroom exercise is to toggle an LED output once -- and only once --  on each button push.  Without switch debouncing, the LED output will toggle a few times or even many times until the switch input stops bouncing.  If the number of 'bounces' is an even number, the LED will seemingly (to the human eye) not change state at all.  If the number of bounces is an odd number, then it will seem as if the pushbutton is working as expected.  So... without debouncing, the simplest logic implementation (without debouncing) will seem to have a flaky switch which sometimes works and sometimes does not.

 

Here is an example of such a (flawed) implementation:

 

input  pushbutton_in;  // pushbutton input, used directly as a clock

output  reg LED_out;

 

// toggle the LED output on every pos edge of the switch

always @(posedge pushbutton_in) LED_out <= ~LED_out;

 

Don't do this!

 

Ignoring implementation for the moment, let's just think through the debouncing process on principles.  For example, the following process steps:

 

1.  Wait for the pushbutton input to change -- this means the button has been pushed.

 

2.  Button has been pushed, let's change the state of the LED output.

 

3.  Change the LED output once and only once when a button press has been detected.  Then wait until the pushbutton input stops changing.  The proper time to wait depends on the characteristics of the pushbutton.  This waiting period, during which subsequent changes of input logic state should be ignored, is called the 'settling time'.

 

4.  Is the pushbutton input settled?  If yes, then we are once again ready to react to the next button push.  Loop to step #1.

 

Does this make sense?

 

Such a sequence involves at least two states of a state machine:

A.  ready to react to a logic level change of the pushbutton input

B.  ignoring input changes, and waiting for the pushbutton input to settle

 

Furthermore, in state B, there needs to be a counter to mark the start and end of the 'settling time'.

 

This scheme, and any similar scheme, requires a free-running clock which is independent of the pushbutton input logic signal.

 

Here is an example implementation of what I've described in functional terms:

 

input clock; // free running clock

input pushbutton_in;  // our beloved pushbutton input, which is NOT used as a clock

output reg LED_out;  // the all-important LED output

 

reg state = ready;  // single bit state register, initialised on power up to 'ready' state

reg [7:0] counter = 0;  // a counter, long enough to mark the beginning and end of settling time

reg pushbutton_delay1 = 0;  // delay the button input by a clock cycle to detect transitions

 

always @(posedge clock)  pushbutton_delay1  <= pushbutton_in;  // delayed copy of input

 

always @(posedge clock)

case (state)

  ready:  // ready to recognise a new button push

    begin

      counter <= 0;  // keep the delay counter initialised to 0

      if (pushbutton_delay1 != pushbutton_in)  // button has been pushed!

        begin

          LED_out  <= !LED_out;  // toggle the LED output

          state    <=  waiting;  // switch states from ready to waiting for end of switch settling time

        end

    end

 

  waiting:  // button pushed, wait for input to be stable for <settling_time> clock cycles

    begin

      if (pushbutton_delay1 != pushbutton_in// input is not yet settled

            counter <= 0; // re-start the settling time delay

      else  counter <= counter + 1;  // count settling time clock cycles

      if (counter == settling_time)  // settling time is over

         state <= ready;  // switch state to ready, accept the next button push

    end

 

  endcase  // only two states

 

There is at least one flaw in this logic.  The asynchronous input pushbutton_in 'drives' multiple clocked registers (state, LED_out), and we cannot be certain that the pushbutton_in input will propagate to all of these registers in the same clock cycle.  A single logic transition might be recognised before clock edge [n] at one register, and after clock edge [n] at another register.  So we must align the asynchronous input to the system clock at a single point (a single register), eliminating the hasard of interconnect skews.  Here is the modified implementation:

 

input clock; // free running clock

input pushbutton_in;  // our beloved pushbutton input, which is NOT used as a clock

output reg LED_out;  // the all-important LED output

 

reg state = ready;  // single bit state register, initialised on power up to 'ready' state

reg [7:0] counter = 0;  // a counter, long enough to mark the beginning and end of settling time

reg [1:0] pushbutton_delay = 0;  // delay the button input by 2 clock cycles to detect transitions

 

always @(posedge clock)  pushbutton_delay[0]  <= pushbutton_in;  // sync'ed copy of input

=> always @(posedge clock)  pushbutton_delay[1]  <= pushbutton_delay[0];  // delayed copy of input

 

always @(posedge clock)

case (state)

  ready:  // ready to recognise a new button push

    begin

      counter <= 0;  // keep the delay counter initialised to 0

=>    if (pushbutton_delay[0] != pushbutton_delay[1]// button has been pushed!

        begin

          LED_out  <= !LED_out;  // toggle the LED output

          state    <=  waiting;  // switch states from ready to waiting for end of switch settling time

        end

    end

 

  waiting:  // button pushed, wait for input to be stable for <settling_time> clock cycles

    begin

=>    if (pushbutton_delay[0] != pushbutton_delay[1]// input is not yet settled

            counter <= 0; // re-start the settling time delay

      else  counter <= counter + 1;  // count settling time clock cycles

      if (counter == settling_time)  // settling time is over

         state <= ready;  // switch state to ready, accept the next button push

    end

 

  endcase  // only two states

 

There is an interesting 'hole' in this logic.  When the pushbutton is first pressed and released, the LED output will toggle.  If the pushbutton is pressed and held, the LED output will toggle on the press and will toggle again on the release.

 

Here is a fix for this hole, assuming (typical case) that the pushbutton input is LOW when the button is pressed and the switch contact is closed.

 

input clock; // free running clock

input pushbutton_in;  // our beloved pushbutton input, which is NOT used as a clock

output reg LED_out;  // the all-important LED output

 

reg state = ready;  // single bit state register, initialised on power up to 'ready' state

reg [7:0] counter = 0;  // a counter, long enough to mark the beginning and end of settling time

reg [1:0] pushbutton_delay = 0;  // delay the button input by 2 clock cycles to detect transitions

 

always @(posedge clock)  pushbutton_delay[0]  <= pushbutton_in;  // sync'ed copy of input

always @(posedge clock)  pushbutton_delay[1]  <= pushbutton_delay[0];  // delayed copy of input

 

always @(posedge clock)

case (state)

  ready:  // ready to recognise a new button push

    begin

      counter <= 0;  // keep the delay counter initialised to 0

      if (pushbutton_delay[0] != pushbutton_delay[1])  // button has been pushed!

        begin

=>        if (pushbutton_delay == 2'b10)  // first transition is 1->0, a press (not release)

            LED_out <= !LED_out;  // toggle the LED output

          state    <=  waiting;  // switch states from ready to waiting for end of switch settling time

        end

    end

 

  waiting:  // button pushed, ignore input changes for <settling_time> clock cycles

    begin

      if (pushbutton_delay[0] != pushbutton_delay[1]// input is not yet settled

            counter <= 0; // re-start the settling time delay

      else  counter <= counter + 1;  // count settling time clock cycles

      if (counter == settling_time)  // settling time is over

         state <= ready;  // switch state to ready, to accept the next button push

    end

 

  endcase  // only two states

 

 

A state machine is not necessary; the counter value can be used as a substitute for a state bit.  Here is an example:

 

input clock; // free running clock

input pushbutton_in;  // our beloved pushbutton input, which is NOT used as a clock

output reg LED_out;  // the all-important LED output

 

reg [7:0] counter = 0;  // a counter, long enough to mark the beginning and end of settling time

reg [1:0] pushbutton_delay = 0;  // delay the button input by 2 clock cycles to detect transitions

 

always @(posedge clock)  // a single clocked process

 begin

  pushbutton_delay <= {pushbutton_delay[0], pushbutton_in};  // sync'ed, delayed copies of input

 

  if (pushbutton_delay[0] != pushbutton_delay[1])  // transition detected!

    counter <= 0;  // always restart counter on any input transition

 

  else  // no input change

    if (counter < settling_time) counter <= counter + 1; // count up to settling_time, then stop

 

  if (    (counter == settling_time)    // input has been stable for long enough

       && (pushbutton_delay == 2'b10) ) // now 1->0 input transition, a button press!

         LED_out <= !LED_out;  // toggle the LED output

 end

 

-- Bob Elkind

SIGNATURE:
README for newbies is here: http://forums.xilinx.com/t5/New-Users-Forum/README-first-Help-for-new-users/td-p/219369

Summary:
1. Read the manual or user guide. Have you read the manual? Can you find the manual?
2. Search the forums (and search the web) for similar topics.
3. Do not post the same question on multiple forums.
4. Do not post a new topic or question on someone else's thread, start a new thread!
5. Students: Copying code is not the same as learning to design.
6 "It does not work" is not a question which can be answered. Provide useful details (with webpage, datasheet links, please).
7. You are not charged extra fees for comments in your code.
8. I am not paid for forum posts. If I write a good post, then I have been good for nothing.
2 Replies
Adventurer
Adventurer
2,549 Views
Registered: ‎01-09-2018

Re: bouncing switches and debouncers - principles and implementations

Thanks !

 

just a small bug in the lowest example:

 

if (pushbutton_delay[0] != pushbutton_delay[1])  counter <= 0;  

  else  if ( (counter == settling_time)   

       && (pushbutton_delay == 2'b10) ) <--- this condition will never happen

 

because of the if clause is (pushbutton_delay[0] != pushbutton_delay[1])

and the else clause is && (pushbutton_delay == 2'b10) 

 

and  (pushbutton_delay == 2'b10) == (pushbutton_delay[0] != pushbutton_delay[1])

 

the condition will never happen

 

 

 

         

 

0 Kudos
2,513 Views
Registered: ‎01-08-2012

Re: bouncing switches and debouncers - principles and implementations

I used to design debouncers (using analog (!)) back in the day, for start / stop buttons on turntables (Technics SP10) for FM broadcast.

 

In addition to the features of your design, I also included a low pass filter in the input to reject RFI and ESD (e.g. from DJ wearing woollen jumper) induced turntable misbehaviour.

This can be done digitally as well, of course, by filtering the input signal pushbutton_in.  This has the downside of introducing delay.  I found a few hundred microseconds was adequate to reject any noise sources encountered in the studio.

0 Kudos