Pages: [1]   Go Down
Author Topic: Full Debouncer  (Read 523 times)
0 Members and 1 Guest are viewing this topic.
Canada
Offline Offline
Sr. Member
****
Karma: 13
Posts: 401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

More details in Full Debouncer.pdf below

Code:
/*
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, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH,  /* no change */
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, HIGH, HIGH, HIGH, HIGH, HIGH,  /* no change */
HIGH, HIGH, HIGH, HIGH, HIGH, LOW,  HIGH, HIGH, LOW,  HIGH,  /* two impulses */
HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH,  /* no change */
HIGH, HIGH, LOW,  HIGH, HIGH, LOW,  HIGH, HIGH, LOW,  HIGH,  /* three impulses */
HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH,  /* no change */
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*/  
HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH}; /* no change */


// 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);
//---------------------------------------------------


 Serial.begin(9600);
}

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(fd4);
  Serial.print(fd3);
  Serial.print(fd2);
  Serial.print(fd1);
  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------------------------------------------

}

* Full Debouncer.pdf (19.41 KB - downloaded 12 times.)
« Last Edit: September 23, 2013, 12:17:48 am by dlloyd » Logged

Electricity is really just organized lightning - George Carlin

nr Bundaberg, Australia
Offline Offline
Tesla Member
***
Karma: 126
Posts: 8475
Scattered showers my arse -- Noah, 2348BC.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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
« Last Edit: September 23, 2013, 03:56:29 am by Graynomad » Logged

Rob Gray aka the GRAYnomad www.robgray.com

Canada
Offline Offline
Sr. Member
****
Karma: 13
Posts: 401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
/*
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
  }
}

     
« Last Edit: September 23, 2013, 11:22:35 am by dlloyd » Logged

Electricity is really just organized lightning - George Carlin

Offline Offline
Newbie
*
Karma: 1
Posts: 42
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Canada
Offline Offline
Sr. Member
****
Karma: 13
Posts: 401
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

SAM3X:

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.
Logged

Electricity is really just organized lightning - George Carlin

Pages: [1]   Go Up
Jump to: