5
\$\begingroup\$

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:

  1. IRQHigh() and IRQLow() each take significant time to complete (which is why I want one to interrupt the other)

  2. IRQLow is not initially enabled, but is enabled down the line with NVIC_EnableIRQ(IRQLOW_EXTI_IRQn);

  3. Within EXTI15_10_IRQHandler(), I'm getting a return value from IRQLow().

  4. 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...

\$\endgroup\$
  • \$\begingroup\$ Your ISR is very short, it could actually be tail-chaining them. How certain are you they occur together? Look at NVIC->IABR. \$\endgroup\$ – Jeroen3 Sep 12 '17 at 5:40
  • \$\begingroup\$ I don't see anything wrong in what you have shown. What is in CleanUpOverflow(); ... Is there anything in there that would disable interrupts? \$\endgroup\$ – Tut Sep 12 '17 at 13:41
  • \$\begingroup\$ @Jeroen3 Thanks for that - I'll check when I get into the office. Both ISR handlers call a function (ISRHigh() and ISRLow()), each taking significant time to complete. I've update the question to mention this. \$\endgroup\$ – bitsmack Sep 12 '17 at 13:46
  • \$\begingroup\$ Oops, I missed ISRLow(). I should have asked: Is there anything that disables/enables interrupts in ISRLow() or CleanUpOverflow() that could be delaying execution of your higher priority interrupt. \$\endgroup\$ – Tut Sep 12 '17 at 13:55
  • \$\begingroup\$ Also (doubt this is related) is CC1101_INT_EXTI_LINE the same as IRQLOW_EXTI_LINE? \$\endgroup\$ – Tut Sep 12 '17 at 13:59
5
\$\begingroup\$

The priorities are not being initialized correctly.

The code in the question initializes the NVIC like so:

// IRQHigh, enabled immediately
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, to be enabled later:
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);

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:

NVIC_SetPriority(IRQLOW_EXTI_IRQn, 5);

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.

\$\endgroup\$
  • 2
    \$\begingroup\$ +1 ... Good find and it's the same in the SPL for STM32F4 (I just checked the source code for v1.8.0). I need to review some code! \$\endgroup\$ – Tut Sep 12 '17 at 19:21
  • 1
    \$\begingroup\$ Better avoid HAL & SPL. Especially the latter. \$\endgroup\$ – P__J__ Sep 12 '17 at 20:26
  • 1
    \$\begingroup\$ @PeterJ_01 It's funny; I was using the SPL as a lower-level, less bloated alternative to STM32Cube :) \$\endgroup\$ – bitsmack Sep 12 '17 at 20:42
  • \$\begingroup\$ @bitsmack what is "low level" in the SPL. This is older version version of HAL (abandoned by the STM). CubeMx is only the HAL init code generator - nothing else. You have just chosen similar but the older (and more buggy) libraries (you have gone from the bad to the worse). Low level definitions and functions you have in the CMSIS \$\endgroup\$ – P__J__ Sep 12 '17 at 20:45
  • 1
    \$\begingroup\$ FYI: This may be moot since you are using NVIC_PriororityGroup_4, but CMSIS provides NVIC_EncodePriority() which accepts as arguments: PriorityGroup, PreemptPriority and SubPriority. "The returned priority value can be used for NVIC_SetPriority(...) function". (found in core_cm4.h) \$\endgroup\$ – Tut Sep 13 '17 at 10:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.