De-bouncing button

Bouncing button is a well known problem in embedded circuit design. It is due to the fact that the mechanical switch when pressed, producess - other then level transision - a series of spikes  of High and Low signals that can be interpreted by CPU as more than one interrupt. As a result a single button press results to a multiple CPU interrupts - which in any case is undesirable.

There are a few well known solutions to the problem which are well documented and easily found online and i will not repeat them but rather describe a simple software solution in the context of acorn kernel.

Let us assume that we have a button connected to INT0 regrdless of the AVR CPU as long as it runs on acorn kernel and have a single task to wait on and process button press.

Our objective is getting rid of the consecutive interrupts generated after the first level transition due to the series of spikes. So all we need to do is accept the first interrupt and filter out all consecutive ones that fall within certain time interval.

Here is the proposed solution in tinyAcorn task.

/*
External Button handle
*/
.set dpc_index2=2


1.Task_2:    
2.    in temp,MCUCR
3.    sbr temp,(1<<ISC01)       ;intr on falling edge INT0
4.    cbr temp,(1<<ISC00)
5.    out MCUCR,temp

6.    in temp,DDRB
7.    sbr temp, (1<<PB1)    ; Set PB1 as output to control LED
8.    out DDRB, temp

9.main2:
10.    in temp,GIMSK
11.    sbr temp,1<<INT0
12.    out GIMSK,temp
13._INTERRUPT_WAIT dpc_index2
14.     in temp,PINB
15.    cbr temp,1<<PB1
16.    sbis PINB,PB1
17.    sbr temp,1<<PB1
18.    out PORTB, temp         
19._INTERRUPT_END  dpc_index2

20._SLEEP_TASK 250
21._SLEEP_TASK 250
22._SLEEP_TASK 250

//clear pending
23. in temp,GIFR
24. sbr temp,1<<INTF0
25. out GIFR,temp

26.rjmp main2      
27.ret



And the interrupt handler itself.

1.int0INT:
2._PRE_INTERRUPT       

//disable interrupt if first level transition
3.    in temp,GIMSK
4.    sbrs temp,INT0
//don't dispatch if consequtive pending interrupt
5.    rjmp endint0
6.    cbr temp,1<<INT0
7.    out GIMSK,temp
9._keDISPATCH_DPC dpc_index2

10.endint0:
11._POST_INTERRUPT
12.reti

We will be using a Deffered Procedure Call to serve the external INT0 interrupt. The semantic of DPC states that:

  1. Deffered Procedure is executed at DEVICE level.
  2. If the DPC task is in sleep, it will be awaiken to process the DPC.

Task lines (13,19) mark DPC boundery executed at DEVICE level. It simply toggles a LED. Lines (20,22) put the task to sleep at USER level. We need to make sure that code (13,22) executes without the pressence of another interrupt.

In inttrupt handler int0INT code (6,9) disables external interrupt if it is the first spike and queues DPC for processing it. All consecutive spikes during the DPC task sleep are discarded (10,12) thus preventing the DPC task waik up. DPC task sleep time out is determined to fit the speed of the button presses.