Go Down

Topic: Full Debouncer (Read 809 times) previous topic - next topic


Sep 23, 2013, 06:55 am Last Edit: Sep 23, 2013, 07:17 am by dlloyd Reason: 1
More details in Full Debouncer.pdf below

Code: [Select]

The Full Debouncer is highly tolerant of unwanted noise, occurring at any time and works on
inverted signals that are normally high. It does this by continuously checking if the input
has been stable for 4 consecutive readings and will never update its output unless this
condition is true.

Note: When the micros() function overflows, this will not create a problem. It may add an extra
partial duration period and cause an extra reading to be taken. If this reading is invalid, it will
get filtered out.

Created September 2013
by D. Lloyd  */

// --- Arduino Debounce Section-----------------------------------------------------------------
// constants won't change. They're used here to
// set pin numbers:
const int buttonPin = 2;    // the number of the pushbutton pin
const int ledPin = 7;       // the number of the LED pin  ### CHANGED FROM 13 TO 7 ###

// Variables will change:
int ledState = HIGH;         // the current state of the output pin
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin

// the following variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers

// ---Full Debouncer Section---------------------------------------------------------------------
// Full Debouncer Constants:
const int SignalGeneratorLED = 13;           // Signal Generator Output on pin 13
const int FullDebouncerLED = 12;             // Full Debouncer Output on pin 12

// Signal Generator
const boolean SignalGenerator[200] = {
/* normal phase*/
LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  HIGH, LOW,   /* one impulse */
LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,   /* no change */
LOW,  LOW,  LOW,  LOW,  LOW,  HIGH, LOW,  LOW,  HIGH, LOW,   /* two impulses */
LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,   /* no change */
LOW,  LOW,  HIGH,  LOW, LOW,  HIGH, LOW,  LOW,  HIGH, LOW,   /* three impulses */
LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,   /* no change */
HIGH, LOW,  HIGH, LOW,  HIGH, LOW,  HIGH, LOW,  HIGH, LOW,   /* 5 contact bounces on rising input*/  
HIGH, LOW,  HIGH, LOW,  HIGH, LOW,  HIGH, LOW,  HIGH, LOW,   /* 5 contact bounces on falling input*/  
LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,   /* no change */
/* inverted phase*/
HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, LOW,  HIGH,  /* one impulse */
HIGH, HIGH, HIGH, HIGH, HIGH, LOW,  HIGH, HIGH, LOW,  HIGH,  /* two impulses */
HIGH, HIGH, LOW,  HIGH, HIGH, LOW,  HIGH, HIGH, LOW,  HIGH,  /* three impulses */
LOW,  HIGH, LOW,  HIGH, LOW,  HIGH, LOW,  HIGH, LOW,  HIGH,  /* 5 contact bounces on falling input*/  
LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,  LOW,   /* no change */
LOW,  HIGH, LOW,  HIGH, LOW,  HIGH, LOW,  HIGH, LOW,  HIGH, /* 5 contact bounces on rising input*/  

// Full Debouncer Variables:
int i=0;                                      // signal generator index
long StartTime = micros();                    // start time for the filter
long StartTimeGen = micros();                 // start time for the signel generator
boolean fd1=LOW, fd2=LOW, fd3=LOW, fd4=LOW;   // input status "registers"
boolean InternalRead = LOW;
boolean FullDebouncerOut = LOW;
boolean SignalGeneratorOut = LOW;

long DurationTime = 2000000;     /* duration time in
microseconds for the filter. Suggest using 2000000 (2 seconds)
for visual testing, 200000 (200 milliseconds) for normal operation,
40 minimum for high speed signals. */

long DurationTimeGen = 100000;   // duration time in microseconds for the signal generator.


void setup() {

// --- Arduino Debounce Section----------------------
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);
  // set initial LED state
  digitalWrite(ledPin, ledState);

// ---Full Debouncer Section-------------------------
 // I/O pin setup
 pinMode(SignalGeneratorLED, OUTPUT);
 pinMode(FullDebouncerLED, OUTPUT);


void loop() {

//---begin Full Debouncer--------------------------------------------------------------------------
 InternalRead = SignalGeneratorOut;                 // take reading
 if ((micros()-StartTime) > (DurationTime/4)){      // check if 25% of duration has expired
 fd4=fd3; fd3=fd2; fd2=fd1; fd1=SignalGeneratorOut; // shift previous status, then store new status
 if (( fd1 && fd2 && fd3 && fd4) == HIGH){          // check if stable when HIGH for 4 readings
 digitalWrite(FullDebouncerLED, HIGH);
 FullDebouncerOut = HIGH;
 if (( fd1 && fd2 && fd3 && fd4) == LOW){           // check if stable when LOW for 4 readings
 digitalWrite(FullDebouncerLED, LOW);
 FullDebouncerOut = LOW;
 StartTime = micros();                              // update start time
 } //---end Full Debouncer-----------------------------------------------------------

//---begin Signal Generator----------------------------------------------------------
 if ((micros()-StartTimeGen)>(DurationTimeGen)){
 if (  0 < i < 200) { SignalGeneratorOut = SignalGenerator[i]; }  // run generator
 digitalWrite(SignalGeneratorLED, SignalGeneratorOut); i++;
 if (i >= 200) { i=0; }                                           // reset generator
 StartTimeGen = micros();                                         // update start time
 } //---end Signal Generator----------------------------------------------------------

 //logic analyzer
 Serial.print(" fd4 fd3 fd2 fd1 FullDB ArdDB YourDB SigGen:  ");
 Serial.print("  ");
 Serial.print(FullDebouncerOut);                           // FulldDB
 Serial.print("  ");
 Serial.print(ledState);                                   // ArdDB
 Serial.print("  ");
 Serial.print("-");                                        // YourDB
 Serial.print("  ");
 Serial.println(SignalGeneratorOut);                       // SigGen

 //---begin Arduino Debounce Example---------------------------------
 // read the state of the switch into a local variable:
 //   int reading = digitalRead(buttonPin);
  int reading = SignalGeneratorOut; // READ SIGNAL GENERATOR

  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH),  and you've waited
  // long enough since the last press to ignore any noise:  

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer
    // than the debounce delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // only toggle the LED if the new button state is HIGH
      if (buttonState == HIGH) {
        ledState = !ledState;
  // set the LED:
  digitalWrite(ledPin, ledState);
  // save the reading.  Next time through the loop,
  // it'll be the lastButtonState:
  lastButtonState = reading;

//---end Arduino Debounce Example-------------------------------------

//---begin your Debounce Code----------------------------------------
//---end your Debounce Code------------------------------------------



Sep 23, 2013, 10:52 am Last Edit: Sep 23, 2013, 10:56 am by Graynomad Reason: 1
Umm, doesn't the SAM have hardware debouncing?

I confess I don't follow the code yet but it seem overly complex, I did a debounce func for the LPC1227 (that don't have hardware help), there is an array for each port (holds all 32 pins) and a few fast lines of code that gets executed every timer tick so almost no runtime overhead, then if you want the current debounced version of a pin you call this routine.

Code: [Select]
uint32 debounceReadPin (uint32 pin) {

uint32 pinDebounceVal = 0;
uint32 port = pinPort(pin);
uint32 pin_pos = pinPos(pin);
  uint32 x;

// Step 1
// Extract the vertical data and transform it into
// a single word.
for (int i = 0; i < __debounceInterval; i++) {

x = debouncePinArrays[port][i];    // get the port reading for that mS
x >>= pin_pos;        // move the bit we need into the LSB
x &= 1;                 // Isolate the bit
pinDebounceVal |= x; // Add this bit to the accumulated word
pinDebounceVal <<= 1; // shift accumulated word


// Step 2
// And compare that value
if (pinDebounceVal == 0)
return LOW;

if (pinDebounceVal == (pinDebounceVal ^ 2) -1)
return HIGH;

return -1;


It won't mean much out of context but it's a simple routine and it only gets called when the user wants a pin value.

That was my first pass and it could certainly be improved.

Rob Gray aka the GRAYnomad www.robgray.com


Sep 23, 2013, 06:11 pm Last Edit: Sep 23, 2013, 06:22 pm by dlloyd Reason: 1
Full Debouncer, on its own, is very short with only 5 lines of code in the loop (see code below).

The original code has built in comparison for testing different methods. There is also a built in signal pattern generator and logic analysis by using the status monitor. You only need to run the sketch and open the status monitor. It will reveal the operating characteristics of the Full Debouncer, the Arduino debounce and your own code (if added). The flashing LED on the DUE is the signal pattern used for input. More details: http://www.testcor.ca/pdf/Full%20Debouncer.pdf

I'm not sure if the SAM debounce works on both edges of the input. I'm quite sure it couldn't handle extremely difficult applications, like when trying to count contact closures on a relays with "break" before "make" operation, or with any contacts that bounce for both operations - being switched on and being switched off.

Another example - say the input is a 60 Hz squarewave but has four 1 ms impulses that occur but are required in the design. If the Full Debouncer duration period is set somewhere below 16 ms (use 8 ms), the 60 Hz component of the input could be reliably monitored and/or counted in software.

This method could be adapted to use arrays and/or shortened for high speed, dual-edge cleanup and transmission of bytes of data or other applications.

Code: [Select]

The Full Debouncer is highly tolerant of unwanted noise, occurring at any time and works on both leading
and trailing edges of input transitions as well as on inverted signals that are normally high. The output will
be "set" HIGH or "reset" LOW only when 4 consecutive readings are identical.

Operation:     fdin --> fd1 --> fd2 --> fd3 --> fd4 --> RSLATCH --> fdout

Created September 2013
by D. Lloyd  */

const int fdout = 13, fdin = 12;              // Output pin 13, Input pin 12
long StartTime = micros();                    // start time for the filter
boolean fd1=LOW, fd2=LOW, fd3=LOW, fd4=LOW;   // input status "registers"
long DurationTime = 200;                      // duration time in milliseconds

void setup() {

pinMode(fdin, INPUT);
pinMode(fdout, OUTPUT);

void loop() {

 if ((millis()-StartTime) > (DurationTime/4)){                            // check elapsed duration
 fd4=fd3; fd3=fd2; fd2=fd1; fd1=digitalRead(fdin);                        // shift status then read
 if (( fd1 && fd2 && fd3 && fd4) == HIGH) { digitalWrite(fdout, HIGH); }  // stable when HIGH
 if (( fd1 && fd2 && fd3 && fd4) == LOW) { digitalWrite(fdout, LOW); }    // stable when LOW
 StartTime = millis();                                                    // update start time



The hardware debounce filter of the DUE is already quite powerful and you get it for free without any additional code in the main loop.
see http://forum.arduino.cc/index.php?PHPSESSID=hfj7q1ivrn02qfcq7orovfjtu5&topic=156474.0

As far as I remember from the datasheet it works for both rising and falling slopes and impacts the CHANGE interrupts, but not the RISING and FALLING interrupts.. so in priniciple you can do whatever you want.

The only restriction I know is that the filtering time cannot be chosen differently for two pins that are located on the same port. So if two different filter times are needed the pins need to be on different ports, of which there are 4 available.


Thanks for the link … some excellent info there. I'll have to give the posted hardware function a try when I get a chance. I'm quite new with microcontrollers but am very impressed with the capabilities of the DUE with its SAM3X. After reading section 32.5.9 of the datasheet "Input Glitch and Debouncing Filters" it becomes quite apparent how powerful and flexible this MPU is.

When considering a wide variety of input signals and their sources with various timings and signal quality - I'm attempting to get the widest range possibilities working without having to choose between them. For instance, consider a product that is just a simple counter with one input and one display. The user wants to use this product to:

a)   Totalize contact closures at 0-0.1 Hz with 10ms bounce time.
b)   Count open collector transitions in a noisy environment at 0-10 Hz.
c)   Count a clean, active digital signal operating at 0-1 kHz.

In this case, I would set up the hardware registers or use a hardware function to clean up and debounce the signal for b) without filtering out c).( i.e 250us max filtering and/or delay). Then, I would use a software function to allow proper operation of all 3 items. In this case, both hardware and software debouncing would be required. On the software end, I would simply modify the Full Debouncer to use 10 storage registers and set the delay to 250us. Any latency would be minimal and would not be an issue when counting or for most other applications.


It appears that we need to choose between input glitch filtering (PIO_IFCSR = 0) and  input debouncing filter (PIO_IFCSR = 1). It also appears some other parameters need to be chosen … it's not clear (to me) which settings can work together.

The SAM3X hardware registers do not store previous status or older (only current) status, so any historic status information or pattern recognition of readings would require a software solution.

Go Up

Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

via Egeo 16
Torino, 10131