Need help with PIC Assembly for PIC16F72 Interrupt service routine

jpanhalt

Joined Jan 18, 2008
11,087
1) If you flowchart both functions, you might get some ideas.
2) Why can't you used the interrupt delay for your debounce period too?
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
1) If you flowchart both functions, you might get some ideas.
2) Why can't you used the interrupt delay for your debounce period too?
1)which 2 functions
2)i just feel that it might miss the button press. It does have a good delay of 30ms. But I guess it should work, will try and see what happens.
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
With regards to using the interrupt delay. I will have a problem because I don't have my own delay it won't wait for the delay it would execute the next instruction after change frequency, which is check if button has been released.

CALL CHANGEFREQUENCY
CALL DELAY20MS ;wait for signal to settle at 0
WAITRELEASE BTFSS PORTA,0 ;check if button depressed
GOTO WAITRELEASE ; reduce time instead of looping whole thing wait here till button released
CALL DELAY20MS ;add delay to allow signal to settle back to 0

So button is pressed, frequency changed, it won't wait for signal to settle as i am using the interrupt delay, so it will execute the check, which might be one of the bounce effects within the 10ms and then mess everything up until after 30ms is up it would have executed quite a bit of code before interrupt kicks in, unless there is a command, which says wait for interrupt then continue after.
 

jpanhalt

Joined Jan 18, 2008
11,087
I do not have a clue what you are trying to say in words. For example, what frequency is changed? That is determined by your crystal/ceramic resonator. What do you mean "I don't have my own delay"? Didn't you write it?
Flowchart it so we can see what you are trying to do.
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
Ok let me re-phrase it.

My main program now does nothing and waits every 30ms to get an interrupt.
If i was to add the switch function, which will change the frequency of the toggling of the light when I press a button.
So I can write my own delay to wait 30ms and then check if signal has settled then wait for button to be released and then wait 30ms delay. so I can write my own 30ms delay function. But the question was why can't I use the time it takes for the interrupt to trigger as the delay for the debouncer method instead of my own 30ms delay.

I have attached the flow chart.

I can't use the interrupt timer to overflow as the delay, but rather I have to use my own delay and then when the interrupt occurs before my delay starts the total delay time will be 50-60ms, which I guess will do.
 

Attachments

jpanhalt

Joined Jan 18, 2008
11,087
Nicely done flow chart. Generally speaking, one would detect the button being pressed, wait for the signal to settle, and then effect the change. The reason for that is because the cycle time of the program may be faster than the bounce time. For example. let's say you are entering control words as binary bits, and one button is a 1 and another is 0. That data entry routine is likely fast , and pressing the 1 may end up with multiple 1's being entered as the bounces are detected.

In your program, the loop time is roughly 30 ms for the interrupt. Bouncing may not affect the change you make in delay variables, e.g., your presets for TMR0 and INTDELAY.

My thought was to use the interrupt delay to also time your debounce. For example, if the switch read* is outside of the interrupt, e.g., you poll the switch in the PROGRAM/NOP/PROGRAM loop, you could read INTDELAY and keep reading it until it decrements by 2 (minimum delay a little more than 30 ms to about 60 ms depending on when the button was pressed relative to TMR0 rolling over).

You could also test the switch on each entry into the interrupt and require that two reads be the same to effect a change, which would give you a built in delay of at least 30 ms, so the chance of a double read is remote. Of course, anything you do in the interrupt will affect its period slightly by a few μs. I would prefer this approach.

Are you planning to have release of the switch affect flash rate too? That is, press the button and flash rate changes. Will it require another press of the button to change the rate back or will that happen on release?

How are you planning to change flash rate? If possible, I would recommend only changing INTDELAY and not TMR0 preset. You will need to have two states and switch between those states. That is, in your steps: movlw .33 ; movwf INTDELAY , you cannot actually change the .33 on the fly. You would have to have 2 states: 1) movlw .33 and 2) movlw <something else> and switch between the two based on the status of your switch.**

*By switch "read," you can read the switch, but if it is bouncing, you may get a wrong reading. I often read the port interrupt flags. Those change, but do not bounce back. You must clear them in your software. So if you see a change in RBIF, you can be sure there is a change and visa versa. If you do not see a change when reading the port, it may just be bouncing. Newer chips allow you to actually read an IF flag for each pin, and you can set which or both edges are detected.

** For example,
Code:
     movlw     .33          ;state 0
     btfsc     <some flag bit>
     movlw     .20          ;state 1
     movwf     INTDELAY
 
Last edited:

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
Nicely done flow chart. Generally speaking, one would detect the button being pressed, wait for the signal to settle, and then effect the change. The reason for that is because the cycle time of the program may be faster than the bounce time. For example. let's say you are entering control words as binary bits, and one button is a 1 and another is 0. That data entry routine is likely fast , and pressing the 1 may end up with multiple 1's being entered as the bounces are detected.

In your program, the loop time is roughly 30 ms for the interrupt. Bouncing may not affect the change you make in delay variables, e.g., your presets for TMR0 and INTDELAY.

My thought was to use the interrupt delay to also time your debounce. For example, if the switch read* is outside of the interrupt, e.g., you poll the switch in the PROGRAM/NOP/PROGRAM loop, you could read INTDELAY and keep reading it until it decrements by 2 (minimum delay a little more than 30 ms to about 60 ms depending on when the button was pressed relative to TMR0 rolling over).

You could also test the switch on each entry into the interrupt and require that two reads be the same to effect a change, which would give you a built in delay of at least 30 ms, so the chance of a double read is remote. Of course, anything you do in the interrupt will affect its period slightly by a few μs. I would prefer this approach.

Are you planning to have release of the switch affect flash rate too? That is, press the button and flash rate changes. Will it require another press of the button to change the rate back or will that happen on release?

How are you planning to change flash rate? If possible, I would recommend only changing INTDELAY and not TMR0 preset. You will need to have two states and switch between those states. That is, in your steps: movlw .33 ; movwf INTDELAY , you cannot actually change the .33 on the fly. You would have to have 2 states: 1) movlw .33 and 2) movlw <something else> and switch between the two based on the status of your switch.**

*By switch "read," you can read the switch, but if it is bouncing, you may get a wrong reading. I often read the port interrupt flags. Those change, but do not bounce back. You must clear them in your software. So if you see a change in RBIF, you can be sure there is a change and visa versa. If you do not see a change when reading the port, it may just be bouncing. Newer chips allow you to actually read an IF flag for each pin, and you can set which or both edges are detected.

** For example,
Code:
     movlw     .33          ;state 0
     btfsc     <some flag bit>
     movlw     .20          ;state 1
     movwf     INTDELAY
Thanks.
Interesting thought of testing switch in the interrupt. Testing it at the start of the interrupt if button is pressed. if it is there is already the 30ms time it takes for the next interrupt to take place. But the interrupt routine itself takes 20ms and there is an approx 10ms rollover time before interrupt kicks in. So I guess 20ms is ample delay and that a flag variable should be set to indicate button was detected and delay has taken place. so outside of the interrupt we can check for the flag and execute the change without delay. then when interrupt happens again the switch would be unpressed and flag cleared. which i would check it is cleared outside of the interrupt and keep looping in that area till flag is cleared. switches are in portA so I can't check the RBIF flag.

A button press would change the frequency and the release does nothing only the pressing. Changing INTDELAY was my plan.
if you remember from the code I wrote prior to the bounce checking thing was discussed. Also one button sets the signal to one frequency only it won't change back if pressed again. I have 8 buttons so 8 different frequencies from just pressing the button. The only state that matters is that it was pressed.

MOVLW .33 ;state 0
MOVWF INTDELAY

I understand your code, which is pressed one frequency and released original frequency.

Thanks
 

atferrari

Joined Jan 6, 2004
4,770
It is always wise to use the destination when needed and not rely on the default. Standardize on using 0,1 or W,F, respectively. I prefer W,F.
Using 1 & 0 is bad practice. W & F are explicit instead.

Mnemonics are the way to go. The same with variables with obfuscated names that even their creator cannot recognize three days after. Think of revisiting your code in five months time...
 

atferrari

Joined Jan 6, 2004
4,770
Ok let me re-phrase it.

My main program now does nothing and waits every 30ms to get an interrupt.
In your case or any other kind of program the main has to do its own tasks but never be "waiting for interrupts". These (interrupts) will occurr as they are programmed, interrupting actually, the main. Pretty much what happens in real life in a dynamic environment where a wife, a boss, a policeman, can have the power to interrupt whatever you are doing. The difference is that here, these interrupts will keep happening.

I did not check the actual details of the thread but if the main is doing nothing and all depends of you ISR, something is wrong.
 

jpanhalt

Joined Jan 18, 2008
11,087
Using 1 & 0 is bad practice. W & F are explicit instead.

Mnemonics are the way to go. The same with variables with obfuscated names that even their creator cannot recognize three days after. Think of revisiting your code in five months time...
The mothership seems to prefer 0 and 1:
1576500239063.png
So, I keep an open mind. Whichever you use, they do not seem to be case sensitive. As mentioned, I use w and f (usually lower case) as they are easier to read.
 

jpanhalt

Joined Jan 18, 2008
11,087
In your case or any other kind of program the main has to do its own tasks but never be "waiting for interrupts". These (interrupts) will occurr as they are programmed, interrupting actually, the main. Pretty much what happens in real life in a dynamic environment where a wife, a boss, a policeman, can have the power to interrupt whatever you are doing. The difference is that here, these interrupts will keep happening.

I did not check the actual details of the thread but if the main is doing nothing and all depends of you ISR, something is wrong.
The purpose is to flash an LED. That's all. Of course, an interrupt is not needed, but the lesson is apparently about how to use an interrupt. Thus, I don't see anything wrong with following directions. What else would you suggest doing in the "Main?"
 

atferrari

Joined Jan 6, 2004
4,770
The purpose is to flash an LED. That's all. Of course, an interrupt is not needed, but the lesson is apparently about how to use an interrupt. Thus, I don't see anything wrong with following directions. What else would you suggest doing in the "Main?"
Hola @jpanhalt

Caveat to the OP: let us make clear that I am not replying other than John's question.

It is obvious that just to blink a LED, an on_line delay would be more than enough (and simpler), repeating forever inside the main loop.

To show how an interrupt works with all this, I would do something additional (inside the ISR) while blinking that proverbial LED in the main loop.

I propose this (which, BTW, is something I've implemented when I learnt to use interrupts and kept doing for every micro):

You enter the ISR and:

A) Save the environment.

B) Set PIN_DEBUG (you can see it whith a scope going high).

C) Do whatever the interrupt is intended for.

D) Clear PIN_DEBUG (you can see it whith a scope going low).

E) Retrieve the environment.

You leave the ISR


Ejemplo John.gif
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
That'
Hola @jpanhalt

Caveat to the OP: let us make clear that I am not replying other than John's question.

It is obvious that just to blink a LED, an on_line delay would be more than enough (and simpler), repeating forever inside the main loop.

To show how an interrupt works with all this, I would do something additional (inside the ISR) while blinking that proverbial LED in the main loop.

I propose this (which, BTW, is something I've implemented when I learnt to use interrupts and kept doing for every micro):

You enter the ISR and:

A) Save the environment.

B) Set PIN_DEBUG (you can see it whith a scope going high).

C) Do whatever the interrupt is intended for.

D) Clear PIN_DEBUG (you can see it whith a scope going low).

E) Retrieve the environment.

You leave the ISR


View attachment 194642
That's what my main intention was. Make the main program do something and then let the interrupt stop the program and do it's own thing that it creates a square wave where the main program is setting the program to high and the interrupt setting it to low. but it didn't work and now I have reached a point where the main program will do nothing and the interrupt will toggle the light right now my latest work is checking for button presses in the interrupt and do the action in main.

But the program I have now I guess I can change it to work that way that instead of xoring the port I can just set the output port or clear it.
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
First I was thinking how would I do this for several buttons, maybe several isbuttonpressed variables and functions. Then I was thinking why not just use one variable and use PortA instead of PortA,0 or PortA,7. or shall I just do 8 BTFSS statements to check each button and and use one variable and check each bit if set or not
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
My code so far for checking only 1 button. for 8 i will do the same but 8 checks and several goto statements.

Code:
    #include <p16f72.inc>
     __config (_HS_OSC & _WDT_OFF & _PWRTE_OFF & _CP_OFF)

; /////////////////////////////////////////////////////////////////////////

    CBLOCK    h'25'
        W_SAVE       
        STATUS_SAVE   
        INTDELAY
        ISPRESSED
    ENDC   

;******************************************************************

    list    P=16F72           

    ORG 000H
    GOTO MAIN

    ORG 004H
    GOTO INTFUNCT

; /////////////////////////////////////////////////////////////////////////

;             Initialize all variables and port pins here!

; /////////////////////////////////////////////////////////////////////////
MAIN:
    BSF    STATUS, RP0
    MOVLW    B'00000000'
    MOVWF    TRISB   
    MOVLW    B'11111111'
    MOVWF    TRISA
    MOVLW    B'10000110'
    MOVWF    OPTION_REG
    BCF    STATUS, RP0
    CLRF    PORTB
    MOVLW    .178
    MOVWF    TMR0
    BCF    INTCON, TMR0IF
    BSF    INTCON, TMR0IE
    BSF    INTCON, GIE
    BSF    PORTB, 0
    MOVLW    .33
    MOVWF    INTDELAY

PROGRAM:
    BTFSS    ISPRESSED,0
    GOTO PROGRAM
    CALL FREQUENCY1
TEMPSTOP:
    BTFSC    ISPRESSED,0
    GOTO    TEMPSTOP
    GOTO    PROGRAM           
    
INTFUNCT:
    MOVWF W_SAVE    ; save W
    SWAPF STATUS, W    ; save STATUS
    MOVWF STATUS_SAVE
    
    BCF    STATUS, RP0   

    BTFSC    INTCON, TMR0IF       
    GOTO ENDIR

    BTFSC    PORTA,0
    GOTO    SETBIT1
    GOTO    CLEARBIT1

SETBIT1:
    MOVLW    1
    MOVWF    ISPRESSED,0
    GOTO CONTINUE

CLEARBIT1:
    MOVLW    0
    MOVWF    ISPRESSED,0
    GOTO CONTINUE
            
CONTINUE:
    BCF    INTCON, TMR0IF

    DECFSZ    INTDELAY,f
    GOTO ENDIR    ;once it reaches 0, then do the main part of the interrupt and then reset intdelay
    
    MOVLW    .1
    XORWF    PORTB,f
    
    MOVLW    .178
    MOVWF    TMR0
    MOVLW    .33
    MOVWF    INTDELAY
    GOTO ENDIR

ENDIR: 
    SWAPF STATUS_SAVE, W
    MOVWF STATUS
    SWAPF W_SAVE, F
    SWAPF W_SAVE, W 
    RETFIE

FREQUENCY1:
    MOVLW    .32
    MOVWF    INTDELAY

    END
 

atferrari

Joined Jan 6, 2004
4,770
right now my latest work is checking for button presses in the interrupt and do the action in main. /QUOTE]

Sorry, that is nonsense.

I dare to suggest you doing exactly what I mentioned in my post; nothing simpler. Instead of my "blah blah blah" insert some 20 or 30 NOPs and see on the scope how the pulse gets wider / narrow.

And then, not before, you could try something a little bit more complex where you do something in the main loop, based on a flag set or cleared inside the ISR.

Baby steps will have you running faster much sooner that you could suspect.
 

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
I have a fair understanding of what your plan is.

Enter interrupt
set output to low where square wave is generated.
turn light on showing interrupt has occurred
main interrupt action delay for 1 seconds - i don't like adding lots of NOPs
turn light off
exit interrupt

main program
square wave
loop

But I did try this last time and interrupt was never executed. maybe because i disabled the gie interrupt.

so there is flashing light of main and flashing light of interrupt.

but when I do switch presses do the whole checking thing in the main where each function adds a certain number of NOPs.

or am i understanding you wrong
 

jpanhalt

Joined Jan 18, 2008
11,087
Thanks.
Interesting thought of testing switch in the interrupt. Testing it at the start of the interrupt if button is pressed. if it is there is already the 30ms time it takes for the next interrupt to take place. But the interrupt routine itself takes 20ms and there is an approx 10ms rollover time before interrupt kicks in.
The interrupt code takes 18 us, not ms. The code stops at that external loop while TMR0 counts up.
So I guess 20ms is ample delay and that a flag variable should be set to indicate button was detected and delay has taken place. so outside of the interrupt we can check for the flag and execute the change without delay. then when interrupt happens again the switch would be unpressed and flag cleared. which i would check it is cleared outside of the interrupt and keep looping in that area till flag is cleared. switches are in portA so I can't check the RBIF flag.

A button press would change the frequency and the release does nothing only the pressing.
20 ms to 30 ms is probably long enough, some people use just 10 ms.

With your clarification of the toggle vs. action on release, I would probably change from IOC on RB4..7 to the INT pin (RB0) where you can choose the edge. Say you set it to a rising edge.

I am still learning Google's Draw.io so my flowchart may not be clear. My criteria were: 1) There are two states; one flashes the LED faster than the other and duty cycle for each is about 50%; 2) It can remain in either state for an indeterminate time until the button is pushed again; 3) It never changes state upon release of the button; and 4) Only one button is involved and it will be held a minimum of two rollovers of TMR0 (faster presses (>30 ms) may work, but not consistently). Here goes on my attempt to toggle the state with each press of a button switch on the INT pin (RB0) set to rising edge and without enabling its interrupt.

BTW, It looks like the code you just posted will reset to the slower state as soon as the button is released or sooner as INTDELAY is refreshed to the old value in the interrupt..

EDIT: Ignore step 3 under Cycle bit set decision.
 

Attachments

Last edited:

Thread Starter

seifullaah73

Joined Dec 13, 2019
51
The interrupt code takes 18 us, not ms. The code stops at that external loop while TMR0 counts up.

20 ms to 30 ms is probably long enough, some people use just 10 ms.

With your clarification of the toggle vs. action on release, I would probably change from IOC on RB4..7 to the INT pin (RB0) where you can choose the edge. Say you set it to a rising edge.

I am still learning Google's Draw.io so my flowchart may not be clear. My criteria were: 1) There are two states; one flashes the LED faster than the other and duty cycle for each is about 50%; 2) It can remain in either state for an indeterminate time until the button is pushed again; 3) It never changes state upon release of the button; and 4) Only one button is involved and it will be held a minimum of two rollovers of TMR0 (faster presses (>30 ms) may work, but not consistently). Here goes on my attempt to toggle the state with each press of a button switch on the INT pin (RB0) set to rising edge and without enabling its interrupt.

BTW, It looks like the code you just posted will reset to the slower state as soon as the button is released or sooner as INTDELAY is refreshed to the old value in the interrupt..

EDIT: Ignore step 3 under Cycle bit set decision.
Port B has lights on them and the switches are on PortA. so I won't be able to use the switch as interrupts. Teacher had configured it that way.

So I have to rely on clock interrupt. my first idea when i first began was using the switch but pic16f72 only accepts interrupt from portB so he suggested use a timer as he didn't want me changing the switch over to portB. So using RB0 is out of the question.

I have 8 buttons each button sets the frequency to a specific amount. The code I posted was just one button and then if that works I can add the 7.

BTW, It looks like the code you just posted will reset to the slower state as soon as the button is released or sooner as INTDELAY is refreshed to the old value in the interrupt..
Oh yeah I forgot about that. I will have to use another temp variable, which I can decrement and then after the countdown reaches 0 copy the original value of intdelay to the temp variable and decrement count delay so something like

MOVF INTDELAY,W
MOVWF TEMPINTDELAY

DECFSZ TEMPINTDELAY,F

MOVF INTDELAY,W
MOVWF TEMPINTDELAY

How would you stop the variable changing to it's original state when button is released.

BTW I used microsoft powerpoint to draw the flowchart using shapes, much easier.
 
Top