Button

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.

Demonstrating Button.

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:

Code: