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.


; 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