Debouncing

I want to use a debouncer within an ISR, and this is the code I wrote:

ISR(PCINT0_vect) {
// Interrupt service request for external interrupt
// Interpret switches position
      static uint8_t prevState = 0x03;
      // Get current state
      uint8_t startState = ((((PINC >> _swA) & 0x01) << 2) | (((PINC >> _swB) & 0x01) << 1) | ((PINC >> _swPb) & 0x01)); 
      // Simple debouncing. 1 nop = 62.5 ns, 1600 nops approx 100 us
      for (int i=0; i < 1600; i++) 
            asm volatile ("NOP"); 
      // Get current state
      uint8_t stopState = ((((PINC >> _swA) & 0x01) << 2) | (((PINC >> _swB) & 0x01) << 1) | ((PINC >> _swPb) & 0x01)); 
      // Check if the previous state was stable and different from the previous one
      if ((startState == stopState) && (stopState != prevState)) { 
            _buttonDown = (~startState & 0x01); // Update button state
            if ((startState >> 1) == 0x03) { // If in idle state
              // Decode steps pattern
                  if (patternBuffer == patternCW) 
                        _counts++;             
              if (patternBuffer == patternCCW) 
                        _counts--;             
                  patternBuffer = 0x00; // reset buffer
            }
            else {
                  // Append state to the buffer
                  patternBuffer <<= 2; 
                  patternBuffer |= (startState >> 1) ;
            }
            prevState = stopState; // Record state for next change
      }
}

It sounds like there are much better solutions. Does one of you has contructive comments and suggestions?

Hey there,

You need to see if you can shorten that interrupt routine- they need to be as short as possible, because other interrupts may be waiting, or even missed- you should just get the data you need and let your main code deal with any data manipulation.

When you read a port "PORTC", you should instead read "PINC" (see section 13.2.4 of the datasheet)

I think that all the states should be volitile global variables, so that the main code can take some functionality from that interrupt routine.

:slight_smile:

There's a very long loop in that routine - I'd really recommend getting rid of it.

Oooops about
"When you read a port "PORTC", you should instead read "PINC" (see section 13.2.4 of the datasheet)"

Your are 100% right, I simplified my code for the post, please read PINC instead. In my application PORTs are passed as arguments and the pin reading is slightly less self explicit.

uint8_t startState = ((((PIN(*_encPort) >> _swA) & 0x01) << 2) | (((PIN(*_encPort) >> _swB) & 0x01) << 1) | ((PIN(*_encPort) >> _swPb) & 0x01));

For my code, I just had my interrupt increment an int like so:

void ISR(){

dummyvar++
}

and then in my code, to check if a button was pressed I just do

if (dummyvar){
//do stuff
dummyvar=0;
}

Sorry if I sound unconstructive from time to time - this is due to some needed comic relief :slight_smile:

What is bouncing? When a switch is closed, it will shortly reopen after that due to mechanical elasticity, this is the same as if you let go a ball to the floor which will bounce many times until it rests. The length of this bouncing phase differs greatly between switches, not only between brands but also between specimen, and also depends on age and wear.
A "last bounce" can even happen 100ms after the switch is pressed.

Note that there can be no bouncing when a switch is re-opend, but there is a similar though much shorter electrical effect.

It is considered good practice in software debouncing not to wait until the bouncing is over, but to immediately act at the first edge because you know that the key has been pressed!

Now after some internal activities are over you are wait.ing again that the key has been pressed. You can be in of three situations:

  • The key is still bouncing
  • The key is still pressed
  • The key is already open

How to handle this depends on whether you look at a rising edge - often leading to an interrupt - or if you are polling looking at the level.

The solution of BetterSense is excellent, if the time of "do stuff" is long enough to have left the bouncing phase.

Generally there will be no advantage whith interupts. At the place where you check the dummyVar-flag you can as well read digitalInput(). (Except the key press is very short and you are in danger to miss it!)

The tricky thing comes when the processing of the keypress event is over. Now you have to look for an open key phase, long enough so it cannot be confused with a bounce spike (those spikes are very short btw and in fact difficult to probe without edge sensitive hardware...)

In cases when you can poll regularly, you can use a state variable openCounts to know that a resonably open time has passed

The Code for this might go (sorry no check, just typed in)

// poll button
if (digitalRead(buttonPin) {
  if (openCounts>2) {

     // do things

    }
    openCounts= 0; 
} else 
  openCounts ++;

Well debouncing a switch contact efficently inside a ISR is kind of tricky, as there could still be a lot of useless interrupts being generated from the switch bounces. If it could be debounced with a external filter components it might make for a simpler more reliable system, just saying....

Lefty

Thanks all for these very helpfull answers.
What I learned from them is that my first intuition which consisted in staying away from interrupts for debouncing was funded.

Just for your records, ALPS data sheets for rotary encoders series EC12 says <= 3 ms. But yes, some switches may bounnnnnnnnnnnnnnnnnnce for a longer time.

After multiples tests, I can say that the push button switch is much more prone to bounces than the A & B contacts from the encoder.

This is black art... High quality buttoms http://www.ck-components.com/13328/digitast_9mar10.pdf/differ, in that their bounce time is specified, most likely accomplished by selection....

In mechanical rotary switches the contacts slip in a lateral movement which most likely reduces bouncing conciderably, as many points of contact exist...

It is considered good practice in software debouncing not to wait until the bouncing is over, but to immediately act at the first edge because you know that the key has been pressed!

It is considered good practice in electronics to make sure that the circuit is not experiencing Impulse noise and giving a false reading. You don't know that the first edge is due to the button being pushed: Instead, you should recheck a short time later to see if the input is still high.

Generally speaking, debouncing applies to many places, other than switches.
At this stage I may explain (taking the risk to bring some confusion) that my personnal approach to Arduino is to try to challenge it using the minimal number of external components. In the case of the rotary encoder, I did not even use the recommanded filter (RC, 10 k + 10 nF wired to ground)! So that I tried to apply a software only solution to the bouncing problem. And it works fine. I would not obviously recommend that for military/medical devices :P)
Back to bouncing in general, I remember very well a very nicely written paper in Measure (the internal newspaper at HP) dealing with pulse recognition from magnetic heads in 10 Mb disk drives (which were as big as washing machines :smiley: ): this was the starting point of my interest to signal recognition. Since then I paid strong interest to that, applied to other purposes (I am actually lecturing this science from times to times at university). But the techniques in use for such debouncing techniques require a lot of real time analysis and high tech ressources. Thus my choice for having a little fun with a minimalist approach (Way back to Occam razor) :wink:

I struggled with bounce on a reed switch using interrupts in one of my projects and trying to handle it in software. I gave up and implemented a hardware solution, works perfectly now :wink:

It is considered good practice in electronics to make sure that the circuit is not experiencing Impulse noise and giving a false reading. You don't know that the first edge is due to the button being pushed: Instead, you should recheck a short time later to see if the input is still high.

You said that just to be funny, right?
If you expect noise or spikes of TTL level in your line, we are no longer talking about "de-bouncing", but about "de-glitching" or how you want to name it. There are of course techniques to address both at the same time.

What do you mean by "TTL level"? Are you trying to return a joke? Microprocessors are built with CMOS technology.

I was working on my example above, which is quite stable code.. Accidentically probing in a "spike" is very, very unlikely. This is of course differently when your interrupt-system is lurking for that :slight_smile:

There can be more in "switch" but a switch, though generally it is counterpart of a pulldown or pullup of 100k or less. To inject here a 1us spike of 2.5 volts is - tricky.

But, yes, there can be circumstances...

Actually, it's quite easy when the switch is in the other room and is connected with unshielded wire.

If all digital inputs where 100% reliable, we would not need error detection (parity checks, cyclic redundancy checks, ...) on serial communication.

I'm sorry, but I don't think that to "just do it" when the first edge is detected is good practice (for a switch input). This is because the chance of having a program long enough (quote "100ms") to oversee the bouncing is "very, very unlikely".

The edge detector, in my opinion, is much better suited to analysing digital data communication, rather than a switch input.

I never talked in favour of "edge detection", on the contrary!
Noise is a very critical issue with analog input. True "noise" on digital lines is rare. What you get is "spikes", the source of which is often a carelessly attached brush motor.

Still I cannot see how this will affect an input pin pulled by the typical paranoic value of 10k to well decoupled + or ground....

Edit

I just managed to dig out an articled I had read some years ago, with a lot of details..Debouncing Contacts and Switches

A Digital signal is just an interpretation of an analogue signal and obeys the analogue laws, such as attenuation. If the signal to noise ratio (SNR) is not large enough, your chances of reading a false reading are much more probable (this is the power of the signal, not voltage).

Because we are dealing with small currents here (hence lower powered signals), false readings should be a concern. The problem with impulse noise is that it is a high powered signal, which can be caused by coupling with a light being turned on or off (not just a brush motor).

It would be wrong to assume that the switch used in Joker's project is of ideal nature. How long are the wires? How thick are the wires? What possible sources of noise are there where their project is designed to run? You don't know what environment and hardware Joker's project will be operating with, so saying that the probability of transmission impairments occurring is "very, very unlikely" is just not a good assumption. What if the switch was to turn on a nearby "brush motor"?


On Debouncing Contacts and Switches

The experiment in this article does not address the issue of any sort of transmission impairments (eg: attenuation, delay distortion, thermal noise, impulse noise, crosstalk, ...). It does not say what power the signal it is, so it would be wrong to speculate; however, it does seem to be strong enough to ward off the effects of noise.

It also acknowledges the random nature of bouncing saying that 2 switches exceeded 6.4mSec in bouncing time, with one sample at 157mSec in opening (I'd put that switch in the bin). This article finished by saying, "Assume things are worse than shown".

If you then click on at the bottom of the page, there are many different techniques for solving the debouncing switch problem.

It says, "But the environment itself can induce all sorts of short transients that mask themselves as switch transitions. Called EMI (electromagnetic interference), these bits of nastiness come from energy coupled into our circuits from wires running to the external world, or even from static electricity zaps induced by shuffling feet across a dry carpet. Happily EMI and contact whacking can be cured by a decent debounce routine. . . . but both factors do affect the design of the code".


The bottom line of what I (and page 2 of the article deSilva provided) was trying to say is that using debouncing methods in your code/hardware is good practice. Also, methods for debouncing can usually double up as protection against some transmission impairments: Therefore, debouncing should NOT be omitted from your design as suggested by deSilva.

I definitely support nearly everything said. But being a little bit theoretically oriented, I can't help noticing that debouncing is not deglitching. Both try to avoid to erroniously find a signal where no signal is. Also both can be considered as a kind of "pattern matching"... Still one has to know what the characteristics of both issues are, and they are different.

It will do no harm to also do all-purpose deglitching. But when you feel you need this, then you are in greater trouble than you think in the first place. When you have to debounce a switch then you just have a common switch.

The simple algorithm I gave above will work in many situations. It will be less reliable when you exchange the level-probing by interrupt edge detection. That will be something quite different. So when you - for what reason ever - have to use interrupts, my small example is not at all the best algorithm!

I - and some of you - have used small devices that react to a pressed key after 200 ms only. This is awkward. The programmers meant well I am sure, but this is not my style..