Exit interupt routine with goto?

geezer88

Senior Member
I'd like to exit an interrupt routine by using goto instead of the return. To keep the compiler happy, I'd still put the return at the end. This would allow going to a known position in the calling part of the code. What would happen? The calling routine is essentially a loop that never ends, and the interrupt occurs when a switch is momentarily pressed. The press can happen anytime in the calling routine, and I'd like to skip the remainder of the loop and start at the beginning. I haven't actually coded anything yet. I'm just trying to sort out the structure in my mind.

I've spent an hour or so searching through a number of posts, and from what I can tell this won't work, but I'm not sure I've understood previous discussions.

thanks,
tom
 

srnet

Senior Member
What would happen?
Never tried it, but I would not expect it to work either, each gosub (the interrupt call) needs to have a matching return which is actually executed.

What I would expect to happen is as the return at the end of the interrupt is never being executed, the firmware will eventually crash as its runs out of stack space where the return addresses are stored.
 

Goeytex

Senior Member
Also, the interrupt will not be re-enabled if the return is never executed.

You could do your own "polled interrupt" that does not require a gosub/return. Below is an example

Code:
MAIN:

DO 
  
     let b0 = 1 : if pinC.1 = 1 then _interrupt 
     let b0 = 2 : if pinC.1 = 1 then _interrupt
     let b0 = 3 : if pinC.1 = 1 then _interrupt
     
     ENTRY_POINT:
     let b0 = 4 : if pinC.1 = 1 then _interrupt
     let b0 = 5 : if pinC.1 = 1 then _interrupt
     let b0 = 6 : if pinC.1 = 1 then _interrupt
     let b0 = 7 : if pinc.1 = 1 then _interrupt
           
LOOP 

_interrupt:

   sertxd ("SW was pressed",cr,lf)
   goto ENTRY_POINT
 
Last edited:

westaust55

Moderator
Can you consider setting (or incrementing) a flag in a bit or byte variable when you are about to exit the Interrupt: routine via a RETURN command.
Then within your main program you can check if the flag is set (check at several different points if the code is large) and jump to the required label with a
IF... Flag = set THEN label
At that label the first thing to do is clear or decrement (depending on whether there could be multiple needs)
 

MartinM57

Moderator
"Standard and simple" design (flame-suit on) is, as above, just using interrupts to set flags to communicate to the main loop...
...then design the main loop to copy the flag(s) at the top and use those copy/ies to determine what to do in the main loop
...and a slightly more complex way is to retain "thisTimeRoundLoop" and "lastTimeRoundLoop" versions of the interrupt flags to allow you to detect whether a switch has just been pushed, is still pushed, or has just been released i.e.

thisTimeRoundLoop = TRUE and lastTimeRoundLoop = FALSE ; switched just pressed
thisTimeRoundLoop = TRUE and lastTimeRoundLoop = TRUE ; switched still pressed
thisTimeRoundLoop = FALSE and lastTimeRoundLoop = TRUE ; switched just released

In that way, even if the flags are changed by the interrupt part way through the loop, the loop doesn't know about it but everything is nice and simple - but you have to be sure/happy that the overall system behaviour is acceptable with that design.

Usually main loop are fast enough or the system behaviour doesn't matter if say the second half of the loop still continues if the switch is pushed exactly half way through. If that is not acceptable, you have to sprinkle IF tests inside the loop, as per WA55's method, which is a bit messy, but will work.
 

geezer88

Senior Member
Mpep; A reset would require starting from the begining, so I'd have to wait for initialization stuff to complete, but that may be the best solution. I have to ponder on that a bit more.

Srnet; That is what I thought from reading the various posts.

Goeytex; Doing my own polling may be the best compromise between useability and responsiveness.

Westaust55; That is pretty close to what I intended to do. The main loop will have perhaps 30 lines, including a count that will take one second to execute. This means that most of the time the button press will occur during that count. If I do my flag test right after the count, it would allow skipping the rest of the main loop.

Martin; All that would happen in the interupt routine I envision would be to sort out if the press was long or short, then set some bits in a flag byte that the main loop would use to change it's behavior.

All, thanks for your suggestions. I appreciate your willingness to discuss this without a sample of code and schematic diagram. 8^) One of my pet peaves is devices that don't respond quickly to user inputs. Unfortunately the one second count makes this tough. I even thought about breaking the count and flag tests into four parts, of one quarter second each and then summing the counts. This would get faster response for sure, but as Martin pointed out it would be messy. At this point I'll probably just do it as planned and see how it feels.

If there was a way to drop the stack and reset the interrupt system variables, that might have been nice, but your thoughts have help reinforce what I was expecting to have to do.

Oh yeah. The project is to utilize an air velocity measuring device that has been in my junk bin so long that I don't even remember where I got it. The original device used a capacitance sensor to detect the passing blades for counting. I only have the beautiful little mechanism part of the original instrument. I'm using fiber optic light guides to direct a light beam to and from each side of the rotor to detect the rotations. The impetus to get started on this after so many years is the acquisition of a neat little opto sensor made by Banner that uses light pipes. I've got it all breadboarded and working fine. Now I'm just getting the programming rounded out for easy use and addition of features such as changing units, doing running averages, and calculating volume flow rate associated with a user input flow area. It looks like the display from a very old QualCom cell phone will be the output device, and a pushbutton enabled rotary encoder knob the user input device.

Thanks again for your help. This forum is a great resource for experimenters. In my case, I only get around to a microprocessor project every year or two, so I never get to be a whiz at it.

tom
 

bryanl

Member
Also, the interrupt will not be re-enabled if the return is never executed.
If I read the manual right, with PICAXE, re-enabling happens only when setintflags or setint (depending upon what you want to invoke the interrupt) is executed. This is not like the underlying MCU where a return from the interrupt routine re-enables the interrupt. In PICAXE, the interrupt routine appears to be more like a standard subroutine called under special circumstances by the underlying interpreter and, like any subroutine, you want any call to be matched by a RETURN as the depth of subroutine calls is rather limited.

As westaust55 indicates, proper practice in an interrupt routine is to inspect machine state, accomplish indicated critical operations including setting flags and values to communicate with the normal operating code, and exit.

The challenge appears to be in the desire to abort a process on a key press. This is an opportunity to think about good programming practice. One thing you don't want to do is to cede control of a process arbitrarily. That could lead to an unpredictable machine state. This is why interrupt routines need care so they can do what they have to do without disturbing what was going on before it was called. Then, when it returns, the original process can proceed in a predictable manner.

This is where the event loop comes in. A decision loop spends its time either polling for an event or sleeping to be awoken via an event driven interrupt. When it discovers an event by either method, a decision is made to 'do something' which gets done before returning to the loop. If timing is critical, then the event triggered routines need special care.

To abort a process on an event with good programming practice is as described above. Test for the event before undertaking the task and use that test to decide where to go.

anyway, sorry for the preaching. If my understanding of PICAXE interrupt handling is off, please let me know.
 

hippy

Technical Support
Staff member
For fastest responsiveness of an 'abort and start again' button you perhaps can't beat a button connected to the physical reset pin of a 28X2 or 40X2.

A 'push to cut power' button can also work for those without reset pins.

There is unfortunately no way to break out of a COUNT command until it completes other than by means of one of those tricks.

There was some mention of determining if the button push were of long or short duration; it might be worth specifying exactly what the program has to do and how it is to be used as that may elicit some suggestions for the best solution.
 

Goeytex

Senior Member
The main loop will have perhaps 30 lines, including a count that will take one second to execute. This means that most of the time the button press will occur during that count.
@geezer88

I assume that the COUNT is to determine the velocity of the fan/rotor. So to get a somewhat accurate reading counting the pulses for one second is necessary.

It is also possible determine the velocity using pulsin with a single read by measuring the width of the pulse. So instead of taking 1 second to get an accurate velocity reading, it only takes a few ms (depending upon the speed). There is a potential drawback though. Pulsin will timeout and return a value of zero at extremely low rpm.

Without knowing the max / min speed of the rotor and the sensor mark / space times it is impossible to know if pulsin would be practical in your application. But for reference, I have used a Picaxe with pulsin to control the ignition system on a two cylinder engine up to 9,000 rpm. The velocity reading, is used to advance / retard the ignition timing.
 

Morganl

Senior Member
For a non-program-locking method you can set up timer3 to count pulses, then loop around waiting for a specific time to pass.
Measuring time could be by counting servo timer rollovers (20 ms ticks) or checking the time variable - first loop until next second happens, start counting, stop when another second pass.
 

geezer88

Senior Member
Well, I've got my project working great, so here's how I handled the button pushing. I need to count for 500 milliseconds to get sufficient data. I distinguish long and short presses to branch the program various ways. Because of the delay caused by count, short presses would not register if the press and release all occurred inside the count period. This makes for a very unsatisfactory user experience: Repeated pressing may be required or a longer press that may be hard to distinguish from the real long press. So this led to a quest for a way to make the program more responsive.

My first effort was to split the count into two 250 millisecond counts with a test in between and after the counts. Then the two counts are added together. This reduced the delay by half and worked pretty well.

Then I thought of the hardware SR latch. This works even better. I initialize the latch to use the button for setting and the output to go to an unused output pin. Now, no matter how fast the button is pushed, the latch sets. Then, when the program gets to one of the tests, it won't matter if the button has already been released. The RS latch output is used instead of the button input for the test. When I'm done attending to the button stuff I reset the latch for the next use.

This method works great. A quick press and you get the expected result every time. It may be delayed by the program execution, but you can count on it happening.

So, I hope this may help someone else for their application. Of course, this would work on other inputs besides buttons, that you don't want to miss while the program is doing something else.

tom
 

fernando_g

Senior Member
So, you used a 4013 or something similar?

I'm also a fan of using "helper" external logic, if that simplifies or improves my software.
But after all, I was weaned in 74XX and 40XX logic, therefore sharper software individuals may not see the need for these external components.
 

geezer88

Senior Member
No external stuff required, just use the onboard SR latch in most of the new picaxes.

First setup the latch:

srlatch %10001000, %10000000 'setup for SRI pinB.0 to set latch and ouput to pinC.3 specifically for the 18M2 (I don't know what pins on other picaxes)
srreset 'ensure latch is reset (experimenting I found that sometimes the latch would set by itself on startup)

Then pol the C.3 output, when the button is pressed:

if outpinC.3 > 0 then gosub Buttrupt 'test for button press (sorry for the not so politically correct subroutine name, but not sorry enough to change it)

Then do what ever you want in the subroutine. In my situation, I discriminate between a long press and a short one. The beauty of the latch is that even a very short press of the button will get noticed.

j=0
do
pause 10
if PuB = 0 then goto Quickpress 'this section ends with a return after doing some stuff
j = j + 1

loop until j > 200 'about 2 second wait to get slowpress

goto Slowpress 'this section ends with a return, after doing different stuff
 
Top