Debouncing a switch on a microcontroller input is always a problem. You can do it using hardware which often involves a C-R circuit into a Schmitt trigger device and/or latches and gives a very precise switch but at the cost of components or you can do it using software to try and sort out the bounces. This latter method usually involves delays to get you past the twitches as the switch contact bounces back and forth at a micro-to-milli second scale.
At its worst and poorly trapped, contact bounce can easily lead to multiple button presses as you read each spike as a press. Consider the above trace and perhaps you have only waited half as long as you should. Clearly you could fall foul of those two very large spikes towards the end of the period. Very often, delays are used to ensure the switch has truly settled which wastes CPU cycles and leads to sluggish response times. Such a routine might be as follows:
If Pin(DoorUpSW)=0 Then ' if the door switch is pressed (active lo)
Pause 25 ' wait for some time
If Pin(DoorUpSW)=0 Then ' and check again - if still pressed it must be real and finished bouncing
... rest of code here
EndIf
EndIf
This code works well enough but it is hardly elegant and wastes compute cycles. It involves two tests, a lengthy delay and two control structures. There has to be a better way.
Jack Ganssle wrote a University paper "A Guide to Debouncing" in which he approached the problem of de-bouncing in a variety of ways, one of which was a software solution from the point of a state machine. The algorithm runs continuously, successively shifting the input pin (bounces and-all) into a variable and only when (in this case as active lo) you have shifted in enough zero bits has the time elapsed for the reading to be stable. Any bounce during the free-running "spoils" the variable and anything but full scale one way or the other indicates the state of the switch cannot be trusted. Fully zero or fully &H1F (in this example) tells the state of the switch - pressed or not pressed - reliably!
What I really like about this approach is the switch-sample routine is left running all the time and the very lightweight piece of code keeps track of any contact bouncing. The whole time the main loop is running - it's not stuck, spinning, wasting time in some Pause/Delay, the micro-controller is getting on with other stuff and importantly, you don't need to do something specific to test the button by actively running some code or setting a timer etc. If,
at any point, the variable is zero (this is the button's state stimulus) you know the button is reliably pressed. The only "tuning" to be done is to decide how many bits you shift in - this is directly down to the cycle time of your main loop. If the loop executes rapidly, you need to allow more bits to shift in to ensure enough time has passed. A slower loop can have a smaller count.
The following is some working code based on Ganssle's approach - notice the simple elegance of the single line that tracks the bouncing pin and the single test of a variable to see if the button is pressed.
Const DoorUpSW=1
Dim Integer btnUPctr ' counter to hold the shifts
SetPin DoorUpSW,DIN,PULLUP
MainLoop:
Do
...
btnUPctr=btnUPctr<<1 Xor Pin(DoorUpSW) And &h1f ' sample the input pin and shift the bit into the counter
' If this loop is fast, increase the &h1f so we sample more bits... reduce it if the main loop is slower - tune it for your purposes
' If we shift enough zeroes in, the button has been pressed.
' Any bounce (back to 1) will shift a one in and "spoil" our counter for which will start again.
Select Case btnUPctr
Case 0 : Print"dn",timer
Case &h1f : Print"up",timer
Case Else : Print"--",timer
End Select
Loop
The simple output from this loop clearly shows the change of state of the switch from up to down - passing through an indeterminate state as we sample the input pin:
up 15363
up 15375
-- 15386
-- 15398
-- 15409
-- 15421
dn 15432
dn 15444
dn 15455
-- 15466
-- 15478
-- 15489
-- 15501
up 15512
up 15524
up 15535
-- 15546
-- 15558
-- 15569
-- 15581
dn 15592
dn 15604
dn 15615
dn 15627
-- 15638
-- 15649
-- 15661 definite bouncing here as the sequence is: down - indeterminate - down,
-- 15672 but the switch state is un-ambiguous. Trustworthy or not.
-- 15684
-- 15695
-- 15707
-- 15718
dn 15730
dn 15741
dn 15752
dn 15764
References:A Guide to Debouncing - An Alternative, towards the bottom of the page