I have two external interrupt sources coming into an STM32F105. I want one of them (call it "IRQHigh") to pre-empt the other ("IRQLow"). Currently, if IRQHigh is triggered during the IRQLow ISR, the program flow waits until I clear the IRQLow ITPending bit before it branches into the IRQHigh ISR.
The STM32F105 is a Cortex-M3-based microcontroller. It supports nested interrupts. My application is written in C, using GCC (arm-none-eabi-gcc) in Eclipse, with the STM32F1 Standard Peripheral Library.
I think I have the priorities configured correctly but I must be missing something.
Here is the corresponding initialization code. I have stripped out AFB clock commands, GPIO config, etc, since each subsystem seems to work fine by itself:
#define IRQHIGH_EXTI_PORT GPIO_PortSourceGPIOA
#define IRQHIGH_EXTI_PIN GPIO_PinSource3
#define IRQHIGH_EXTI_LINE EXTI_Line3
#define IRQHIGH_EXTI_IRQn EXTI3_IRQn
#define IRQLOW_EXTI_PORT GPIO_PortSourceGPIOC
#define IRQLOW_EXTI_PIN GPIO_PinSource11
#define IRQLOW_EXTI_LINE EXTI_Line11
#define IRQLOW_EXTI_IRQn EXTI15_10_IRQn
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
// Sixteen levels of pre-emption priority, no subpriorities
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
// IRQHigh
// Connect EXTI Line to GPIO Pin
GPIO_EXTILineConfig(IRQHIGH_EXTI_PORT, IRQHIGH_EXTI_PIN);
// Configure EXTI line
EXTI_InitStructure.EXTI_Line = IRQHIGH_EXTI_LINE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// Configure and enable EXTI Interrupt
NVIC_InitStructure.NVIC_IRQChannel = IRQHIGH_EXTI_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// IRQLow
// Connect EXTI Line to GPIO Pin
GPIO_EXTILineConfig(IRQLOW_EXTI_PORT, IRQLOW_EXTI_PIN);
// Configure EXTI line
EXTI_InitStructure.EXTI_Line = IRQLOW_EXTI_LINE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// Configure, but do not enable, EXTI Interrupt
NVIC_InitStructure.NVIC_IRQChannel = IRQLOW_EXTI_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE;
NVIC_Init(&NVIC_InitStructure);
The IRQ handlers are set up as so:
void EXTI3_IRQHandler(void)
{
IRQHigh();
EXTI_ClearITPendingBit(IRQHIGH_EXTI_LINE);
}
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(IRQLOW_EXTI_LINE) == SET)
{
if (IRQLow()) // This returns a non-zero value if an overflow happened
{
CleanUpOverflow();
}
// Clear interrupt bit.
EXTI_ClearITPendingBit(IRQLOW_EXTI_LINE);
}
else // unknown EXTI source
{
ErrorHandler(ERR_UNKNOWN_EXTI15_10_IRQ); // This never happens
}
}
A few things of note:
-
IRQHigh() and IRQLow() each take significant time to complete (which is why I want one to interrupt the other)
-
IRQLow is not initially enabled, but is enabled down the line with
NVIC_EnableIRQ(IRQLOW_EXTI_IRQn);
-
Within EXTI15_10_IRQHandler(), I'm getting a return value from IRQLow().
-
I have declared the xxx_IRQHandler() functions with and without
__attribute__ ((interrupt ("IRQ")))
. I understand that this attribute isn't necessary with Cortex-M3, but I tried anyway (and got the same results).
What am I doing wrong?
Update: With help from a comment by @Jeroen3, I've discovered that IRQLow is interrupting IRQHigh. Now I need to find out why…
Best Answer
The priorities are not being initialized correctly.
The code in the question initializes the NVIC like so:
Then, later, IRQLow is explicitly enabled with
NVIC_EnableIRQ()
.The problem is that the
NVIC_Init()
function (from the SPL) doesn't actually initialize anything (!) if IRQChannelCmd = DISABLE. It simply sets the ICER register to disable that interrupt.This seems like a bug to me, but so be it. Now I need to go and check the other
xxx_Init()
functions in the SPL and see if anything else might bite me later :)There are a few solutions. The one I prefer is to leave out all five lines of the IRQLow init sequence and replace them with this:
Then, when it is time,
NVIC_EnableIRQ()
can be used as intended.Note that this works because the NVIC is configured with
NVIC_PriorityGroup_4
(no subpriorities). Different PriorityGroup settings would require you'd to configure the subpriority, too.