2

I am using an ISR, which is written to be as minimal as possible:

volatile bool interrupt1{};

void ISR1() {
    interrupt1 = true;
}

The interrupt is handled by polling the interrupt1 variable in loop():

if (interrupt1 == true) {

// Do stuff

interrupt1 = false;
}

Even though the interrupt signal coming in on the gpio isn't polled, the method described above is instead polling the interrupt variable from loop().

As an alternative to the above, is it possible to use some kind of push logic on the software side whenever an interrupt occurs, instead of continuously polling the interrupt variable? I hesitate to handle the full task from the ISR, as it's performing a lot of things.

What are my options here?

6
  • 1
    if (interrupt1 == true) the true is redundant ... use if (interrupt1) instead
    – jsotola
    Nov 28, 2023 at 0:56
  • volatile bool interrupt1{}; - what are the braces for?
    – Nick Gammon
    Nov 28, 2023 at 6:19
  • @NickGammon This is an empty braced-init-list, I think blindly added or because of source code guidelines. Nov 28, 2023 at 7:11
  • @Nick Gammon Uniform initialization
    – Erik
    Nov 28, 2023 at 8:36
  • @jsotola I know it's redundant, but i prefer the verbosity in this case
    – Erik
    Nov 28, 2023 at 8:37

2 Answers 2

10

If the task is pretty short and time sensitive, your best option is to put it in the interrupt handler.

If the task is big and not too urgent (like: it could well wait for a millisecond or so), your approach of polling the flag within loop() is the best one, provided the main program is non-blocking.

If the task is both big and urgent... you may have a difficult problem. You could try both approaches and see which one works best. Or you may try to split the task into a top half (short and urgent) and a bottom half (long but not so urgent). The interrupt handler would then perform the top half and set a flag, whereas the main loop would poll the flag and perform the bottom half.

Edit: for a short discussion on the top half / bottom half approach, see the first paragraphs of this article on Linux kernel development.

2
  • 1
    Thanks for your reply. Last portion is particularly interesting. I might be able to refactor the responsibilities for a better tradeoff between timing and heavy lifting, so to speak...
    – Erik
    Nov 27, 2023 at 11:54
  • 2
    It can be tricky to think about what part of a task is "urgent". The ISR handler should take care of anything that will go wrong if you wait (like anything that's necessary to prepare the hardware for another event, if it arrives before you've finished with this one.) Nov 28, 2023 at 4:16
3

Since you tagged it Teensy: You could also reduce the priority of the interrupt, so that it can be interrupted by higher priority interrupts. Thus you can implement interrupts with longer durations without blocking important system functions.

Here is an example using the GPT1 and GPT2 timers running at different priorities:

#include "Arduino.h"
#include "TeensyTimerTool.h"

using namespace TeensyTimerTool;

PeriodicTimer loPrioTimer(GPT1);
PeriodicTimer hiPrioTimer(GPT2);

void onLowPrioTimer() // some long task running from interrupt with low prio
{
    Serial.printf("  Start low prio isr @t=%d ms\n", millis());

    for (int i = 0; i < 10; i++)
    {
        Serial.printf("    lowPriority task %d\n",i);
        delay(10);
    }

    Serial.println("  Stop low prio isr");
}

void onHiPrioTimer() // high prio, will interrupt the low prio isr
{
    Serial.printf("high priority @t=%d ms\n", millis());
}

void setup()
{
    while (!Serial) {}

    loPrioTimer.begin(onLowPrioTimer, 500ms); // GPT1
    hiPrioTimer.begin(onHiPrioTimer, 50ms);   // GPT2

    NVIC_SET_PRIORITY(IRQ_GPT1, 128); // lowest prio
    NVIC_SET_PRIORITY(IRQ_GPT2, 16);  // high prio
}

void loop()
{
}

which prints:

high priority @t=1057 ms
high priority @t=1107 ms
high priority @t=1157 ms
high priority @t=1207 ms
high priority @t=1257 ms
high priority @t=1307 ms
high priority @t=1357 ms
high priority @t=1407 ms
high priority @t=1457 ms
high priority @t=1507 ms
  Start low prio isr @t=1507 ms
    lowPriority task 0
    lowPriority task 1
    lowPriority task 2
    lowPriority task 3
    lowPriority task 4
high priority @t=1557 ms
    lowPriority task 5
    lowPriority task 6
    lowPriority task 7
    lowPriority task 8
    lowPriority task 9
high priority @t=1607 ms
  Stop low prio isr
high priority @t=1657 ms
high priority @t=1707 ms
high priority @t=1757 ms
high priority @t=1807 ms
high priority @t=1857 ms
high priority @t=1907 ms
high priority @t=1957 ms
high priority @t=2007 ms
  Start low prio isr @t=2007 ms
    lowPriority task 0
    lowPriority task 1
    lowPriority task 2
    lowPriority task 3
    lowPriority task 4
high priority @t=2057 ms
    lowPriority task 5
    lowPriority task 6
    lowPriority task 7
    lowPriority task 8
    lowPriority task 9
high priority @t=2107 ms
  Stop low prio isr
high priority @t=2157 ms
high priority @t=2207 ms
high priority @t=2257 ms
high priority @t=2307 ms

This shows that the high priority timer (running at 50 ms interval) intercepts the low priority ISR which runs every 500 ms for about 100 ms.

7
  • Thanks for your reply. I've never used interrupt priority. Wanna elaborate?
    – Erik
    Nov 27, 2023 at 12:21
  • 1
    @Erik added an example
    – luni64
    Nov 27, 2023 at 15:03
  • Very interesting, thank you!
    – Erik
    Nov 27, 2023 at 15:50
  • What if an interrupt of the same kind occurs? For example, if it was counting something, wouldn't it miss a count increment (it presumably isn't a reentrant ISR)? Nov 27, 2023 at 21:20
  • What baud rate was used for 'Serial'? Nov 27, 2023 at 21:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.