Sorry to interrupt ... - Page 1
Forum section: electronics / beginners

author: PerranOak
author_rank: Frequent Contributor
timestamp: May 31, 2018, 04:14:42 pm
content: As I understand it, when an interrupt occurs (I’m using a PIC 16F1827) the programme counter (PC) is pushed to the stack and the programme jumps to0004hto start the interrupt code.Programmes that I have seen always seem to use a subroutine ending in “RETFIE” so that the programme runs this then gets back to what it was doing.In my project the programme will be in a loop displaying the current result when an interrupt occurs. I then want that interrupt to jump to executing code part the way down the main loop and carry one, not acting as a subroutine at all - more of a "GOTO".Is this “allowed”? Will it cause unpredictability? What is it that triggers the return of the PC from the stack, is it “RETFIE”?Also, I note in the datasheet that the “GIE: Global Interrupt Enable bit” of INTCON0 is cleared on interrupt. Is this normal/usual?Thank you.

author: mikerj
author_rank: Super Contributor
timestamp: May 31, 2018, 05:04:25 pm
content: The RETFIE instruction re-enables interrupts and pops the last address off the stack and loads it to the program counter. It's quite possible to use a normal "RETURN" at the end of an interrupt, the only difference is that interrupts will not be automatically re-enabled.If you jump to a specific location at the end of your inetrrupt rather than executing a RETFIE/RETURN then you will need to manually "pop" the saved address from the hardware stack by decrementing the STKPTR register. If you don't do this then the stack will quickly overflow. Note this is only possible on later versions of the 14 bit PIC core (such as the 16F1827) that allows access to the stack pointer, early devices had no way of doing this.GIE is disabled when servicing an interrupt to prevent further interrupts occurring (re-entering the ISR) and again quickly filling up the stack. It's possible to re-enable interrupts whilst in the interrupt handler, but you really need to understand the implications of this.

author: langwadt
author_rank: Super Contributor
timestamp: May 31, 2018, 05:14:38 pm
content: Quote from: mikerj on May 31, 2018, 05:04:25 pmif the mainloop just pushed something on the stack before the interrupt, then returning from the interrupt to a different place inmain those will never be popped off the stack and it'll just keep growing

author: mikerj
author_rank: Super Contributor
timestamp: May 31, 2018, 05:19:46 pm
content: Quote from: langwadt on May 31, 2018, 05:14:38 pmYes, that's what I said....

author: langwadt
author_rank: Super Contributor
timestamp: May 31, 2018, 05:41:32 pm
content: Quote from: mikerj on May 31, 2018, 05:19:46 pmyou said pop the saved address, but the main loop could have pushed other things on the stack at the moment the interrupt happens if you return to a different place the code that pops those off again will never happen

author: PA0PBZ
author_rank: Super Contributor
timestamp: May 31, 2018, 05:48:39 pm
content: Quote from: PerranOak on May 31, 2018, 04:14:42 pmGenerally speaking, if you think you need to do this you are doing something wrong, so think it over again and try to find another solutions where you use the interrupt the way it was ment to be. For instance, you could set a flag in the interrupt routine and let the main loop check for this flag and then execute the code you want... there are many ways to solve your problem in a normal way. Maybe post what you are trying to do?

author: PerranOak
author_rank: Frequent Contributor
timestamp: May 31, 2018, 07:00:15 pm
content: mikerj:I see so I don't need to set GIE if I use RETFIE, understood.What happens when the stack overflows? Only I have a very odd result happening: my project displays temperature and every fifth reading (each new reading is triggered by the interrupt) is bogus while the intervening four are fine. Could this be a stack problem? I realise it could be just bad programming!langwadt:As you see from the above I may be experiencing problems.PA0PBZ:Yes, you're right, I was doing it this way out of laziness. I just wondered if it was possible/allowable.

author: langwadt
author_rank: Super Contributor
timestamp: May 31, 2018, 07:29:40 pm
content: Quote from: PerranOak on May 31, 2018, 07:00:15 pmpushing on the stack writes at memory locations not reserved for the stack i.e. possibly other variables etc. so anything can happen

author: Ian.M
author_rank: Super Contributor
timestamp: May 31, 2018, 07:58:30 pm
content: Generally speaking, exiting an interrupt service routine (ISR) by anything other than a normal return from interrupt instruction to resume execution of the main program from the point it was interrupted at, is an *EXTREMELY* bad idea. If you write an ISR that modifies its return address without a dammed good reason, expect to have to justify yourself in a hostile performance review!However in a few special circumstances, it can be useful. e.g if you are bit-banging a protocol or signal in a loop that's so tight you don't have enough instruction cycles slack to test a flag to exit the loop, or in a preemptive RTOS kernal, which swaps stacks to swap threads, then returns to the point in the new thread at which it was previously interrupted and suspended.As the enhanced midrange PICs have dedicated stack memory, they are ill suited to premptive RTOSes as the stack would have to be copied off to RAM one return address at a time and reloaded with its previously saved contents from the new thread. On a processor with a stack in general purpose RAM, the equivalent operation is to save the current stack pointer to the thread table, and load it with the one from the next thread which effectively swaps the stack leaving the previous thread's stack inactive till its needed again and making the new thread's stack current. Of course there are some other critical details required to successfully resume the next thread. e.g preserving critical flags and registers on a per thread basisThe remaining use - to break out of a loop that's so tight you cant afford a single extra instruction to check an exit flag can be handled semi-cleanly.Immediately after the loop, duplicate it except the final BRA or GOTO instruction that returns to the start of the loop must be replaced by a NOP. In the ISR, check if the TOSH:TOSL register pair is in the address range of the first copy of the loop. If so, add the number of instructions in the loop to TOSH:TOSL, so the RETFIE returns to the equivalent next instruction in the second copy of the loop, so the loop processing is completed cleanly and the loop finally exits as the second copy doesn't have a BRA or GOTO back to the start.@langwadt: All 8 bit PICs have dedicated storage for the stack, It is physically impossible for it to overflow into general purpose RAM.

author: langwadt
author_rank: Super Contributor
timestamp: May 31, 2018, 08:49:33 pm
content: Quote from: Ian.M on May 31, 2018, 07:58:30 pmnever had the misfortune to use a picso the stack pointer will probably wrap and still cause all kinds of havoc

author: jpanhalt
author_rank: Super Contributor
timestamp: May 31, 2018, 09:20:57 pm
content: I am late to the party, sorry. But since you are using an ehannced mid-range PIC, you can "pop" the last address just by decrementing STKPTR (Bank 31).Here's an alternative I call MIB (Men in Black, if you remember the movie). It erases all memory of the interrupt and lets you startafresh.Code: (asm)MIB ;erases interrupt return functionalitymovlb 31clrf STKPTRmovlb 2bcf LATC,2 ;ensures LED is off when entering Command Modecall ClrScrn ;call from any bank, returns |B0 |B0btfss INTCON,IOCIFbra Event ;not switch, must be Eventmovlb 7 ; |B7btfsc IOCBF,SW1 ;test for SW1 |B7bra DoSW1 ; |B7bra DoSW2 ; |B7Only the first 3 instructions are relevant. The rest are to identify the source of the interrupt. Of course, if you want to retain where you were umpteen steps before the interrupt, change the clrf to decrf...John

author: MarkF
author_rank: Super Contributor
timestamp: June 01, 2018, 01:44:29 am
content: Quote from: PerranOak on May 31, 2018, 07:00:15 pmI agree with PAOPBZ on this one.Yes, it's doable. But, HIGHLY DISCOURAGED! You are just asking for stack corruption and other mysterious problems. I remember an occasion in my early years where one word was left on the stack by mistake. It took 3 engineers over a week to find and correct. When you start having these questions about a design, STOP and look at the big picture. There will be a better way.Just use the interrupt routine to read a new value and store it in a global variable. Then, allow the main loop to display the value during its periodic update. If you are NOT updating the entire display at a periodic rate, then set a flag in the interrupt routine to trigger an update in the main loop.

author: Brumby
author_rank: Unknown
timestamp: June 01, 2018, 03:03:06 am
content: I cringed at the idea of playing with interrupt returns. Like any common computer architecture, the fundamental execution is a linear one and every logical branch is - quite literally - a "go to". So, yes, you can do it - but it's like lighting up a cigarette in a fireworks factory IMO.Free use of the "go to", however, is not conducive to well structured, robust programs which is why the modular style with calls has been a fundamental mainstay in the programming sphere for a very long time. Even in my first job writing application software in IBM assembler, this structural approach was the norm.Programming with interrupts is, perhaps, the quintessential example of this structured process. Everything is built around the idea.Most specific of the concerns I have is the state of execution when the interrupt occurs. Whatever you were doing at the time stops while the interrupt is serviced and if you were part way through doing something, not allowing the return to that processing could leave you in an unknown condition. Where this becomes a true minefield is if you have another interrupt fire off while you are still faffing around with the first.Personally, I would avoid such programming like the plague - but if you really,really,reallyfeel you need to, then think long and hard, tread very carefully and test the crap out of it.Once you're happy with the testing, do it again ... and maybe get someone who doesn't like you to test it. If you don't, the universe will find an edge case that will shoot you down in flames.

author: jpanhalt
author_rank: Super Contributor
timestamp: June 01, 2018, 03:51:07 am
content: 1) If you are just going to set a flag in the interrupt and then poll that flag as recommended by some, why have an interrupt to begin with? Just poll the xyzIF.2) I have other routines, e.g., string print routines (macros), that use the stack to calculate a return with no interrupt. Among other things, the da directive is used to pack two, 7-bit ascii into a single program memory location (Microchip reference manual), "• da "abcdef" will put 30E2 31E4 32E6 into program memory." For routines that have a lot of "dialog" that can save hundreds of lines of code. Try to do that concisely without using the stack.For example:Code: (asm)StrPrint macro labelcall PutTextda label,0endm3) Fears that that the world will end if you use the stack as a tool are greatly exaggerated. They all sound a little like the warnings against flying ("if god had wanted man to fly he would have given him wings..."). Sure there are sincere believers in that, but the person writing the code is ultimately responsible for every line, not just the stack. To put that another way, if Microchip hadn't wanted users to touch the stack, why did it give them the tools to do it? Obviously, there is demand for the ability to push and pop the stack by programmers, and Microchip simply complied to compete.

author: Brumby
author_rank: Unknown
timestamp: June 01, 2018, 06:41:10 am
content: Quote from: jpanhalt on June 01, 2018, 03:51:07 amReally? I can come up with a couple of reasons without even trying.

author: wraper
author_rank: Unknown
timestamp: June 01, 2018, 06:50:43 am
content: Quote from: jpanhalt on June 01, 2018, 03:51:07 amSo how often do you need to poll to not loose the event?If the event is so slow that you don't need to use interrupt, then sure. You shouldn't use interrupts right and left when there is no need for that.

author: Brumby
author_rank: Unknown
timestamp: June 01, 2018, 06:52:08 am
content: That was one ^ ^ ^ ^Another is if you needed to know the exact time the event occurred. Then there's handling multiple events that might occur during a single poll cycle.

author: MarkF
author_rank: Super Contributor
timestamp: June 01, 2018, 07:21:55 am
content: Quote from: wraper on June 01, 2018, 06:50:43 amOne example that I recently did was processing a rotary encoder. The interrupt routine read the encoder at a high fixed rate and saved the total number of clicks until the Display Loop was able to process them. The Display Loop run at a much slower rate and didn't need to see each event. Just the total number of clicks. The total number clicks was used in sequencing through menu items, incrementing/decrementing values, etc. In this case, a 1000Hz system timer created interrupt events to perform varies tasks (read the encoder and update some hardware at a fixed rate). The Main Loop processed the encoder inputs and updated a display in the background.Whether you poll for an event or use interrupts depends on the task(s) and rates required by your design.One thing to remember here is that you want the interrupt service routine to be fast (i.e. get in and get out as quickly as possible) so it is ready for the next event. Especially if you are looking for multiple events. Do the heavy processing in your main loop where things are NOT time critical.

author: mikerj
author_rank: Super Contributor
timestamp: June 01, 2018, 08:23:09 am
content: Quote from: langwadt on May 31, 2018, 05:41:32 pmThe 14 bit PIC has a hardware stack purely for return addresses, it's not used to save or pass data between functions.

author: mikerj
author_rank: Super Contributor
timestamp: June 01, 2018, 08:25:03 am
content: Quote from: Brumby on June 01, 2018, 06:52:08 amIf the ISR is ONLY setting a flag, then you might as well poll the interrupt flag. It's only when the ISR is doing other stuff that polling may not be useful.

author: langwadt
author_rank: Super Contributor
timestamp: June 01, 2018, 08:34:04 am
content: Quote from: mikerj on June 01, 2018, 08:23:09 amso the main loop could call functions so there are addresses on the stack that will never get popped if you just straight to a different place in main?

author: NivagSwerdna
author_rank: Super Contributor
timestamp: June 01, 2018, 08:47:08 am
content: Quote from: PerranOak on May 31, 2018, 04:14:42 pmRead the manualQuoteSo why is there a RETFIE instruction? Because it atomically does two things... it pops the PC and sets the GIE bit. You are going to find that operation crucial for reliable operation. But that doesn't preclude sharing code paths between interrupt code and other code just that you need to keep track when you return and use the correct mechanism to get back.Experience tends to result in keeping interrupt routines as small and simple as possible as they are hard to debug and you need to keep them short otherwise you will be late servicing the next one (the event will just set the flag and you will pick it up when you REFIE and potentially lead to starvation of the main thread of control).Anything is possible... but there is a reason for the existance of the RETFIE instruction.Have fun!

author: jpanhalt
author_rank: Super Contributor
timestamp: June 01, 2018, 09:15:51 am
content: Quote from: Brumby on June 01, 2018, 06:52:08 amHere's what PAOPBZ said and others agreed:QuoteLet me summarize your comments: If you don't know what "your" program does, you shouldn't be writing it.I agree. But, nothing you have said relates factually with using access to the stack responsibly.

author: jpanhalt
author_rank: Super Contributor
timestamp: June 01, 2018, 09:21:16 am
content: Quote from: MarkF on June 01, 2018, 07:21:55 amOnly since you quote my statement will I respond. I have no issue with doing what you did with the encoder, nor did I mean you should always poll in IF and not use an interrupt. I was responding specifically to PAOPBZ's comment.

author: NivagSwerdna
author_rank: Super Contributor
timestamp: June 01, 2018, 09:23:53 am
content: ... on reflection... I recently disassembled a very old bit of 8039 code...The technique used there was the IRQ would service an outside request, calculate and then and just push an address (after removing the original stack entry) of one of a number of routines to run after the ISR completed. The 8039 would run that routine and then finally enter a hard loop, waiting to be interrupted and scheduled again. The technique was pre-emptive... i.e. a new IRQ would schedule the new work with the old work (if it hadn't already reached the hard idle loop) being superseded.Not an unusual technique

author: jpanhalt
author_rank: Super Contributor
timestamp: June 01, 2018, 10:02:04 am
content: Quote from: NivagSwerdna on June 01, 2018, 09:23:53 amExactly, and now the 8-bit, enhanced mid-range PIC's can do that too. It has been available with 18F's and others for a long time.

author: wraper
author_rank: Unknown
timestamp: June 01, 2018, 10:13:14 am
content: Quote from: PerranOak on May 31, 2018, 04:14:42 pmYou need to be wary that interrupt can occur in any random point of main loop. And you need to be wary of data corruption which may happen when you make goto from a random point of a program. I would consider it relatively safe only if you completely discard all of the data you had before such interrupt.Not to say that you will have data transfer corruption if MCU was talking with some peripheral devices. You may just hang the bus and will need to reset it.

author: PerranOak
author_rank: Frequent Contributor
timestamp: June 01, 2018, 12:40:03 pm
content: Wow! Apologies for causing such trouble!I have only programmed in high level languages on PCs before so the stack was a mystery to me. While I was learning PIC stuff the significance of theRETFIEcommand was not made clear ... I know, ignorance of the law is no excuse.I now understand that I must not be lazy and must code properly even (especially?) on practice projects!

author: NivagSwerdna
author_rank: Super Contributor
timestamp: June 01, 2018, 12:51:41 pm
content: You didn't cause .any troubleBe creative don't necessarily follow any of these recommendations!Have fun

author: Brumby
author_rank: Unknown
timestamp: June 01, 2018, 12:58:07 pm
content: Quote from: PerranOak on June 01, 2018, 12:40:03 pmDon't apologise ... you didn't cause any trouble. You simply asked a reasonable question.QuoteThis is one of the reasons why caution has been strongly represented. You can certainly do whatever you like, but some of the trouble you can get yourself into playing with the stack can be pretty hairy. Understanding and discipline are useful companions.

author: PerranOak
author_rank: Frequent Contributor
timestamp: June 01, 2018, 02:15:27 pm
content: Cheers both.What a noble but gassy bunch we are!

author: dnwheeler
author_rank: Regular Contributor
timestamp: June 01, 2018, 06:45:19 pm
content: Probably the biggest issue to consider is that you don't really know what code was being interrupted. For example, the code may have been in the middle of interacting with some external device, or reconfiguring several hardware registers, or clocking out a series of data values, etc. If an interrupt occurs and you don't return from it, some hardware could be left in an "in-between" state. Lights could be left on, devices could get stuck on/off, allocated memory may not be freed, etc.

author: mikerj
author_rank: Super Contributor
timestamp: June 04, 2018, 09:00:36 am
content: Quote from: langwadt on June 01, 2018, 08:34:04 amExactly. This is why you would need to manually pop the last address off the stack if you were going to jump out of the ISR, rather than returning.

author: langwadt
author_rank: Super Contributor
timestamp: June 04, 2018, 02:05:04 pm
content: Quote from: mikerj on June 04, 2018, 09:00:36 amthat would get rid of the address pushed by entering the interrupt, what if the main loop just pushed an address on the stack to call another function?