5
\$\begingroup\$

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?

asked Sep 8 at 20:40
\$\endgroup\$
10
  • 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\$ Commented 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\$ Commented Sep 9 at 7:38
  • \$\begingroup\$ What is "efficiency" for you? Used stack space, ISR run time, needed power? \$\endgroup\$ Commented 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\$ Commented 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\$ Commented Sep 9 at 10:15

1 Answer 1

13
\$\begingroup\$

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.

answered Sep 8 at 21:09
\$\endgroup\$
4
  • \$\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\$ Commented 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\$ Commented 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) (see VPORTs). A jump table architecture like shown here, can even use SBIS etc. Making a local variable might be a good idea anyway for atomicity reasons, as the port registers are declared volatile 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\$ Commented 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 with PORTD_INTFLAGS = PIN2_bm; \$\endgroup\$ Commented Sep 11 at 1:55

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.