This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Cortex-M4 interrupt priority dynamically change while in ISR

Hello,

I am using STM32F407ZGT6 Cortex-M4 microcontroller. I am interested in changing the priority of a certain interrupt while servicing the same interrupt. Is that possible?

More elaborately, lets say I have an interrupt which has a priority of 4. The interrupt has arrived and I am currently in the ISR servicing that interrupt. After running a certain code (in the ISR), I want to run the remaining code (in the ISR) to run at higher priority (say 2). And when I was about to leave the ISR, I again change its priority to 4. Is this possible?

Currently, I have achieved this by calling a higher priority interrupt inside the lower priority interrupt.

Thanking in anticipation for your time and help.

Parents
  • Many thanks for the response.

    Well, I think I was unable to properly explain the issue.

    Let's suppose I have following ISRs:

    IRQHandler_0

    {

        // priority = 0 (highest priority)

    }


    IRQHandler_1

    {

        // priority = 1

    }


    IRQHandler_2

    {

        // priority = 2

    }


    IRQHandler_3

    {

        // priority = 3

    }


    IRQHandler_4

    {

        // priority = 4

        Code A // needs to run at priority 4

        Code B // needs to run at priority 2

    }

    I am asking, is this possible that I can change the priority while in IRQHandler_4 and run a certain part at higher priority (say 2). Please note that when running the higher priority code (Code B) in the IRQHandler_4, higher priorities interrupts (such as 0 and 1) should be able to pre-empt the IRQHandler_4 and come back to Code B with the same higher priority of 2. But when running Code B, lower priority interrupts (such as 2, 3, 4) should not be able to pre-empt Code B.

    BASEPRI / PRIMASK registers prevent other low priority exceptions. I don't think, they will be useful in my case.

    Thanking in anticipation for your time and help.

Reply
  • Many thanks for the response.

    Well, I think I was unable to properly explain the issue.

    Let's suppose I have following ISRs:

    IRQHandler_0

    {

        // priority = 0 (highest priority)

    }


    IRQHandler_1

    {

        // priority = 1

    }


    IRQHandler_2

    {

        // priority = 2

    }


    IRQHandler_3

    {

        // priority = 3

    }


    IRQHandler_4

    {

        // priority = 4

        Code A // needs to run at priority 4

        Code B // needs to run at priority 2

    }

    I am asking, is this possible that I can change the priority while in IRQHandler_4 and run a certain part at higher priority (say 2). Please note that when running the higher priority code (Code B) in the IRQHandler_4, higher priorities interrupts (such as 0 and 1) should be able to pre-empt the IRQHandler_4 and come back to Code B with the same higher priority of 2. But when running Code B, lower priority interrupts (such as 2, 3, 4) should not be able to pre-empt Code B.

    BASEPRI / PRIMASK registers prevent other low priority exceptions. I don't think, they will be useful in my case.

    Thanking in anticipation for your time and help.

Children
  • Well, when I would do as follows.

    Before entering the "Code A", I do not interfere in the priority of the current interrupt.

    Before entering the "Code B", I change the current interrupt priority to 2.

    Have you tried it? what happened?

  • Thanks for your detailed reply. It seems to me that you are misinterpreting that higher priority values mean higher priorities. Please note that low priority values represent higher priorities (which means 0 is the highest priority here). Please correct me if I am wrong.

    So, I am trying to change the priority level of a running interrupt to a higher priority.

    Thanks!!

  • Thanks for your reply.

    I've not tried it yet, I am interested in knowing the definite answer. However, I achieved the same by calling a higher priority interrupt inside a lower priority interrupt.

    Thanks!!

  • (Sorry for the confusion, I've cleaned up this reply)

    I think the article Re: Changing interrupt priority to prevent nesting may be what you're looking for.

    You've probably already read A Beginner’s Guide on Interrupt Latency - and Interrupt Latency of the ARM® Cortex®-M processors - but if not, it's an excellent article.

    But if you still want to change the priority level in the middle of your interrupt, I believe it's possible.

    Typically you would only use an approach like this in a multitasking environment or with very simple high-speed applications.

    You could use a svc handler and make the svc handler modify the priority, then call the svc handler from within your interrupt...

    ...or you could set the priority level of the svc handler to a higher priority, then do a svcall and place the critical section inside your svc handler.

    I think both jyiu and chrisshore may have some valuable input on this.

  • > BASEPRI / PRIMASK registers prevent other low priority exceptions. I don't think, they will be useful in my case.

    Wouldn't changing the priority also prevent other low priority exceptions as well ?

    As far as I understood it, this is how the priority mechanism actually works (but I may be wrong).

    Have you tried ...

    IRQHandler_4

    {

       Code A

       dump contents of BASEPRI

       execute SVC, which has priority 2

       dump contents of BASEPRI

    }

    SVC

    {

    dump contents of BASEPRI

    }

    ... (try making a short test-program, which does nothing except from the above and trigger IRQHandler_4 once only.

    -So using __set_BASEPRI(priority) for setting / restoring it inside the interrupt would have the same effect; you should not have to call/trigger a higher priority interrupt manually.

  • Thanks for the reply!

    I am still unable to understand why I need to use BASEPRI. Because SVC has a priority higher than IRQHanlder_4, so the SVC interrupt will easily pre-empt the IRQHandler_4 and I don't need to change BASEPRI. Please correct me if I am wrong.

    Any ways, the idea of using SVC is as similar as I am already achieving by calling a higher priority (software) interrupt inside IRQHandler_4. Please correct me if I am wrong.

    Many thanks for your time and help!

  • jyiu Can I've a definite answer please!

    Many thanks!!

  • I simply practically implemented the following code:

    IRQHandler_4

    {

         // priority = 4

         Code A // needs to run at priority 4

         Change IRQHandler_4 to higher priority 2

         Code B // needs to run at priority 2

         Change back IRQHandler_4 to lower priority 4

    }

    And it runs perfectly. Code A runs at lower priority 4 and Code B at higher priority 2.

    Thus, in ARM Cortex-M4, the interrupt priority can be dynamically changed anywhere, even inside the interrupt handler (which is servicing the interrupt)

  • Sorry for delay of my reply.

    Glad to know that you have find the answer. Yes, Cortex-M3 and Cortex-M4 supports dynamic changing of the priority level.

    Please note that this is not supported in ARMv6-M (Cortex-M0/M0+/M1). For ARMv6-M, you should only change the priority level of a interrupt when it is inactive and ideally disable it first, change the priority level, and then reenable it.

    Normally we expect users to use BASEPRI to block the lower priority level interrupts.

    For example, when you change the priority level of IRQHandler_4 to 2:

      NVIC_SetPriority(PORT0_4_IRQn, 0x2);

    You can get the same effect by:

      __set_BASEPRI(0x2 << (8 - __NVIC_PRIO_BITS));

    But at the end of the ISR, when you set the priority level back to 4:

      NVIC_SetPriority(PORT0_4_IRQn, 0x4);

    If you are using BASEPRI, you should be restoring it to orginal value (e.g. 0). Otherwise it could block other lower priority level interrupts.

    for example, your ISR could look like:

    void IRQ4_Handler(void)

    {

    uint32_t last_basepri;

      // Run code A

      ...

      last_basepri = __get_BASEPRI();

      __set_BASEPRI(0x2 << (8 - __NVIC_PRIO_BITS));

      // Run code B

      ...

      __set_BASEPRI(last_basepri);

      return;

    }

    Comparing between using IRQ priority level and BASEPRI, there is one difference:

    The function code using BASEPRI (function_X below) can be used by both an IRQ handler as well as in Thread, and can be shared by multiple IRQ handlers:

    void function_X(void)

    {

    uint32_t last_basepri;

      // Run code A

      ...

      last_basepri = __get_BASEPRI();

      __set_BASEPRI(0x2 << (8 - __NVIC_PRIO_BITS));

      // Run code B at level 2

      ...

      __set_BASEPRI(last_basepri);

      return;

    }

    Change the priority level of an active IRQ cannot work in Thread (because the IRQ isn't active at that stage), and if the code need to be shared by multiple ISRs, you need to detect which IRQ it is serving to change the right priority level setting.

    Hope this helps.

    regards,

    Joseph

  • Thanks a lot for the detailed reply. It has cleared up many confusions.