I developed this code for a vertical debounce counter for the PIC micro (PIC16F57). The code is called every 512 microseconds. It takes 5 bits from PORTB and compares it to a last known debounced state. If an active input has been stable for 64 calls it is accepted as changed. Though it is sufficiently good for the current application, I am interested in possible improvements to speed up overall execution time. I am not interested in prematurely leaving the loop in case there is nothing to update.

                        cblock              0x08
gpcounter:1                                         ; general purpose counter

    ; Button debounce
                            ; A button state is considered stable if it has not changed in 64 polls each 512 usec
        debounce            ; Read input state. Only bits 0-4 are inputs
                            movf            PORTB, W
                            andlw           0x1f
                            ; Any input bits that have changed with respect to the last known state
                            ; will be set to 1's in delta and toggle.
                            ; delta remains fixed, toggle will act as a carry while looping
                            ; Because toggle bits will be reset only, toggle will always be a subset of delta.
                            xorwf           debstate, W
                            movwf           delta
                            movwf           toggle
                            ; Load first counter row in index register
                            movlw           vcounter
                            movwf           FSR
                            ; Counter has 6 rows 
                            movlw           0x06
                            movwf           gpcounter
        debounceloop        ; Reset row for all bits which  have not changed states
                            movf            delta, W
                            andwf           INDF, F
                            ; Flip row bits according to toggle mask.
                            ; Because the 1's in toggle are a subset of those in delta, 
                            ; any reset counter bits can never be toggled back to 1
                            movf            toggle, W
                            xorwf           INDF, F
                            ; If any bits were toggled from 1 to 0, retain carry bit, else discard it
                            comf            INDF, W
                            andwf           toggle, F
                            ; We're done with this row
                            incf            FSR, F
                            decfsz          gpcounter, F
                            goto            debounceloop
                            ; toggle will now contain 1's for any counters
                            ; that have overflown, so it can be used as xor mask
                            ; to update the last known debounced state
                            movf            toggle, W
                            xorwf           debstate, F
                            retlw           0x00
  • \$\begingroup\$ Is this for school? The question is important, because under nearly all circumstances it's a bad idea to use this device. \$\endgroup\$
    – Reinderien
    Commented Oct 15, 2020 at 14:43
  • \$\begingroup\$ @Rienderien This is not schoool, I am not even a student. Sometimes we just need to work with what is available. \$\endgroup\$
    – Bart
    Commented Oct 16, 2020 at 5:12
  • \$\begingroup\$ I don't blame you, if this is hobby work. My advice will not change because we strive for production quality. \$\endgroup\$
    – Reinderien
    Commented Oct 16, 2020 at 12:14
  • \$\begingroup\$ @Rienderien School work, hobby project. You are making a lot of assumptions. I am not interested in discussions about what I should or should not use. I am looking for possible improvements on an existing algorithm, \$\endgroup\$
    – Bart
    Commented Oct 17, 2020 at 10:22
  • 3
    \$\begingroup\$ Good news! There's an easy way to preclude this assumption: tell us more about the project. \$\endgroup\$
    – Reinderien
    Commented Oct 17, 2020 at 12:31

1 Answer 1


Device selection

I guess the reason you're on the PIC16F57 is that you're a hobbyist, and this device was the one on hand. I can nearly guarantee that, for a production application, you can find a device from Microchip that is newer, cheaper, smaller and more capable.

One change that most new PICs include is selectable Schmitt-trigger input mode, which will help with your debounce or even eliminate the need for software debounce, depending on your specifications. Your current device has no Schmitt trigger selection register (chapter 6), and only a limited number of special-purpose pins that have Schmitt trigger inputs (chapter 11.3).

Another feature you can take advantage of is software-enabled weak pullup, which - given that you're monitoring a button - is likely to simplify your circuit, which you have not shown. This is commonly available in new PICs.

Yet another feature you could use is integrated pin change detection, commonly available in new PICs - the IOCI module. This can potentially reduce your power consumption, since you wouldn't have to run a polling loop - you could set up a timer, set up IOCI, and then if the timer fires before an IOCI the button event is valid. If an IOCI fires before the timer expires, reset the timer. Some devices can actually go to sleep while this happens and wake up for either interrupt.


overflown = overflowed


I don't recognize the flavour of assembly you're using, perhaps because I'm used to the assembler bundled with contemporary releases of MPLABX. But any self-respecting assembler should be able to:

  • Accept 6 instead of 0x06, the latter having no need for hexadecimal representation. The same with 0x00.
  • Include a device-specific header file with symbols for PORTB so that the magic number 0x1F can be replaced by what you're actually doing, which is
andlw PORTA0_MASK |
      PORTA1_MASK |
      PORTA2_MASK |
      PORTA3_MASK |

All (official) PIC environments offer such symbols in their header files, and using them will make your project more maintainable.

General approach

It seems deeply strange to me that you're debouncing five buttons all at once. I would expect, for the current device you have,

  • Poll for one button press - you can check all pins of PORTA at once using the Z flag
  • When you hear a button press, assign a mask variable that monitors only that button pin
  • Poll until either the time expires (you can use TIMER0 and its prescaler to get 32ms) or the button is released
  • Poll on that pin only, using your mask, until 32 ms are up

For most applications you'll only care to process one button at a time.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.