Request for 16f628 design help

Thread Starter

NomadAU

Joined Jul 21, 2012
46
Hi,

I'm looking for some design help with the logic for a fairly straightforward circuit I've built. Basically, I have a stepper motor that raises or lowers a gate and at each extreme there is a microswitch that is set when the gate slides by. In my PIC I have logic for controlling the movement of the stepper motor (clockwise or anticlockwise) and have also enabled RB7-RB4 change interrupt - the 2 switches are wired to RB4 and RB5. In my ISR I detect which pin has changed state and set a corresponding bit in a status register. The latter are checked during the routine that raises or lowers the gate so that the routine knows when to exit its stepper loop.

It all kinda works, except for the odd occasion when it looks like a switch change might be getting missed. I've come to the conclusion that I need to implement switch debounce and that's where I could use some advice.

I understand how debounce logic works, in fact I've implemented it several times in inline code, but in this case, I'm wondering what might be the best way to design my PIC code so that the switch changes are still detected by the PIC interrupts but somehow a 20ms time delay can be introduced to eliminate switch bounce.

I think that what might be causing my problem is that a state change interrupt (due to bounce) might be happening before I have processed the first one in my ISR and then I really don't know what the behaviour of the PIC will be. Anyhow, here's my current ISR routine to help you understand what I've done so far. Also, note I'm also using RB6 as an activation switch, i.e. it causes the gate to lower.


Rich (BB code):
; start of ISR
ISR     CODE    0x0004
        ; save context
        movwf   cs_W                        ;   1 instr
        movf    STATUS,w                    ;   1 instr
        movwf   cs_STATUS                   ;   1 instr

        ; Test why ISR has been driven
processInterrupt
        btfsc   INTCON, RBIF    ; RB pin change
        call    processRBPinChange

        goto    ISR_END                     ; 2 instr


processRBPinChange
        bcf     INTCON, RBIF            ; clear interrupt flag   ; 1 instr

        ; compare PORTB with previous state
        movf    PORTB, w
        xorwf   stateWord, w
        btfsc   STATUS,Z
        return

        ; something has changed, store the bitstrip and figure out what
        movwf   bitStrip
        ; refresh last state
        movf    PORTB, w
        movwf   stateWord

        btfsc   bitStrip, UPPER_SWITCH
        call    processUpperSwitchChange

        btfsc   bitStrip, LOWER_SWITCH
        call    processLowerSwitchChange

        btfsc   bitStrip, BUTTON
        call    processButtonChange

        return          ; not interested in other changes

;-----------------------------------------
processUpperSwitchChange    ; get state of upper switch
        btfsc   PORTB, UPPER_SWITCH
        goto    upperSwitchSet
upperSwitchReset
        bcf     FLAGWORD, UPPER_SWITCH_SET
        bcf     PORTA, LED_PIN
        return
upperSwitchSet
        bsf     FLAGWORD, UPPER_SWITCH_SET
        bsf     PORTA, LED_PIN
        return

;-----------------------------------------
processLowerSwitchChange    ; get state of lower switch
        btfsc   PORTB, LOWER_SWITCH
        goto    lowerSwitchSet
lowerSwitchReset
        bcf     FLAGWORD, LOWER_SWITCH_SET
        bcf     PORTA, LED_PIN
        return
lowerSwitchSet
        bsf     FLAGWORD, LOWER_SWITCH_SET
        bsf     PORTA, LED_PIN
        return

;-----------------------------------------
processButtonChange         ; get state of button
        btfsc   PORTB, BUTTON
        goto    buttonSet
buttonReset
        bcf     FLAGWORD, BUTTON_PRESSED
        return
buttonSet
        bsf     FLAGWORD, BUTTON_PRESSED
        return

ISR_END ; restore context then return
        movf    cs_STATUS,w             ; 1 instr
        movwf   STATUS                  ; 1 instr
        swapf   cs_W,f                  ; 1 instr
        swapf   cs_W,w                  ; 1 instr
        retfie                          ; 2 instr
; end ISR
Thanks for your feedback.
 

JohnInTX

Joined Jun 26, 2012
4,787
RBIF can be a bit tricky. A couple of things jump out here.

You get the interrupt based on a change on the port but then you re-sample the port in your various processing - its not guaranteed to be the same, particularly with bouncing switches. A better way to do it would be to movf PORTB,W then MOVWF PORTBimg once then do all further processing for that interrupt on PORTBimg.

Keep in mind that after the IRQ, PORTB can change due to bouncing bit also legitimate changes. Each time you read PORTB you clear the mismatch condition so you might lose quick changes there.

If you needed to ensure that the port settled down, there are ways to do that but delays inside an interrupt routine is not recommended. If you have 20ms to burn debouncing the port, you can do that by polling instead of interrupt on change.

See what happens when you process a port image first and we can go from there.

Have fun.

EDIT: sorry, didn't see the PIC type in the header.

Basically, I have a stepper motor that raises or lowers a gate and at each extreme there is a microswitch that is set when the gate slides by.
.. and actually, since these are slow switches, I would run debouncers off a timer interrupt then post the results for examination by the main routine..

EDIT EDIT: sorry for the rambling post.. the latter is exactly what I would do. I would also encourage you (strongly) to shadow the output ports. In a noisy environment, bcf, bsf on a midrange PIC's port can be problematic.
 
Last edited:

ErnieM

Joined Apr 24, 2011
8,377
I understand how debounce logic works, in fact I've implemented it several times in inline code, but in this case, I'm wondering what might be the best way to design my PIC code so that the switch changes are still detected by the PIC interrupts but somehow a 20ms time delay can be introduced to eliminate switch bounce.
I do it by making the 20 mS time delay with the ISR and testing the switches.

Using Timer2 it's straightforward to set up a regular "heartbeat" inside the code. I've found 1mS pretty easy to get all the time.

So every 20 ISR's or so (counted by a local static variable) I do a button scan, and only accept a change if I see it for 2 consecutive reads. That works well for manually pressed button, you might need to play with the time for your project.

Note the buttons themselves do not fire interrupts, the timer does, and the buttons are read and tested over and over.

Once I have a new stable pattern I place it into a global variable. I've also tried setting bits when a button is pressed, and another variable where released buttons set bits; the code to set these variables works but I've never applied them ;-)

In my main loops when I need a button I just use a while loop until a button forces an exit... or loop till the button is released.
 

Thread Starter

NomadAU

Joined Jul 21, 2012
46
Many thanks to both JohnInTx and ErnieM for your very helpful ideas and insights. I've made a few important mods to the code based on your input.
1 - the ISR copies PORTB and subsequent tests are done on the copy
2 - the first time a change is detected for a switch, I set a 'in debounce' bit in a flagword to indicate that a debounce period has started and kick off TMR0 for 20ms. When the time interval expires, the bit is reset.
3 - the ISR code that processes the pin changes just returns if the 'in debounce' bit is set.

As an aside, if I didn't already have the interrupt on change ISR logic implemented, given the 'slow' nature of my application (i.e. just managing a stepper motor), I'd most likely try ErnieM's approach and scan the buttons every every 20ms - so much simpler.
 

takao21203

Joined Apr 28, 2012
3,702
What works for me is to sample key bits through a low pass, copy them to one specific flag, and then work a state machine and maintain it's state with a byte variable.

It only takes very few lines C.

The low pass is done via timer interrupt.
 
Top