debounces input from a switch and jumps to a routine, optionally performing an autorepeat function if the switch is held down.
Mechanical contacts bounce, producing several milliseconds of on/off noise before they settle into a new position. This is a problem for controllers, because it can be hard to distinguish between a valid switch press and contact bounce.
Many applications can get by without debouncing a switch input, if they read the switch infrequently, or if the switch triggers a fairly long event. But switches that are used to scroll through a range of values or toggle an action definitely require debouncing. To use Button, your program must set the bit state equal to the state of the button when activated, set or clear rpt_sw to turn autorepeat on or off, put the desired pin and port into variables of the same names, and call btn_init. Then include call Button in the main program loop. Button will debounce the switch and jump to the routine named theAction when the switch is activated.
Unlike PBASIC's Button, this version does not permit changes to the delay and repeat values while the program is running. If you wish, you may use variables instead of constants to change these on the fly.
This Button routine also does not directly support multiple buttons, but can be modified to do so. The bit flags for a second button could be stored in the upper four bits of flags. A separate 16-bit db variable, accessed by a pointer, would also be necessary.
To see Button in operation, either run the program with the PSIM simulator, or connect the circuit below to an erasable PIC or PIC emulator, such as the Parallax downloader. Assemble and run BUTTON.SRC. When you press the switch, the LED will blink. Hold the switch down and, after a brief delay, the LED will blink rapidly.
; ; *************************************************************************** ; *** Bubble Software Parallax to PIC Source Converter. Copyright 1999. *** ; *** http://www.bubblesoftonline.com email: sales@picnpoke.com *** ; *************************************************************************** ; ; BUTTON pin, port, state, delay, repeat_rate ; This routine debounces input from a pushbutton and jumps to an ; "action" routine. The pin (0-7), port (0-2 for RA through RC), ; action state of the input (0,1) are all specified at run time. ; The delay and repeat rate are constants. ; Device data and reset vector P = pic16c55 #include <16c55.inc> ; processor assembler definitions _CONFIG _xt_osc & _wdt_off & _protect_off reset start org 8 dbH Res d'1' ; MSB of 16-bit debounce counter. dbL Res d'1' ; LSB of 16-bit debounce counter. pin Res d'1' ; Pin no. (0-7). port Res d'1' ; Port no. (0-2). flags Res d'1' ; Bit flags for use by Button: state equ flags.3 ; Do action when button is in this state. rpt_sw equ flags.2 ; Repeat switch: 0=off; 1=on. old equ flags.1 ; Previous state of button. new equ flags.0 ; Current state of button. ; You can tune the constants assigned to delay, repeat, and upval below by ; substituting different numbers for 150, 10, and 255. The "^0FFh" inverts ; the values to simplify the program code by allowing loops to count up ; instead of down. If ^0FFh is removed, delays will be smaller when the ; numbers are larger. delay equ d'150'^0FFh ; Delay before autrepeat. repeat equ d'10' ^0FFh ; Delay between repeats. upval equ d'255'^0FFh ; Debounce for button-up. org 0 ; Table to convert numbers 0-7 into bytes with a 1 in the corresponding ; bit position. For example, 3 converts to 00001000b. Pinz ADDWF pcl RETLW d'1' RETLW d'2' RETLW d'4' RETLW d'8' RETLW d'16' RETLW d'32' RETLW d'64' RETLW d'128' start MOVLW d'0' ; Make RB output to drive LEDs. TRIS 6h MOVLW d'15' ; Make RA input for button. TRIS 5h CLRF 6h ; Turn off LEDs. CLRF flags ; Clear the bit flags. CLRF dbL ; Clear the debounce counter. CLRF 0xdb BSF flags,d'3' ; Set up for active-high (1=pushed) BSF flags,d'2' ; Turn on autorepeat. BSF flags,d'1' ; Initialize old state of button. MOVLW d'2' ; Read button on pin 2 MOVWF pin MOVLW d'0' ; of port RA. MOVWF port CALL btn_init ; Initialize button variables. ; This illustrates how button is used--the routine is called from a main ; loop that may or may not contain other instructions. The more instructions ; inside the loop, the smaller the button delay and repeat values should be. start_loop CALL button GOTO start_loop ; Since Button is called frequently from within a main program loop, it makes ; sense to move code that only needs to be executed once into a separate ; initialization routine. btn_init MOVF pin,w ; Look up pin value in table and CALL Pinz ; store it for use by Button. MOVWF pin MOVLW 5h ; Add offset to point to I/O ports. ADDWF port RETLW 0h Button MOVF port,w ; Point to the port. MOVWF fsr MOVF indirect,w ; Move port bits into w. BTFSS flags,d'3' ; IF state=0 THEN w = NOT(port bits). COMF indirect,w ANDWF pin,w ; IF w AND pin THEN new = 1 BTFSS status,z ; ELSE new = 0 (0 means button "on") BCF flags,d'0' BTFSC status,z BSF flags,d'0' MOVLW d'3' ; Move 0000011b to w to mask bits ANDWF flags,w ; other than old and new. ADDWF pcl ; Jump based on state of bits old, new. GOTO Button_held ; old = 0, new = 0 - button held down. GOTO Button_release ; old = 0, new = 1 - button just released. GOTO Button_push ; old = 1, new = 0 - button just pushed. GOTO Button_up ; old = 1, new = 1 - button left up. Button_push BCF flags,d'1' ; Copy new button state to old. MOVF dbL ; If dbL is not 0, then the last button-up BTFSS status,z ; period was short--probably a bounce-- RETLW 0h ; so don't do theAction. Else, load delay MOVLW delay ; and jump to theAction. MOVWF 0xdb GOTO theAction Button_held INCFSZ dbL ; Increment 16-bit db variable until it RETLW 0h ; reaches 0. When it does, reload it with INCFSZ 0xdb ; the repeat delay , and, if the repeat RETLW 0h ; switch is on, do theAction. If repeat MOVLW repeat ; switch is off, skip theAction and return. MOVWF 0xdb BTFSC flags,d'2' GOTO theAction RETLW 0h Button_release MOVLW upval ; Button released: put upval into MOVWF dbL BSF flags,d'1' ; dbL and copy new state to old. This RETLW 0h ; sets up the button-up debounce in :up. Button_up MOVF dbL,1 ; Increment dbL until it equals 0. BTFSS status,z ; Since :push won't do theAction unless INCF dBL ; dbL=0, this debounces button release. RETLW 0h ; This is the code activated by Button. Note that it ends with ret in order to get ; back to the code that called Button in the first place. theAction MOVLW d'255' XORWF 6h RETLW 0h
; BUTTON pin, port, state, delay, repeat_rate ; This routine debounces input from a pushbutton and jumps to an ; "action" routine. The pin (0-7), port (0-2 for RA through RC), ; action state of the input (0,1) are all specified at run time. ; The delay and repeat rate are constants. org 8 dbH ds 1 ; MSB of 16-bit debounce counter. dbL ds 1 ; LSB of 16-bit debounce counter. pin ds 1 ; Pin no. (0-7). port ds 1 ; Port no. (0-2). flags ds 1 ; Bit flags for use by Button: state = flags.3 ; Do action when button is in this state. rpt_sw = flags.2 ; Repeat switch: 0=off; 1=on. old = flags.1 ; Previous state of button. new = flags.0 ; Current state of button. ; You can tune the constants assigned to delay, repeat, and upval below by ; substituting different numbers for 150, 10, and 255. The "^0FFh" inverts ; the values to simplify the program code by allowing loops to count up ; instead of down. If ^0FFh is removed, delays will be smaller when the ; numbers are larger. delay = 150 ^0FFh ; Delay before autrepeat. repeat = 10 ^0FFh ; Delay between repeats. upval = 255 ^0FFh ; Debounce for button-up. ; Device data and reset vector device pic16c55,xt_osc,wdt_off,protect_off reset start org 0 ; Table to convert numbers 0-7 into bytes with a 1 in the corresponding ; bit position. For example, 3 converts to 00001000b. Pinz jmp pc+w retw 1,2,4,8,16,32,64,128 start mov !rb, #0 ; Make RB output to drive LEDs. mov !ra, #15 ; Make RA input for button. clr rb ; Turn off LEDs. clr flags ; Clear the bit flags. clr dbL ; Clear the debounce counter. clr dbH setb state ; Set up for active-high (1=pushed) setb rpt_sw ; Turn on autorepeat. setb old ; Initialize old state of button. mov pin,#2 ; Read button on pin 2 mov port,#0 ; of port RA. call btn_init ; Initialize button variables. ; This illustrates how button is used--the routine is called from a main ; loop that may or may not contain other instructions. The more instructions ; inside the loop, the smaller the button delay and repeat values should be. :loop call button goto :loop ; Since Button is called frequently from within a main program loop, it makes ; sense to move code that only needs to be executed once into a separate ; initialization routine. btn_init mov w,pin ; Look up pin value in table and call Pinz ; store it for use by Button. mov pin,w ADD port,#ra ; Add offset to point to I/O ports. ret Button mov fsr,port ; Point to the port. mov w,indirect ; Move port bits into w. sb state ; IF state=0 THEN w = NOT(port bits). mov w,/indirect AND w,pin ; IF w AND pin THEN new = 1 movb new,z ; ELSE new = 0 (0 means button "on") mov w,#3 ; Move 0000011b to w to mask bits AND w,flags ; other than old and new. jmp pc+w ; Jump based on state of bits old, new. jmp :held ; old = 0, new = 0 - button held down. jmp :release ; old = 0, new = 1 - button just released. jmp :push ; old = 1, new = 0 - button just pushed. jmp :up ; old = 1, new = 1 - button left up. :push clrb old ; Copy new button state to old. test dbL ; If dbL is not 0, then the last button-up sz ; period was short--probably a bounce-- ret ; so don't do theAction. Else, load delay mov dbH,#delay ; and jump to theAction. jmp theAction :held incsz dbL ; Increment 16-bit db variable until it ret ; reaches 0. When it does, reload it with incsz dbH ; the repeat delay , and, if the repeat ret ; switch is on, do theAction. If repeat mov dbH,#repeat ; switch is off, skip theAction and return. snb rpt_sw jmp theAction ret :release mov dbL,#upval ; Button released: put upval into setb old ; dbL and copy new state to old. This ret ; sets up the button-up debounce in :up. :up test dbL ; Increment dbL until it equals 0. sz ; Since :push won't do theAction unless inc dBL ; dbL=0, this debounces button release. ret ; This is the code activated by Button. Note that it ends with ret in order to get ; back to the code that called Button in the first place. theAction XOR rb,#255 ret
See also: