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.
; 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