Switching between two ISRs

Hi everybody, I'm looking for a way to have two alternative ISRs service the same interrupt, with the software choosing which ISR gets called. Is there a neater way of doing this than the following code?

ISR(XXX_vect)
{
  if (global_flag == 1) {
    subroutine_a();
  } else {
    subroutine_b();
  }
}

You could detach the ISR you don't want called, and attach the one you do want called, as conditions change.

With the AVR architecture that's the best you can do AFAIK because the vector table is in flash memory.

The code behind the Arduino attachInterrupt() does similar so you could use that but what you have is probably better depending on how often the functions change.

EDIT: You could also have an external function pointer and change that outside the ISR, that will reduce the latency in the ISR.


Rob

Graynomad: EDIT: You could also have an external function pointer and change that outside the ISR, that will reduce the latency in the ISR.

Thanks, I like that idea much better. It also appears to be slightly more efficient in terms of memory (both RAM and flash).

beware that if you go fo global_flag it should be volatile

robtillaart: beware that if you go fo global_flag it should be volatile

That brings up an interesting point. The references I can find recommend the volatile declaration for variables modified by interrupts, but say nothing about variables which are only tested by the ISR. However I can see that without the volatile declaration the compiler might optimise the main routine such that global_flag's memory location does not contain the current value of global_flag, thereby confusing the ISR. Should all variables referenced inside the ISR be declared volatile?

The safest (and best) strategy…

If it’s shared, make it volatile. If it’s only accessed by one side or the other, no need for volatile. When in doubt, make it volatile.

loop side access must be protected by disabling interrupts.

tim7: Should all variables referenced inside the ISR be declared volatile?

Not at all. Especially since ISRs are supposed to be fast. Just think about what is happening. Inside loop it might be processing some series of assignments. Normally if "foo" is a variable it can load it into a register and save time by using the register. But if an ISR can change "foo" then it can't rely upon "foo" staying the same from one instruction to the next. So that is why you make it volatile.

However for variables used inside an ISR, there is no need for that. And one ISR won't be interrupted by another, so for variables simply shared by ISRs you don't need to worry either.

To solve the original problem it might be simpler just to have an ISR test a variable. Attaching and detaching interrupts has a higher overhead than simply changing a variable.

[quote author=Coding Badly link=topic=95260.msg715780#msg715780 date=1331020964] If it's single byte, no need for protection. [/quote]

Can't agree with that CB. Any shared variable needs protection.

Sounds good to me. (previous post modified)

Coding Badly is referring to a separate problem. That is, even if declared volatile a multiple byte variable might be changed in the middle (eg. after the processor has loaded the low-order byte the high-order one might change).

Disabling interrupts while accessing multi-byte variables, outside an ISR, when they might be changed inside an ISR is prudent.

An alternative approach is to set a single-byte variable (like a boolean) inside the ISR, after you have changed all your other variables (eg. "fired = true;") and then not changing them again until the main loop sets that back to false. This single-byte flag variable can be used to protect a whole lot of multi-byte variables.

Careful attention to detail is required in this case, as you need to be certain you are doing things in the right order.

[quote author=Nick Gammon link=topic=95260.msg715843#msg715843 date=1331026451] However for variables used inside an ISR, there is no need for that. And one ISR won't be interrupted by another, so for variables simply shared by ISRs you don't need to worry either.[/quote]

I was thinking of the situation where the main loop is accessing and modifying a global variable which the ISR needs to read. To gain speed the compiler might decide to temporarily keep this variable in a register. Now when an interrupt occurs, the ISR comes unstuck because it looks in memory rather than the register and reads an out-of-date value for the variable. Can such a situation arise?

To solve the original problem it might be simpler just to have an ISR test a variable. Attaching and detaching interrupts has a higher overhead than simply changing a variable.

But if I follow Graynomad's suggestion I'm still only changing a variable, only it's now a pointer-to-a-function instead of a flag. My code now looks something like this:

void (*function_pointer)();

void loop() {
  function_pointer=function_a; // from now on interrupt will execute function_a
  // ...
  function_pointer=function_b; // from now on interrupt will execute function_b
  // ...
}

void function_a() {
  // do something
}

void function_b() {
  // do something else
}

ISR(XXX_vect) {
  function_pointer();
}

That's what I had in mind, you would have to protect the pointer assignments from interrupts by enclosing them in cli/sei instructions or otherwise disabling the interrupt that used them.


Rob

tim7: I was thinking of the situation where the main loop is accessing and modifying a global variable which the ISR needs to read. To gain speed the compiler might decide to temporarily keep this variable in a register. Now when an interrupt occurs, the ISR comes unstuck because it looks in memory rather than the register and reads an out-of-date value for the variable. Can such a situation arise?

That's why you make them volatile. So the ISR doesn't do that. But I see what you mean about saving the register. I presume volatile variables are not only read each time, but saved as well. And if you catch it just before it is saved, well you might just have well caught it before it was modified, so I don't see much difference there.

As for the function pointers, yes fair enough I suppose. But since they are 2 bytes, as Graynomad said, you now need to protect such modifications by cli() and sei(), so you are basically changing setting and changing a single byte into changing two bytes, plus the call to disable and re-enable interrupts. So I don't see a heap of point.

I guess it depends in the frequency of the function handler changes and the required latency in the ISR as to which method is best.

The global var approach is faster in the body code and the function pointer approach is faster in the ISR, bit either way there's not much between them.


Rob

[quote author=Coding Badly link=topic=95260.msg715780#msg715780 date=1331020964] If it's shared, make it volatile. If it's only accessed by one side or the other, no need for volatile. When in doubt, make it volatile. loop side access must be protected by disabling interrupts. [/quote]

Thanks, that's v. clear.