;Subroutine to detect state changes and debounce push buttons connected ;to a port of an 8052 type microprocessor. Operates with up to 7 buttons, ;which must all be on the same port. ;This subroutine may be freely distributed and used. If it proves useful, ;an email to dave_fletcher@bigfoot.com would be appreciated. ;The subroutine is provided "as is" and no liability is accepted by the ;author for any losses incurred due to its incorporation in any application. ;David Fletcher, April 1999 ;Example of how to use:- ; PUSH #0 ;Dummy byte to reserve a space on the stack ; LCALL DEBOUNCE ; POP DEBOUNCED_BUTTONS ;The programmer needs to provide definitions for the values of:- ; ; BUTTON_MASK This is an 8 bit mask for reading the port ; to which the push buttons are connected. For ; each button, the corresponding bit must be ; set to 1. Every other bit must be a 0. ; ; BUTTON_SAME This should have a single bit set to 1, with ; all of the other 7 bits 0. It does not matter ; which bit is the 1, so long as it is not the ; same bit as any of the 1s in BUTTON_MASK. The ; debounce subroutine uses the BUTTON_SAME value ; to tell the calling routine that the state of ; the push buttons has not changed. ; ;The programmer may also need to change the number of the port on line 80 ;The subroutine was designed to be called from the main loop of a real ;time application. The author's original application is a radio time ;code (Rugby MSF) synchronised digital clock. ; ;It is called during every pass through the main loop, and returns the ;BUTTON_SAME value if none of the buttons has been pressed or released. ;If a button has been pressed or released, a debounce timer is started ;which decrements each time the subroutine is called with the buttons ;in the same state as the last time. Until the timer reaches zero, the ;subroutine keeps returning BUTTON_SAME at each call. ; ;When the debounce counter reaches zero, the subroutine returns the new ;state of the push buttons. DSEG DB_COUNT: DS 1 ;Debounce timer. Counts how many times ;the push buttons have been in the same ;state DB_PREV_STATE: DS 1 ;Previous state of the buttons CSEG DEBOUNCE: PUSH ACC ;Save all registers used by the routine MOV A, R0 PUSH ACC MOV A, R1 PUSH ACC PUSH B MOV R0, SP ;Set up a pointer to the return parameter on the stack DEC R0 ;which after the register saves is now six levels down DEC R0 DEC R0 DEC R0 DEC R0 DEC R0 ;R0 now points to the return parameter MOV A, P3 ;Sample the button state CPL A ;Buttons are active low ANL A, #BUTTON_MASK ;and delete non-button bits MOV R1, A ;Save a copy XRL A, DB_PREV_STATE ;Test to see if the buttons have ;changed since last time JZ DB_NO_CHANGE MOV DB_PREV_STATE, R1 ;If they have changed, MOV DB_COUNT, #100 ;restart the debounce count MOV R1, #BUTTON_SAME ;and tell the calling routine ;nothing has changed.... yet SJMP DB_DONE DB_NO_CHANGE: MOV A, DB_COUNT ;Test the debounce count JNZ DB_WAIT ;If it is not zero, go wait for ;it to count down MOV R1, #BUTTON_SAME ;If it is already zero, the calling ;routine had the button data last time SJMP DB_DONE DB_WAIT: MOV R1, #BUTTON_SAME ;Load this in case the count does not ;expire this time DJNZ DB_COUNT, DB_DONE ;Decrement and test the debounce count MOV A, DB_PREV_STATE ;If it has expired, get the current ;button state, MOV R1, A ;and pass it back to the calling ;routine DB_DONE: ;Put the return value in the stack MOV A, R1 MOV @R0, A POP B ;Restore the machine to the way it was found POP ACC MOV R1, A POP ACC MOV R0, A POP ACC RET