A Simple Code Warrior Project in C
Introduction
The push-button switches are often used to provide input to the microcontroller. However, mechanical switches do not open or close cleanly. When a switch is pressed it makes and breaks contacts several times before settling into its final position. This causes several transistions or "bounces" to occur.
The following photos illustrate the switch debouncing problem. They show the response of the yellow sw5 pushbutton switch attached to port H of the Dragon12 board. The first photo shows the falling edge transition from +5V to 0V that occurs when the switch is pressed. In this case there is almost no bounce.
The next photo illustrates the response when the switch is released (rising edge). There is a noticeable amount of bounce that lasts about 600 microseconds before a steady state is achieved.
The length of time a switch bounces can vary. The following photo also shows the response when the switch was released, however this time the bounce lasts only about 400 microseconds.
The M68HC12's processor is fast enough to see each bounce as an independent input. The extra transistions can be eliminated by debouncing the switch. This ensures that only one transition is recorded when the switch is pushed or released. A switch can be debounced in hardware, using an RC circuit, or with software by implementing a delay. However, the delay must not be too long or else a valid switch transition may be missed. The photo below shows the behavior of the switch on a larger scale. In the photo sw5 was pressed and released two times as quickly as possible. There is about 80ms between transistions. The switch bounce is not visible because of the larger time scale, but it probably did occur.
For the Port H yellow pushbutton switches, a software delay time of 1 msec should be sufficient to surpass the bounce time and allow all valid switch transitions to be recorded.
Finally, it should be noted that different types of switches can have different debounce behavior. On the Dragon12 board, the Port H 8-position DIP switches have a bounce that was quite different than that of the yellow pushbuttons. The screenshot below shows switch bounce on the falling edge when a DIP switch was changed. Recall that the yellow pushbutton switches typically did not show bounce on the falling edge.
Several tests of the DIP switch indicated that for the rising and falling edges, the switch bounce was less than 200 microseconds. As for the yellow pushbutton switches, for the DIP switches a 1 msec software delay should be sufficient to overcome switch bounce transitions.
Hardware Debounce
The most basic circuit used to debounce a switch is shown below. It consists of a resistor and a capacitor in series. The resistor and capacitor values must be chosen such that the RC time constant is greater than the bounce time.
Software Debounce
In this course software will be used to debounce the switches. Below is a portion of code that can be used to debounce the yellow pushbutton sw2 attached to porth of the Dragon12 board. It uses polling to wait for the switch to be pressed then implements a 1 ms delay and tests the condition of the switch register, porth, to verify it is still pushed. Porth contains the switch values. If the bit corresponding to the switch is high, the switch is open, if it is low, then the switch is closed. The program then waits for the switch to be released. Once the switch is released it implements another 1ms delay and tests the condition of the switch register to verify that it was a valid release and exits the subroutine. Note that this code simply demonstrates one method we can use to debounce switches. There are many other debounce routines, and each will depend on the specific application at hand.
;***********************************************************************************
;Switch registers
porth EQU $0260
porth_ddr EQU $0262
;Switch masks
sw2on EQU %00001000
;Delay constants
usec EQU $05 ; startx loop = 5 cycles = 200 nsec, 5 loops = 1 usec (microsecond)
msec EQU $1F4 ; 1000 steps *1us/step =1ms delay
;code section
movb #input, porth_ddr ; set Port H to input
loop:
ldd #$0 ; initialize Port D
sw2:
brset porth, #sw2on, loop ; wait for switch 2 to be pressed
ldd #msec ;If pressed wait 1ms for debounce
jsr DELAY ; delay time = (Reg D) * usec
brset porth,#sw2on,loop ;if switch not pressed after delay exit subroutine
loop2:
brclr porth,#sw2on,loop2 ;wait for switch 2 to be released
ldd #msec ;If released wait 1ms for debounce
jsr DELAY
brclr porth,#sw2on,loop2 ;If invalid release, wait for release
rts ; return from subroutine
;***************************************************************************************
;Subroutine used for delay. Total delay time = (Reg D) * usec
DELAY:
pshx
tfr d,y
starty: ldx #usec
startx: dex
nop
bne startx
dey
bne starty
pulx
rts
;*****************************************************************************************
/****************************************************************
*
* UTEP EE3376
* c_demo.c
* 3 Sept 2004
*
* This code will flash LED0 tied to Port B (PB0) every half second
* (half second on, half second off). The code below is for an
* oscillator clock (OSCCLK) running at 4 MHz, as it does on the
* Dragon12 board. The program is based on the RTI interrupt, and
* the interrupt vector table is remapped to $3E00 since we are
* running DBug12.
*
*
****************************************************************/
/****************************************************************
* Include files
****************************************************************/
#include <mc9s12dp256.h> /* standard CodeWarrior file with reg. defs */
/***************************************************************
* Function prototypes
****************************************************************/
__interrupt void RealTimeInterrupt(void);
/****************************************************************
* Local data definitions
****************************************************************/
static volatile unsigned char rtiCnt; /* to count number of RTIs */
void main(void)
{
DDRB = 0x01; /* set portb pin 0 to output */
PORTB = 0x01; /* turn on LED connected to PB0 */
RTICTL = 0x50; /* 1/(4MHz/2^14) = 4.096 ms per RTI */
CRGINT |= 0x80; /* enable RTI interrupt */
rtiCnt = 0; /* RTI counter to 0 */
asm("cli"); /* enable global interrupts */
for (;;) { /* infinite loop */
if (rtiCnt == 122)
{ /* 500 ms passed? */
PORTB = ~PORTB; /* toggle LED on/off */
rtiCnt = 0; /* reset rti counter */
}
}
} /* end main() */
__interrupt void RealTimeInterrupt(void)
{
rtiCnt++; /* increment the counter */
CRGFLG = 0x80; /* clear rti flag */
} /*end RealTimeInterrupt() */
/***** end lab9_main.c ****/