In an experimental project using an AVR64EA MCU, I've wired two external interrupts to pins of the same port. The ISR looks like this:
ISR(PORTD_PORT_vect) {
if (PORTD_INTFLAGS & PORT_INT_2_bm) {
PORTD_INTFLAGS |= PORT_INT_2_bm;
// do something quick and lightweight
} else if (PORTD_INTFLAGS & PORT_INT_3_bm) {
PORTD_INTFLAGS |= PORT_INT_3_bm;
// do something else quick and lightweight
}
}
While this works fine, I am wondering how efficient (as in time efficiency/ISR run time) and elegant it is to check for up to 8 interrupts in the same ISR instead of having one ISR for each interrupt.
The datasheet explains about Compact Vector Table:
The Compact Vector Table (CVT) is a feature to allow the writing of compact code by having all level 0 interrupts share the same interrupt vector number. Thus, the interrupts share the same Interrupt Service Routine (ISR). This reduces the number of interrupt handlers and thereby frees up memory that can be used for the application code.
So, the above example is desirable (at least for memory usage) but I am still unsure about efficiency. Or is my if .. else if
just too clumsy?
-
1\$\begingroup\$ Never mind. I just looked. The CVT is hardware support. I would not use it unless I was strapped for resources. It's always better if you don't have to do a bunch of tests at the start of an interrupt just to find out who is asking for service. \$\endgroup\$periblepsis– periblepsis2025年09月08日 21:20:54 +00:00Commented Sep 8 at 21:20
-
2\$\begingroup\$ I don't follow the memory argument, as each conditional branch still needs the same code. Consider memory saving techniques only if the memory overflows. \$\endgroup\$the busybee– the busybee2025年09月09日 07:38:56 +00:00Commented Sep 9 at 7:38
-
\$\begingroup\$ What is "efficiency" for you? Used stack space, ISR run time, needed power? \$\endgroup\$the busybee– the busybee2025年09月09日 07:40:25 +00:00Commented Sep 9 at 7:40
-
1\$\begingroup\$ Efficiency here is for me exactly what Dave Tweed's answer is about, time efficiency/ISR run time. \$\endgroup\$Torsten Römer– Torsten Römer2025年09月09日 09:08:37 +00:00Commented Sep 9 at 9:08
-
\$\begingroup\$ Then a dedicated ISR per interrupt will be most efficient, will it not? Because you save the conditional. (BTW, please use the ampersand notation to address commenters. I came back just by accident.) Oh, and this definition of efficiency would be best given directly in the question... \$\endgroup\$the busybee– the busybee2025年09月09日 10:15:26 +00:00Commented Sep 9 at 10:15
1 Answer 1
It's fine.
The only thing I'd change is to get rid of the else
. This potentially improves the time efficiency, by not requiring a separate invocation of the ISR for each interrupt. Instead, you handle ALL of the interrupts that are active for a given invocation.
In other words,
ISR(PORTD_PORT_vect) {
if (PORTD_INTFLAGS & PORT_INT_2_bm) {
PORTD_INTFLAGS |= PORT_INT_2_bm;
// do something quick and lightweight
}
if (PORTD_INTFLAGS & PORT_INT_3_bm) {
PORTD_INTFLAGS |= PORT_INT_3_bm;
// do something else quick and lightweight
}
}
It does mean that a single invocation might run longer, but the total time spent in the ISR over the long term will be less, with fewer context switches. If there are multiple interrupts active at the same time, the background code won't get a chance to execute between them anyway.
-
\$\begingroup\$ What's nice about this solution is you can view it as a precooked event queue; and this is exactly what full multitasking OSs do, they chain a bunch of callbacks in one execution thread (or several, depending on environment). Eliminating all the list and callback overhead at compile time makes it much more efficient. \$\endgroup\$Tim Williams– Tim Williams2025年09月08日 21:44:51 +00:00Commented Sep 8 at 21:44
-
4\$\begingroup\$ On some devices, access to the interrupt flags "port" may be faster or slower than accessing other forms of memory or registers. If slower, it may make sense to copy PORTD_INTFLAGS to a local variable and then test that. But unless there are really high interrupt frequencies it shouldn't make much of a difference. Another thing is that you may order the tests and associated blocks based on how frequent and/or time-sensitive each one is. \$\endgroup\$jcaron– jcaron2025年09月09日 11:28:36 +00:00Commented Sep 9 at 11:28
-
2\$\begingroup\$ @jcaron In the AVR family, ports are memory mapped, with propagation delay of a few clock cycles, and the CPU taking 2-3 cycles to fetch with
LD
/LDS
, or 1 cycle for IO space (IN
) (seeVPORT
s). A jump table architecture like shown here, can even useSBIS
etc. Making a local variable might be a good idea anyway for atomicity reasons, as the port registers are declaredvolatile
so the compiler is obliged to fetch it every time it appears; whether that's desirable here (an interrupt might arrive during execution, and thus be caught sooner than later?) is another matter. \$\endgroup\$Tim Williams– Tim Williams2025年09月09日 15:14:59 +00:00Commented Sep 9 at 15:14 -
\$\begingroup\$ The INTFLAGS register in AVR64EA offers atomic write operation to clear the interrupt by writing a '1' to the bit corresponding to the pin, writing a ‘0’ to bit n in the bit field has no effect. So the read-modify-write operation like
PORTD_INTFLAGS |= PORT_INT_2_bm;
is not necessary, it can be done withPORTD_INTFLAGS = PIN2_bm;
\$\endgroup\$hcheung– hcheung2025年09月11日 01:55:36 +00:00Commented Sep 11 at 1:55