Double throw switch for debouncing

Hi hi,

I'm new to Arduino (and programming, and soldering) and have just ordered a micro for a footswitch macro keyboard project.

Would using a double throw switch improve debouncing?

I figure if I connect a pin to each pole of the switch and xor the read, that may remove the need to use further debouncing code. I suppose it depends on the performance of the switch. Maybe at the least it could make existing debounce methods more accurate or faster?

edit - originally said double throw - I meant double pole
edit - dammit - I was right the first time - I really do mean double throw (I think) The next few posts were written while the title and body said "double pole". To be really clear I'm talking about the kind of switch that alternates between opening one circuit while closing another.

24/8 Update - Solution: In short, yes. An XOR in the right place removes the need for any timer based debounce code if double throw switches are used.

Example code here: Double throw switch for debouncing - #24 by matmanj - Project Guidance - Arduino Forum

No it wont!. And there's no problem the speed or accuracy of the standard debouncing code.

Mark

matmanj:
I figure if I connect a pin to each pole of the switch and xor the read, that may remove the need to use further debouncing code.

Then you did not actually think of what that would do. Two switch poles fed into an XOR will by definition generate twice as many bounces. :astonished: How do you propose that will help?

The point is - you have an intelligent piece of debounce hardware - it is called a microcontroller. It can perform innumerable debounces in plenty of time for breakfast.

Here is some rather solid - and easily configurable - debounce code of mine. Not a "library". Libraries tend to obscure things.

// Blink without "delay()" - multi!

const int led1Pin =  13;    // LED pin number
const int led2Pin =  10;
const int led3Pin =  11;
int led1State = LOW;        // initialise the LED
int led2State = LOW;
int led3State = LOW;
unsigned long count1 = 0;   // will store last time LED was updated
unsigned long count2 = 0;
unsigned long count3 = 0;

// Have we completed the specified interval since last confirmed event?
// "marker" chooses which counter to check 
boolean timeout(unsigned long *marker, unsigned long interval) {
  if (millis() - *marker >= interval) { 
    *marker += interval;    // move on ready for next interval
    return true;       
  } 
  else return false;
}

// Deal with a button read; true if button pressed and debounced is a new event
// Uses reading of button input, debounce store, state store and debounce interval.
boolean butndown(char button, unsigned long *marker, char *butnstate, unsigned long interval) {
  switch (*butnstate) {               // Odd states if was pressed, >= 2 if debounce in progress
  case 0: // Button up so far, 
    if (button == HIGH) return false; // Nothing happening!
    else { 
      *butnstate = 2;                 // record that is now pressed
      *marker = millis();             // note when was pressed
      return false;                   // and move on
    }

  case 1: // Button down so far, 
    if (button == LOW) return false; // Nothing happening!
    else { 
      *butnstate = 3;                 // record that is now released
      *marker = millis();             // note when was released
      return false;                   // and move on
    }

  case 2: // Button was up, now down.
    if (button == HIGH) {
      *butnstate = 0;                 // no, not debounced; revert the state
      return false;                   // False alarm!
    }
    else { 
      if (millis() - *marker >= interval) {
        *butnstate = 1;               // jackpot!  update the state
        return true;                  // because we have the desired event!
      }
      else 
        return false;                 // not done yet; just move on
    }

  case 3: // Button was down, now up.
    if (button == LOW) {
      *butnstate = 1;                 // no, not debounced; revert the state
      return false;                   // False alarm!
    }
    else { 
      if (millis() - *marker >= interval) {
        *butnstate = 0;               // Debounced; update the state
        return false;                 // but it is not the event we want
      }
      else 
        return false;                 // not done yet; just move on
    }
  default:                            // Error; recover anyway
    {  
      *butnstate = 0;
      return false;                   // Definitely false!
    }
  }
}

void setup() {
  pinMode(led1Pin, OUTPUT);      
  pinMode(led2Pin, OUTPUT);      
  pinMode(led3Pin, OUTPUT);      
}

void loop() {
  // Act if the latter time (ms) has now passed on this particular counter,
  if (timeout(&count1, 500UL )) {
    if (led1State == LOW) {
      led1State = HIGH;
    }
    else {
      led1State = LOW; 
    } 
    digitalWrite(led1Pin, led1State);
  } 

  if (timeout(&count2, 300UL )) {
    if (led2State == LOW) {
      led2State = HIGH;
    }
    else {
      led2State = LOW; 
    } 
    digitalWrite(led2Pin, led2State);
  } 

  if (timeout(&count3, 77UL )) {
    if (led3State == LOW) {
      led3State = HIGH;
    }
    else {
      led3State = LOW; 
    } 
    digitalWrite(led3Pin, led3State);
  } 
}

This code also demonstrates timing of multiple repetitive events. Do try it out.

I didn't want to reply before doing some testing.

I am disappointed that the only feedback I received was negative, insulting, condescending, and incorrect.

Granted, I have only tested one double pole switch but it was a very clear success. A simple xor test is all that was necessary to achieve a very reliable debounce on an otherwise quite bouncy switch.

  this_read = digitalRead(pin5);
  if ((this_read ^ button_state) & (this_read ^ digitalRead(pin6))){
    //button state has changed and no further debounce code is required
    button_state = this_read;
  }

Mr "you did not actually think", if you're going to swing that insult around wouldn't it make sense to do a bit of thinking yourself?

Mr "No it wont!", if the circuit and hardware permit, how is this not much better than timers or multiple reads?

Why does it work? While "actually thinking" about it you could consider that both poles may not transition at exactly the same moment and their bounce periods may not intersect at all. If so, there is always a stable state to compare against.

I'm not declaring that this is the only right way to debounce or even that it's the best way. Pins are a limited resource after all. It is the method I'll be using for this project though as I have enough pins and the switches I got for it are double pole. For me it's worth the extra wiring.

Apologies for the pained tones but I can't abide a loudly confident fool.

matmanj:
Mr "you did not actually think", if you're going to swing that insult around wouldn't it make sense to do a bit of thinking yourself?

Mr "No it wont!", if the circuit and hardware permit, how is this not much better than timers or multiple reads?

Apologies for the pained tones but I can't abide a loudly confident fool.

I don't think either of those comments is at odds with the information you provided.

And before dismissing people who try to help as fools you might usefully ask yourself "is that person able to do what I don't yet understand?"

Before worrying about how to solve a debounce problem do some testing to find if you actually have a problem. I understand exactly what switch bouncing is and I have never yet needed to add any specific code to my sketches to deal with it - in spite of it being a popular topic for discussion in this Forum.

...R

Another anti? Is that all we have for contributors here?

You have well demonstrated yet again the confident ignorance that bugs me so much. In paragraph order...

Not at odds? Lets see. "Would using a double pole switch improve debouncing?" "No it won't!" Yes it does.

"What I don't yet understand?" I understand it by testing and measurement. These people's comments show a lack of comprehension or understanding. At the time of providing their comments these people were foolish and ignorant and it's not wrong to say it. I have improved my understanding despite their lack of help.

I no longer have a problem with bouncing. It was fixed with a single XOR. Your projects may not need debouncing but a computer keyboard without bounce compensation is very frustrating to use, generally unusable.

I considered the advice given very carefully almost to the point of conceding that I just didn't get it. I nearly missed out on a truly elegant solution because of ignorant but confidently (and insultingly) spoken advice. I will not apologise again for admonishing the dressing up of ill considered guesses as fact.

You have a nice day, too.

...R

matmanj:

  this_read = digitalRead(pin5);

if ((this_read ^ button_state) & (this_read ^ digitalRead(pin6))){
   //button state has changed and no further debounce code is required
   button_state = this_read;
 }

Now that the dust may have settled I thought it might be interesting to look again at what all the noise is about.

I think the code can be rewritten like this

p5 = digitalRead(pin5);
p6 = digitalRead(pin6);
xorA = p5 ^ buttonState;
xorB = p5 ^ p6;
andXorResults = xorA & xorB; // note this should not be &&
if (andXorResults == true) {
  change buttonState;
}

Assuming, as the OP proposed, that pins 5 and 6 are connected to a double pole switch they will ALWAYS read the same unless the switch is broken. That means that it would be just as valid to have xorB = p5 ^ p5 which, of course is identical to xorB = 0;

In the circumstances I don't see that any extra information is being provided by using two Arduino pins instead of one and if the project is not suffering from switch bounce it is simply because the chosen switch is not bouncing.

Perhaps others understood this immediately without needing to wade through the details.

Magnet motors spring to mind.

...R

  • edit: I see my blunder now. I have incorrectly mixed my switch terms up. For every time I said double pole please read double throw. The whole time I have been talking about paired switches with opposite polarity and everyone else has been imagining a pair of switches with the same polarity. Now that i think about it though the same principle potentially applies to a pair of same polarity switches. I can and will test that soon. What is the name for one side of a double throw switch anyway? Until I know better I'm going to call them "poles".

Looks like you understand the intent of my code example.

The assumption that is holding you back is that both poles transition simultaneously. At microsecond time scales generic switches will not make one pole at precisely the moment that the other pole breaks. That means there IS a period when both poles will show the same state. Further, if the pole offset period is greater than the bounce period then the XOR example provided is enough.

I have tested this concept a number of ways:
test 1: I wrote Bounce Profiler to sample bounce behaviour and export the data
76f97916dcaf74cf26f7cbcaa05dac68035bb520.png
This report shows a rather short break bounce followed 2ms later by the make. Notice that at no time are both poles bouncing simultaneously.

test 2: I built a simple bounce test circuit that would light an LED if the button changed twice within 30ms. Like I said previously, this particular switch is rather bouncy and the result of this test made it VERY clear that this switch is completely debounceable with the XOR method in my example.

test 3: Thinking about it:
Keep reading pole 1
Pole 1 makes circuit //button is pressed
Pole 2 has already broken circuit and its bounce period has already passed
Pole 1 passes the changed and Pole 2 XOR tests.
Record event - button has changed to LOW (or HIGH or whatever)
Pole 1 continues to bounce but fails either the changed test or the pole 2 XOR test.
Pole 1 breaks circuit //button is released
Pole 2 has not yet made circuit.
Pole 1 changed test passes
Pole 2 XOR test fails
Pole 1 continues to bounce and fail either the changed or pole 2 XOR tests
Pole 1 finishes bouncing and is now stable but still fails pole 2 XOR test
Pole 2 makes circuit.
Pole 1 now passes both the changed and pole 2 XOR tests.
Record event - button has changed to HIGH (or LOW or whatever)
Pole 2 continues to bounce but the changed test fails either way

In effect this is a lockout type debounce with a period equal to the time between break and make events in the switch.

I haven't put much thought into the implications of a switch with overlapping bounce periods but that's a problem I don't need to solve right now.

I'm starting to get the feeling that this is a novel concept to some. Is there really no precedent of debouncing switches this way? Curious.

Is there anything here that isn't just straight up sensible? I'm certainly not claiming to violate Newtonian convention as implied.

edit:

  • If the bounce periods do overlap a lockout timer would be needed only for the time that both poles are bouncing.

  • For double pole (same polarity) switches the XOR would change to AND.

  • Because double throw switches are doing two mechanically different things when pressed (make and break) they are more likely to have a usable delay for the purpose of this method.

Don't know if this helps but feed the double throw switch to a simple cross coupled latch then to the Arduino input.
100% no bounces.
No software involved!

Thanks LarryD,

Why didn't I think of it that way? Correct me if I'm wrong but haven't I basically described doing an SR latch in software? If not is a software SR latch possible? What would it look like?

Paul__B wasn't wrong about using the silicon we already have in the MCU. I already have transistors. I'd prefer to use those rather than wire in more.

Why didn't I think of it that way? Correct me if I'm wrong but haven't I basically described doing an SR latch in software? If not is a software SR latch possible? What would it look like?

I am mostly hardware.
I would definitely do it in hardware if it was me.
Even though I use software now and then, I believe software de-bouncing is NOT time well spent in code.

If you don't want to use an I.C. use two transistors.

2014-08-25_0-37-09.jpg

Am I right in assuming that this circuit is only guaranteed to be bounce free if TR1 and TR2 are never simultaneously connected?

What happens if A is still bouncing when B makes circuit?

Is it commonly accepted that double throw switches must finish breaking one side before making the other side?

Am I right in assuming that this circuit is only guaranteed to be bounce free if TR1 and TR2 are never simultaneously connected?

Yes in normal double throw devices this NEVER happens.
This happens only in very special switches that are make before break . . .

Bouncing is NOT a contact going from one position to the other position issue.
Bouncing is a make and open circuit issue.

Thanks again Larry.

Apologies to everyone who was thrown by me calling a double throw switch a double pole switch.

Now it's clear to me that I'm just reproducing a double throw debounce circuit in software. No magic involved and 100% effective. Simply trading an extra pin and wire to each switch in return for much simpler, faster code that doesn't need timers.

matmanj:
For every time I said double pole please read double throw. The whole time I have been talking about paired switches with opposite polarity and everyone else has been imagining a pair of switches with the same polarity.

Sorry, but I still don't follow. Can you draw a diagram of how your switch is wired and post a photo of the drawing?

The other issue I am struggling with is the fact that digitalRead() captures a single instantaneous value (either HIGH or LOW - using the INPUT_PULLUP option LOW signifies the switch is closed). Unless the code reads the pin again after a very short interval it will not know there was a bounce and, even then, it only matters if the new switch value has an impact on the logic of the program. It seems to me much easier to design the code so the bounce does not affect the logic. Easier to understand and easier to write. If reading a pin too frequently causes a problem then don't read it so frequently.

...R


If using the switch depicted above connect L2 to pin 5, L1 to pin 6 and COM to ground.

// pseudo program using software SR latch debounce
// using both sides of a double throw switch
int button_o_pin = 5; // button normally open side
int button_c_pin = 6; // button normally closed side

void setup() {
  // make the pushbutton's pin an input:
  pinMode(button_o_pin, INPUT_PULLUP);
  pinMode(button_c_pin, INPUT_PULLUP);
}

void loop() {
  // initialize the static remembered state variable to the resting state of button_o_pin
  static int button_state = 1;
  int button_changed = false;

  // begin button state check with debouncing
  int button_o_read = digitalRead(button_o_pin);
  if ((button_o_read != button_state) &&
      (button_o_read != digitalRead(button_c_pin))) { 
    button_changed = true;
    button_state = button_o_read;
  } 
  // end button state check with debouncing
  
  if (button_changed == true){    // because I'm using PULLUP and reading the...
    if (button_state == LOW) {    // ... normally open side, LOW == pressed
      // stuff to do when the button is pressed
    }
    else {
      // stuff to do when the button is released
    }
  }
  // stuff to do regardless of the button's state
}

This code gets an instant accurate read of the current state of the switch. It determines if the switch state has changed by comparing to the value remembered from last time the loop was run.

To explain the debounce another way: A read is only recorded when the sides show as opposite to each other AND the reading is different to last time. If both of these conditions aren't met then the current read is discarded and the state remains unchanged for this loop run. A bounce is always discarded because the secondary read is never bouncing at the same time.

That is a single pole double throw switch - so you will understand our confusion.

The way you are using it the switch is "closed" to one side all the time - apart from the brief period while the contactor is moving.

I will think about it a little more, but I still don't see that the mechanical arrangement eliminates bouncing. As far as I can see any of the following 3 switch states is possible
1 0
0 1
0 0 // when the contactor is moving

And the 4th state 1 1 is not possible.

The state 0 0 won't cause anything useful to happen, so can be ignored. And I don't see the need for the LHS of the other 2 states. You get the same information with
0
1
as with
1 0
0 1

It would also be useful if you could post a short sketch that demonstrates a switch bounce problem.

...R

I will think about it a little more, but I still don't see that the mechanical arrangement eliminates bouncing. As far as I can see any of the following 3 switch states is possible
1 0
0 1
0 0 // when the contactor is moving

I think the suggestion is that any mechanical bounce will be limited to:

a) rapid alternation between 1 0 and 0 0 (as the switch moves away from one contact) , and
b) rapid alternation between 0 0 and 0 1 (as it arrives and settles at the other contact), and
c) vice versa when the switch is released

It assumes the switch won't bounce all the way from (for example) 1 0 via 0 0 to 0 1 (and back again).

At the start I was calling the pictured switch double pole. I spotted my goof and apologised a few posts ago. Definitely a very significant error on my part. All my thinking and posting has actually been concerning double throw switches.

Here's the thing:

0 0 // when contactor is moving and when contactor is bouncing

Both states will be ignored by my code. You can't ignore 0 0 state if you're only reading one side of the switch.

It assumes the switch won't bounce all the way from (for example) 1 0 via 0 0 to 0 1 (and back again).

According to LarryD that will never happen unless the switch is specifically a "make before break" type of double throw.

//pseudo program: annoying bounce problem
loop(){
  rocketLauncher.ammo = 3;
  if (buttonRead(button2) == just_pressed) {
    rocketLauncer.Fire();
  }
  // chance to fire between 1 and 3 rockets on both press and release of button 2 unless I debounce.
  // these psuedo rockets are expensive and wasting them is tanking my scores :-(

  // no annoying problem:
  if (digitalRead(button_pin) == LOW){
    digitalWrite(LEDpin, HIGH)
  }
  //nobody cares if that one bounces unless maybe they're 
  //using a flashing LED to transmit data from a mechanical sensor switch
}

Sure, if we sample the button less often we statistically reduce the chance of reading a bounce. How should we do that? A timer? Delay()? My point is if there's a chance for button bounce to make your program misbehave, best to nip it in the bud right in the pin reading code.