I have some libraries which do non-atomic operations on shared data structures (like fifos, etc.), which may be called from either interrupt or non-interrupt contexts. So far, I've been able to get away with "cheating" by having the "push" operations performed only from an interrupt context, and the "pop" operations only from a non-interrupt context. So, the "push" code assumes interrupts are disabled, and the "pop" code explicitly turns interrupts off at the start, and turns interrupts back on at the end. But, I now want to be able to call both routines from any context.
So the question is: How do I save and restore the interrupt state? It's not clear to me how interrupts are masked/disabled when an ISR is called, and the Due has a multitude of ways of doing it. I'd basically like to create replacements for interrupts() and noInterrupts(). The noInterrupts() replacement would save the current interrupt enable state, then disable all interrupts. The interrupts() replacement would restore the interrupt enable state to exactly what it was before noInterrupts was called.
MorganS:
Do you know where to look to find the source code for noInterrupts()? I would start there.
With great difficulty, I did find where those methods are defined, and it was no help, as they are inline assembler, and I don't know enough about either ARM assembler or the SAM register set to understand what they are doing. Nor does it give a clue how to save and restore the state, rather than setting or clearing it. I'm hoping someone here has already cracked this nut, because I can see it would likely take me hours to learn enough to be able sort it out.
Here are the definitions, from core_cmFunc.h:
/** \brief Enable IRQ Interrupts
This function enables IRQ interrupts by clearing the I-bit in the CPSR.
Can only be executed in Privileged modes.
*/
__attribute__( ( always_inline ) ) static __INLINE void __enable_irq(void)
{
__ASM volatile ("cpsie i");
}
/** \brief Disable IRQ Interrupts
This function disables IRQ interrupts by setting the I-bit in the CPSR.
Can only be executed in Privileged modes.
*/
__attribute__( ( always_inline ) ) static __INLINE void __disable_irq(void)
{
__ASM volatile ("cpsid i");
}
I THINK what I need to do is read and write the PRIMASK register, which I THINK can be done with:
mrs r0, PRIMASK // to read PRIMASK to r0
msr PRIMASK, r0 // to write r0 to PRIMASK
but, again, I don't know enough ARM assembly, or the gnu register conventions, to know how to use these to write functions that read PRIMASK, and store the value, then read the stored value, and write it back to PRIMASK.
/** \brief Enable External Interrupt
This function enables a device specific interrupt in the NVIC interrupt controller.
The interrupt number cannot be a negative value.
\param [in] IRQn Number of the external interrupt to enable
*/
static __INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)
{
NVIC->ISER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* enable interrupt */
}
Looks like name of the register is ISER, and more from data sheet:
12.20.2
Interrupt Set-enable Registers
The ISER0-ISER1 register enables interrupts, and show which interrupts are enabled. See:
Here is the one function I think I need, in the hopes that someone here can help with the actual two or three lines of inline assembler code to implement it:
uint32_t getPRIMASK(void);
All this need to do is read the PRIMASK register, and return it. I can then use interrupts and noInterrupts() to do what I need as follows:
boolean primask = getPRIMASK();
noInterrupts();
// do whatever here...
if (primask)
interrupts();
If I am understanding correctly how gcc allocates registers, using r0 for the return value of a function, this might be as simple as:
But, since I'm not getting any responses on the Programming Forum, perhaps someone here knows how to change compile options? I need to know how to remove the -no-rtti option, which is preventing me from using dynamic_cast. I'm sure there's a configuration file somewhere that sets the compiler options, but I have no clue where to look...
I don't know all the 'specifics' of your initial interrupts issue, but it's a RARE day that I would ever want to disable ALL interrupts.
You might find that start 'missing' an occasional time-critical interrupt that's not even related in any way to your code leading to overrun errors.
For me, I find that 'protecting' those critical areas of code with atomic spinlocks, semaphores, mutexs (lookup LDREX and STREX) will do the job just nicely. However, a LOT depends on how your code is structured so YMMV. Maybe I'm just being 'old school'?
My 'rule of thumb' is to do the barest minimum while I am in interrupt context leaving everything possible to a 'bottom half' handler.