Compressing the PIC debounce example code
The code from the PIC switch debounce example is shown below. One odd thing is that the LastStableState variable uses 1 to indicate the last stable state was “switch down”, which reads as a low (zero) on the input port. I added some pseudo-C comments in my efforts to make sure I understand the code.
;; LSS = 1 ;; actually, this means the switch was stable at 0
movlw 1
movwf LastStableState ; Assume the Switch is down. [not up as original comment]
;; counter = 0
clrf Counter
MainLoop:
btfsc LastStableState,0
goto LookingForUp
;; note redundant CLRW here: could have put before btfsc LSS,0
;; LSS is 0
;; switch was stable with 1: if current_state != 1 counter++ else counter = 0
LookingForDown:
clrw ; assume it's not, so clear
btfss PORTA,3 ; wait for switch to go low
incf Counter,w ; if it's low, bump the counter
movwf Counter ; store either the 0 or incremented value
goto EndDebounce
;; lss is 1:
;; switch was stable with 0: if current_state != 0 counter++ else counter = 0
LookingForUp:
clrw ; assume it's not, so clear
btfsc PORTA,3 ; wait for switch to go low
incf Counter,w
movwf Counter
;; note redundancy here: incf Counter,w puts result in W
;; movwf Counter puts W --> counter
;; movf Counter,w brings same value back into W
EndDebounce:
movf Counter,w ; have we seen 10 in a row?
xorlw 5
btfss STATUS,Z
goto Delay1mS
;; if counter = 5
;; lss = !lss
;; counter = 0
;; if "active" take action
;;
comf LastStableState,f ; after 10 straight, reverse the direction
clrf Counter
btfss LastStableState,0 ; Was it a key-down press?
goto Delay1mS ; no: take no action
[button-down action here]
Delay1ms:
[delay loop here]
goto MainLoop
This version takes 23 instructions plus the length of the “down action” routine, plus the length of the delay loop.
I came up with a slightly more compact version; in the process, to keep my sanity, I reversed the LastStableState convention, so LSS = 1 meant the switch was last stable in the up position.
;; LSS = 1
movlw 1
movwf LastStableState ; Assume the Switch is up.
;; counter = 0
clrf Counter
MainLoop:
clrw ; need w=0 for counter calculation
btfsc LastStableState,0
goto LookingForDown
LookingForUp:
;; lss is 0: if current_state != 0 w=counter++ else counter = 0
;; i.e. if porta,3 is CLEAR skip the increment
btfsc PORTA,3
incf Counter,w ; if it's SET, w = counter+1
goto EndDebounce
LookingForDown:
;; lss is 1: if current_state != 1 w=counter++ else counter = 0
;; i.e., if porta,3 is SET skip the increment
btfss PORTA,3
incf Counter,w ; if switch low, w = counter+1
EndDebounce:
movwf Counter ; store either 0 or incremented value
;; W==counter, either 0 or incremented
xorlw 5
btfss STATUS,Z
goto Delay1mS
;; zero flag was set: means W==5
;; if counter = 5
;; lss = !lss
;; counter = 0
;; if "active" take action
;;
switch_happened:
comf LastStableState,f ; after 5 straight, reverse the direction
clrf Counter
btfsc LastStableState,0 ; Was it a key-down press?
goto Delay1mS ; no: take no action
[down button action here]
Delay1ms:
[delay routine here]
goto MainLoop
This code comes to 20 instructions plus the “down action” and “delay” routine lengths. I still have a vague feeling that I could save another instruction by changing the LSS convention back, and using CLRF LastStableState to initialize it. Perhaps there is an opportunity to simplify the “clrw, conditionally increment counter into W, store W back into counter, test W” by using a different “count up/down to zero” convention, and a inc/decfsz counter. However, that would expand the clrf counter line to movlw initialcount + movwf Couter.
I’m still getting used to the rather limited conditional branching when reading—I keep thinking the branches go far away, past an “else clause”, instead of simply missing one instruction that changes the “then” into the “else”.
RSS Feed