Microcontroller ISRs - Page 1
Forum section: electronics / beginners

author: ice595
author_rank: Regular Contributor
timestamp: October 16, 2016, 05:47:58 pm
content: So I've heard that an interrupt service routine should be quick and not call another functions.Im quite familiar with the PIC24H and PIC32 family of micros as I've worked with them for 2 semesters at school.Lets say for example I want to update a LCD display every second, I would need to setup a timer interrupt to fire every second.During the interrupt service routine, should I call a function to update the LCD?As long as the interrupt flag bit is cleared before calling the function would allow other interrupts to fire right?If not, what other options do I have?

author: Towz
author_rank: Contributor
timestamp: October 16, 2016, 06:10:57 pm
content: It would but it isn't good practice since if there are other interrupt sources it might mess up the lcd update (a fairly long process unless driving it directly). It seems better to do the lcd update via the main program and using the isr just to enable it. But with pic24/32 you have the option to use low and high priority interrupts if you really want to update the lcd with an isr.

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: October 16, 2016, 06:13:09 pm
content: You only use interrupts for things that need attention fast.1 sec display update does not come under that category.

author: ataradov
author_rank: Super Contributor
timestamp: October 16, 2016, 06:25:20 pm
content: Quote from: ice595 on October 16, 2016, 05:47:58 pmSet a flag in the ISR, check that flag in the main program. This way your display update routine can be interrupted by other things and your system will remain responsive.

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: October 16, 2016, 06:34:13 pm
content: Unless you can set up a timer to set an interrupt flag every second, in which case you don't need an ISR at all - just test the flag in your main loop.

author: ataradov
author_rank: Super Contributor
timestamp: October 16, 2016, 06:37:24 pm
content: Quote from: mikeselectricstuff on October 16, 2016, 06:34:13 pmYes, for things that happen rarely, you can just poll the hardware flag.I was thinking of a more complex case, where you may want to run the hardware timer a bit faster, and derive all software timers from that.

author: ice595
author_rank: Regular Contributor
timestamp: October 16, 2016, 07:36:56 pm
content: but does clearing the hardware interrupt flag allow other interrupts of equal or lower priority to fire?if not, then does it matter if the interrupt flag is cleared upon entering the ISR or cleared right before exiting the ISR?void timerISR(){clear timer interrupt flag here?ISR stuff hereor clear timer interrupt flag here?}

author: ataradov
author_rank: Super Contributor
timestamp: October 16, 2016, 07:41:56 pm
content: Quote from: ice595 on October 16, 2016, 07:36:56 pmThere is no ISR stuff. Simplified code:Code:volatile bool timer_flag = false;void timerISR(){timer_flag = true;clear timer interrupt flag here}int main(void){// init everythingwhile (1){if (timer_flag){// do display suff heretimer_flag = false;}// all other stuff}}Or, as Mike suggested for a simple timer:Code:int main(void){// Init everything// Configure timer, but don't enable timer interruptwhile (1){if (interrupt flag is set in the timer register){// do display suff here// clear interrupt flag}// all other stuff}}

author: ice595
author_rank: Regular Contributor
timestamp: October 16, 2016, 07:53:54 pm
content: Quote from: ataradov on October 16, 2016, 07:41:56 pmyeah but isn't polling bad though? (like constantly asking "are we there yet?" during a long car ride)what I ideally want is for the timer to update the LCD every second, but still allow interrupts to fire during the LCD update process. I guess I could set other interrupts to higher priority

author: ataradov
author_rank: Super Contributor
timestamp: October 16, 2016, 07:56:41 pm
content: Quote from: ice595 on October 16, 2016, 07:53:54 pmThat depends. Are you doing anything important while you wait? Or are you just sitting in the same loop? For most practical cases polling works just fine.Quote from: ice595 on October 16, 2016, 07:53:54 pmYou will run into all sorts of problems doing this, but see for yourself.

author: Kleinstein
author_rank: Super Contributor
timestamp: October 16, 2016, 08:28:55 pm
content: It really depends on the context. If there is nothing critical going one on can very well do a lot of things in the ISR. In the extreme case the main program is only sending the µC back into sleep mode and everything else is done in an ISR. For some µCs function calls inside an ISR cause extra overhead, e.g for saving a few extra registers. So in time critical cases one should avoid it, but if not time- (or stack-) critical a few 10 cycles are not that bad.Only the so often heard part of only setting a flag in the ISR is usually bad, as the hardware usually can do that without an ISR as well.Allowing nested interrupts when something not that important in an ISR can very well be a good solution, if one can make sure the same type of interrupt is not called again before the code is finished and one has enough stack space. µCs with internal buffer (e.g. some PIC18) might also need extra attention.Handling slow IO parts like the LCD output can have more difficulties, not to access the same hardware twice at the same time (e.g. ISR and main program). Also waiting for the response is not that nice inside an ISR.

author: ice595
author_rank: Regular Contributor
timestamp: October 16, 2016, 08:51:51 pm
content: I still remember my final project for my microcontrollers course last year.We had to make a temperature logger with a 3x4 keypad and a 128x64 SPI LCD, and mine was the lowest in power consumption.What I did was setup a change notification interrupt to the pins connected to the keypad, so every time a key is pressed the ISR would do a keypad scan to determine which key was pressed and update the statemachine accordingly (e.g read temperature, save it, display saved data, dump data to uart). so it called the state machine function which called many many other functionsOnce the state machine is updated, it would go back to sleep and wait for further interrupts to wake it up again.and everyone else did an infinite loop scanning their keypad to constantly check for keypresses

author: ataradov
author_rank: Super Contributor
timestamp: October 16, 2016, 08:55:11 pm
content: Code:int main(void){// Init everything// Configure timer, but don't enable timer interruptwhile (1){if (interrupt flag is set in the timer register){// do display suff here// clear interrupt flag}// all other stuffwfi(); // make the thing low power (for ARM in this case, but similar thing goes for all other MCUs)}}Still no need for actual ISRs.

author: rstofer
author_rank: Super Contributor
timestamp: October 16, 2016, 09:10:38 pm
content: I like the idea of setting a flag in the interrupt handler. That concept flows nicely into setting a semaphore in an RTOS where, whenever it gets around to it, the RTOS will dispatch the code to do whatever is required of the interrupt. All priority stuff is handled by the RTOS. Sometimes there is more to it than setting a semaphore. We might grab a value in the interrupt handler and stuff it in a queue. But it's still fast! Get in and get out!There is certainly nothing wrong with polling the timer in the super loop; it's a pretty fast operation. What you don't want to poll (and hang) for is something like an incoming character, something that might not happen for a long time, if ever. So, there is a difference between testing and moving on versus testing and hang until.Early games were written in a super loop, in effect, simulating threads without multiple cores. There was a loop counter that wrapped around modulo <something>. The counter value was used to determine what part of the game to update using a large switch statement. That way, there was no need to hang and the game could be responsive without the graphics routine hogging the compute cycles.

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: October 16, 2016, 09:39:23 pm
content: Quote from: ice595 on October 16, 2016, 08:51:51 pmAll they would have needed to do was put a sleep instruction in their loop and it would have worked just as well. In fact if you're squeezing every nanojoule, it would have been more efficient as you wouldn't have the ISR context save overhead.The only normal reason to use an interrupt is when you need a guaranteed response time substantially faster than you can guarantee with your main program loop.You should generally only do the absolute minimum in the int code, as long ISRs will degrade the worst-case performance of other interrupts or your main code.Another good reason to minimise interrupt code is it can be a lot harder to debug problems in ( or caused by) interrupt routines.There are always some special cases where you might do things differently, but in general you should only use ints where they are needed, and then only to the minimum extent to which they are needed.

author: langwadt
author_rank: Super Contributor
timestamp: October 16, 2016, 10:47:21 pm
content: you can do other trickery, like triggering a low priority interrupt or manually adding a stack frame in the interrupt so rti will return to a different function before returning to main.sometimes such tricks can be necessary when an interrupt is shared and the cpu (e.g cortex) doesn'tsupport reentrant interrupts

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: October 16, 2016, 11:41:23 pm
content: Quote from: langwadt on October 16, 2016, 10:47:21 pmIf you need re-entrant interrupts you're probably doing it wrong.

author: dannyf
author_rank: Super Contributor
timestamp: October 17, 2016, 12:12:14 am
content: Quotewhat you were told is generally true. that means it is not always true, especially for a more complex chip like pic24/32. and you may want to see how frequently other tasks in your code run before making a decision here.as to clear the flag, you generally want to clear the flag at the beginning of the isr so not to miss an interrupt event.

author: langwadt
author_rank: Super Contributor
timestamp: October 17, 2016, 08:25:19 am
content: Quote from: mikeselectricstuff on October 16, 2016, 11:41:23 pmcalling it re-entrant might be the wrong term, more like software nesting, as and example a timer timer with a single interruptbut four comparesor an arm7 that only has one interrupt

author: ice595
author_rank: Regular Contributor
timestamp: October 17, 2016, 10:38:14 am
content: Quote from: dannyf on October 17, 2016, 12:12:14 amso as soon as the hardware interrupt flag bit is cleared, other interrupts of same or lower priority can interrupt the current ISR even though it didn't fully exit the current ISR function right?

author: dannyf
author_rank: Super Contributor
timestamp: October 17, 2016, 11:13:01 am
content: Whether interiors can be nested is a hardware question, and not a primary driver here.Think of two tightly timed events being served by the isr.The first event fires and the execution jumps to the isr. Let's say that the isr clears the flag on the way out. While the isr is serving the first event, the 2nd event arrives and the flag remain set. On the exit, the flag is set.As far as the isr is concerned, only one event took place.If instead, you clear the flag right away, the 2nd event will set the flag again. Upon exiting the first isr, the execution will jump right back to it. Ie. Both events are served. And no event is missed.

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: October 17, 2016, 12:30:04 pm
content: Quote from: dannyf on October 17, 2016, 11:13:01 amIf you're expecting this to happen, a better approach is to dowhile (interrupt_flag){<clear interrupt_flag><do interrupt stuff>}That way, you don't have the interriupt entry/exit overhead for the second interrupt

author: langwadt
author_rank: Super Contributor
timestamp: October 17, 2016, 02:26:50 pm
content: Quote from: mikeselectricstuff on October 17, 2016, 12:30:04 pmwith cortex-m it doesn't make much difference, it does tail chaining so it skips the restore-store when an interrupt is pendingon the return from interrupt

author: KL27x
author_rank: Super Contributor
timestamp: October 18, 2016, 01:45:18 am
content: QuoteNo.... Another interrupt flag may be set in the interim. But not until you exit ISR can another equal/lower ISR flag be acted upon by its own ISR. Hence, while any piece of ISR is being executed, there will be latency in response to a flag being set of equal or lower priority.You can write the code, right in your own ISR, to poll for a "higher (by your definition)" interrupt flag, and to abandon current ISR.QuoteIt might matter. In case you want this ISR subroutine to immediately repeat if the condition is retriggered while ISR is still executing or not.The main reason to do it up front, though, IMO, is so you don't have to cover it multiple times, in case of multiple branches in the ISR.Your needs may be unique. You can certainly write good code that doesn't follow the "short ISR" rule. It depends what you need the micro to do. As long as you understand it, you can achieve your result anyway you want.If your code may get more complicated later, it might be nice to use timer interrupt set for 10ms, rather than simply set timer for 1 second and check the timer generated flag in main code loop. In ISR, you can increment multiple counters. In this way you can use a single timer for multiple events. When the "LCD timer counter" reaches 100, set your own flag. This can stretch your peripherals and reduce time spent looking at datasheet. Or you could set it so that some order of 2 will equal 1 second. And let the counter freely increment. Everytime bit X change state, set that user flag. Evertime bit Y changes state, you might have something else periodically firing at a different rate, which is some order of 2 different in period, etc. Now you got multiple concurrent timers out of a single post scaler.

author: viperidae
author_rank: Frequent Contributor
timestamp: October 18, 2016, 08:27:16 am
content: If you call a long running function from an isr then no other isr will run. The isr isn't over until the retfie instruction is called.The only exception to this is high priority interrupts can interrupt a low priority one.That's how it works for pic24.Pic32 is similar

author: ice595
author_rank: Regular Contributor
timestamp: October 18, 2016, 12:54:36 pm
content: Quote from: viperidae on October 18, 2016, 08:27:16 amso, this is basically what I have right nowCode:void main (){(initialize stuff here)while(1){(sleep)}}void timerISR (){hardware_interrupt_flag_bit = 0;update_lcd();}If I make my code like this, the timer interrupt will still update the lcd periodically, but the ISR does not call the update_lcd() function directly and the MCU goes to sleep after update_lcd() is done?Code:void main (){(initialize stuff here)while(1){if (soft_flag){soft_flag = 0;update_lcd();}(sleep)}}void timerISR (){hardware_interrupt_flag_bit = 0;soft_flag = 1;}

author: ataradov
author_rank: Super Contributor
timestamp: October 18, 2016, 03:15:30 pm
content: Quote from: ice595 on October 18, 2016, 12:54:36 pmCorrect. You can even go one step further and to this:Code:void main (){(initialize stuff here, but don't enable interrupt source)while(1){if (hardware_interrupt_flag_bit){hardware_interrupt_flag_bit = 0;update_lcd();}(sleep)}}And you will get the same result on most sane architectures.

author: max_torque
author_rank: Super Contributor
timestamp: October 18, 2016, 04:12:20 pm
content: The important question is not "how often do i need to call my function" but "What happens if my function is a bit late"??Take the OPs 1sec LCD update requirement. He could generate a high priority interrupt, that deffinately ensures the LCD gets updated exactly once a second. But i suspect, the end user won't notice or care, if the LCD is updated outside of that requirement. On a human scale, even 100ms is a pretty short time, but it's an iceage for a processor these days. SO, if your lcd only gets updated after say 1025ms or 1050ms, or even 1100ms does it matter? If the answer is "NO" then why use an interrupt to do it?Of course, sometimes, Latency DOES matter. If your LCD has to be updated at fixed intervals to avoid screen scanning artifacts or flicker or whatever, then yes, an definite, high priority interrupt could well be required. But i suspect, for a typical serial LCD as used in a lot of projects, exact timing is really pretty irrelevant!

author: ice595
author_rank: Regular Contributor
timestamp: October 18, 2016, 05:28:38 pm
content: Quote from: ataradov on October 18, 2016, 03:15:30 pmBut if the interrupt is not enabled, how does the MCU know when to wake up?

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: October 18, 2016, 05:37:01 pm
content: Quote from: ice595 on October 18, 2016, 05:28:38 pmDepends on the MCU - e.g. I think from memory 8 bit PICs will wake on an int that's enabled in teh individual int enable bits, but with global int enable off so it doesn't actually cause an int.Others may need the int to be enabled, and a dummy ISR to catch it.

author: ataradov
author_rank: Super Contributor
timestamp: October 18, 2016, 05:38:04 pm
content: Quote from: ice595 on October 18, 2016, 05:28:38 pmThat is very MCU dependent. In many cases you don't enable the interrupt, you enable the call of ISR. Interrupt sources are still hooked up to the power management subsystem.

author: dannyf
author_rank: Super Contributor
timestamp: October 18, 2016, 08:06:59 pm
content: What's the point of having the interrupt enable bit if it cannot disable the interrupt?

author: langwadt
author_rank: Super Contributor
timestamp: October 18, 2016, 08:07:51 pm
content: Quote from: ataradov on October 18, 2016, 05:38:04 pmyou need some way of enabling what wakes up the cpu, there are usually many interrupt sources that areactive all the time like uart tx empty , timer overflowusually sleep is really halt until interrupt

author: ataradov
author_rank: Super Contributor
timestamp: October 18, 2016, 08:15:39 pm
content: Quote from: langwadt on October 18, 2016, 08:07:51 pmWell, yes. In case of ARM, for example, there are individual interrupt enable bits in the peripheral that are combined to generate a single interrupt request to the NVIC, which actually calls the handler. You need to have individual bits enabled, but NVIC one can be disabled for the core to wake up.

author: lem_ix
author_rank: Regular Contributor
timestamp: October 19, 2016, 04:09:50 pm
content: You've got good replies here, flags are the way to go usually. For something simple like an lcd anything will do, but for a more complex project where you have many interrupts it's good practice to have them execute fast. Interrupts "break" conventional sequential programming and your job in a sense is damage control. Specifically you asked about a function call. If you know any assembly think about what a function call inside an isr is doing. Did you just add some jump instructions? Can the compiler optimize the function? Maybe it had to save a bunch of registers and added a lot of push/pop instructions. Does any of that matter to you for this application? Maybe you're more worried about parallel access to some variable. If you're curious take a look at the assembly output but usually thinking about such things is hard and a waste of time if you can execute the code in a more "controlled" manner.Point being ISRs are nice but are not the tool for every job, so make life easier for yourself

author: mikeselectricstuff
author_rank: Super Contributor
timestamp: October 19, 2016, 05:53:40 pm
content: A general rule - if you have to do a funciton call in an ISR, you're probably trying to do too much in the ISR.And of course you should REALLY NOT EVER have a function that can be called by the ISR and the foreground code. That would be bad.And of course don't forget to declare your ISR-foreground communicaiton variables as volatile.