Electronic – Counter in assembly, using interrupt to prevent multiple counts with single push


I am completely new to assembly, and I must develop a counter using PIC16F628A, a push button, and a display. Additionally it there will be an external oscillator (555).

I made some progress on this, but I think I need some help from you people.
At first I did a delay based on decrements in order to be able to watch numbers on the display.

My problem now is that once I press the button, I need it to count only one number, independently how much time I keep it pressed. Something like, if it changes state up, it will increment 1. I believe this must be done with interrupts, I guess.

Now, what is the best solution to my problem? External interrupt, by state interrupt? Any other thing?

Best Answer

Since you cannot use a timer (gathered from comments you've made), you need a suitable delay routine to provide a specific period of time. I like the period of \$8\:\textrm{ms}\$, from prior experience. But you can use any period you feel is appropriate. Assuming that your processor is using the factory calibrated \$4\:\textrm{MHz}\$ rate, the instruction cycle will be \$1\:\mu\textrm{s}\$ and it will take \$8,000\$ cycles to make up an \$8\:\textrm{ms}\$ period.

The delay code should probably be made into a subroutine, to avoid having to replicate it over and over.

            MOVWF   DLO
            MOVLW   0x07
            MOVWF   DHI
            DECFSZ  DLO, F
            GOTO    $+2
            DECFSZ  DHI, F
            GOTO    $-3
            GOTO    $+1

The total time occupied by the above routine can be computed as:


where \$1 \le D_{LO}\le 256\$ and \$1 \le D_{HI}\le 256\$, with 0 interpreted as 256. The CALL and RETURN instructions take up 2 cycles each and the above code takes all of that into account. Calling it should take exactly \$8,000\$ cycles and, at \$4\:\textrm{MHz}\$ this means \$8\:\textrm{ms}\$.

You will have to create those two variables, \$D_{LO}\$ and \$D_{HI}\$ somewhere. That can be done like this, I think:


There are, of course, other ways. And you can add an absolute address to the CBLOCK line if you want to place the block somewhere specific.

Now that you have a delay routine, you can proceed to the next step. You need two new routines. One that repeatedly delays until the button becomes active and one that repeatedly delays until the button becomes inactive. Debouncing is included here:

            BTFSC   PORTx, PINy
            GOTO    ACTIVE
            CALL    DELAY8MS
            BTFSC   PORTx, PINy
            GOTO    ACTIVE

            BTFSS   PORTx, PINy
            GOTO    INACTIVE
            CALL    DELAY8MS
            BTFSS   PORTx, PINy
            GOTO    INACTIVE

I don't know your port or pin number, so I just put in "dummy" values there. You need to replace them, properly. The above two routines assume that 0 is active and 1 is inactive.

Now you can write your main code:

MAIN        ; <code to reset your counter value>
            GOTO    LOOP_NXT
            ; <code to increment your counter value>
LOOP_NXT    ; <code to display your counter value>
            CALL    INACTIVE
            GOTO    LOOP

The above code resets your counter value to whatever you want to start at and then it jumps into the loop where it displays the value and waits for the button to become inactive. The effect here is that if you start up your code with the button pressed (it should not be, but what if it is?), then the code will still reset the counter and display it... but it will wait until you release it before continuing. So you have to let up on the switch.

Then, once that has happened, the basic loop just waits for a debounced active state of the switch. When it sees that, it increments the counter immediately (on the press, not on the release) but then waits for the button to be released before continuing, again.

That's about it. You still need to write appropriate code for the counter and display. But that gets the idea across for the rest.