I think the article is missing important information about how NVIC latches interrupts, or in general the relation between the interrupt signal and the execution of the interrupt. The NVIC supports both "level" and "pulse" type interrupts, and an interesting property of the "pulse" interrupts is that if the interrupt line goes low and high again while the ISR is running, the ISR will be re-executed. More info is provided in the manuals[1].
It is good to understand what is happening between the peripherals and the NVIC. Even though in many cases the device manual fails to specify exactly what type of interrupts are used and the logic in the peripherals that drives the interrupt lines. E.g. they may just say, these bits enable these interrupts which occur on these events, ommitting info such as whether the interrupts are pulsed or level and how/whether the peripheral internally latches the interrupt condition.
There are also little practical tips provided in the article to prevent "messing up" interrupts. Here's a few of mine:
- Try to get a good understanding of the interrupt logic of the peripheral.
- Make sure to clear any associated interrupt condition in the interrupt handler, as applicable.
- Before enabling the interrupt, set all variables exepected by the ISR and do a memory barrier (e.g. gcc's asm volatile with memory clutter; this may be done implicitly with "enable interrupts", i.e. asm volatile ("cpsid i" : : : "memory")).
- In case of edge-sensitive interrupts be aware they could trigger on ANY edge, including due to transient conditions. E.g. you're filling up a buffer through an interrupt that fires when at least 4 words are available to be written. Say the ISR fires with exactly 4 words available, you write the first word, then the peripheral consumes 1 word before you write the remaining 3 words. With pulse-interrupts that causes another invocation of the ISR, at the start of which you do not necessarily have 4 words of buffer space available. So, generally it is best to understand interrupts as hints only and still verify that a specific event has occurred / condition is satisfied.
- Specific to clocks, I found a very practical way to time different events though interrupts is by running timers in simple "overflowing" mode and just set the output compare units to fire whenever you want them to - treat time as modulo 2^N and deal with overflow right (by only ever looking at differences of times). It's not a problem if you want more range than the N-bit timers can provide because you can simulate that by handling counter-overflow. Also, if you need to synchronize more events than you have compare units on one timer, typically it is possible to run multiple timers synchronzied (set same clock, start them very quickly in succession).
- Some peripherals are just badly designed and you need to read every single piece of the documentation while specifically looking for behavior that's going to bite you. Specifically notable cases is when reading some register implicitly clears some interrupt flags, so you may accidentally acknowledge an interrupt outside of the ISR.
Its missing a ton of information and it seems poorly presented. I found that surprising given that the author teaches the topic.
In general I agree that the most confusing part with these chips are that the ARM interrupts are all very well defined and they have clear semantics, but the vendor specific interrupts are kind of all over the place as the vendors use licensed IP from a variety of vendors and attach them to the interrupt structure in different ways.
That said, interrupts have the inherent complexity of "you were doing something and POW! now you're doing something else." which starts out weird to new programmers and then when nested interrupts are brought up their head explodes. I can imagine it is a tough topic to teach.
It is missing some detail about how the interrupts operate, but:
a) It's a blog post not a reference manual. It's aim appears to be clear up some confusion about terminology (and having been using a Cortex M4 for a year I've spent a fair amount of time trying to reconcile conflicting documentation).
b) It's only the first blog post in an intended series. Who knows what's going to get covered next.
Even though I've got most of this sorted out in my head by now, it's still helpful to me to have it put clearly in one place (even if it's just so that I have a link to give to newer members of my team).
Excellent introduction! That implementations sometimes don't implement the full range of priorities has bit me in the past with really hard to debug issues. Not knowing much about FreeRTOS, I look forward to the next part in the series.
Yes. That's exactly what it means. You can have priorities for different interrupt sources. It has utility in many critical RTOS situations.
Imagine you have a factory machine. The micro has an IRQ for your communication interface. This goes off when there is a packet. Obviously you want this to be handled so there is not backlog of packets, however there is another interrupt for a limit switch that goes off when the door to a chamber in the device has been opened. You want to handle that interrupt above all else so you can shut off the rotating blade in that chamber before a human hand can get in there.
It also means you need to be extra careful compared to simpler architectures (like AVR). For example for locking you may need to disable/re-enable interrupts also within an ISR not just in the main loop / threads.
If nesting is a problem for you and you want to be lazy, I think you can disable nesting between specific interrupts by giving them the same priority (though I can't find any docs right now about that).
1) You should expect lost , delayed and garbled comms. Your app layer or stacks should handle this.
2) A limit switch will still need debounce. If it's the sort of limit switch that will keep people's hand attached to their arm, I would be loathe to depend on interrupt priority for it.
I suppose this was just an example of when you want to do safety-critical control in software. For something as simple, hardware measures are probably more appropriate, but your particular problem may be far too complex to solve with simple hardware-based logic. For example, consider a system in a radiation therapy machine which ensures the patient does not receive too much dose or at the wrong spots.
It is good to understand what is happening between the peripherals and the NVIC. Even though in many cases the device manual fails to specify exactly what type of interrupts are used and the logic in the peripherals that drives the interrupt lines. E.g. they may just say, these bits enable these interrupts which occur on these events, ommitting info such as whether the interrupts are pulsed or level and how/whether the peripheral internally latches the interrupt condition.
There are also little practical tips provided in the article to prevent "messing up" interrupts. Here's a few of mine:
- Try to get a good understanding of the interrupt logic of the peripheral.
- Make sure to clear any associated interrupt condition in the interrupt handler, as applicable.
- Before enabling the interrupt, set all variables exepected by the ISR and do a memory barrier (e.g. gcc's asm volatile with memory clutter; this may be done implicitly with "enable interrupts", i.e. asm volatile ("cpsid i" : : : "memory")).
- In case of edge-sensitive interrupts be aware they could trigger on ANY edge, including due to transient conditions. E.g. you're filling up a buffer through an interrupt that fires when at least 4 words are available to be written. Say the ISR fires with exactly 4 words available, you write the first word, then the peripheral consumes 1 word before you write the remaining 3 words. With pulse-interrupts that causes another invocation of the ISR, at the start of which you do not necessarily have 4 words of buffer space available. So, generally it is best to understand interrupts as hints only and still verify that a specific event has occurred / condition is satisfied.
- Specific to clocks, I found a very practical way to time different events though interrupts is by running timers in simple "overflowing" mode and just set the output compare units to fire whenever you want them to - treat time as modulo 2^N and deal with overflow right (by only ever looking at differences of times). It's not a problem if you want more range than the N-bit timers can provide because you can simulate that by handling counter-overflow. Also, if you need to synchronize more events than you have compare units on one timer, typically it is possible to run multiple timers synchronzied (set same clock, start them very quickly in succession).
- Some peripherals are just badly designed and you need to read every single piece of the documentation while specifically looking for behavior that's going to bite you. Specifically notable cases is when reading some register implicitly clears some interrupt flags, so you may accidentally acknowledge an interrupt outside of the ISR.
[1] http://infocenter.arm.com/help/topic/com.arm.doc.ddi0337h/CH...