M0+/M4中断优先级设置问题(Tips about the interrupt priority of M0+/M4)

Document created by Calvin Ji Employee on Jan 22, 2014
Version 1Show Document
  • View in full screen mode

        在我们嵌入式工程应用中,中断作为最常用的异步手段是必不可少的,而且在一个应用程序中,一个中断往往是不够用的,多个中断混合使用甚至多级中断嵌套也经常会使用到,而这样就涉及到一个中断优先级的问题。

        以我们最熟悉的Cortex-M系列为例,我们知道ARM从Cortex-M系列开始引入了NVIC的概念(Nested Vectors Interrupts Controller),即嵌套向量中断控制器,以它为核心通过一张中断向量表来控制系统中断功能,NVIC可以提供以下几个功能:

1)可嵌套中断支持;

2)向量中断支持;

3)动态优先级调整支持;

4)中断可屏蔽。

        抛开其他不谈,这里我们只说说中断优先级的问题。我们知道NVIC的核心工作原理即是对一张中断向量表的维护上,其中M4最多支持240+16个中断向量,M0+则最多支持32+16个中断向量,而这些中断向量默认的优先级则是向量号越小的优先级越高,即从小到大,优先级是递减的。但是我们肯定不会满足于默认的状态(人往往不满足于约束,换句俗话说就是不喜欢按套路出牌,呵呵),而NVIC则恰恰提供了这种灵活性,即支持动态优先级调整,无论是M0+还是M4除了3个中断向量之外(复位、NMI和HardFault,他们的中断优先级为负数,它们3个的优先级是最高的且不可更改),其他中断向量都是可以动态调整的。

        不过需要注意的是,中断向量表的前16个为内核级中断,之后的为外部中断,而内核级中断和外部中断的优先级则是由两套不同的寄存器组来控制的,其中内核级中断由SCB_SHPRx寄存器来控制(M0+为SCB_SHPR[2:3],M4为SCB_SHPR[1:3]),外部中断则由NVIC_IPRx来控制(M0+为NVIC_IPR[0:7],M4为NVIC_IPR[0:59]),如下图所示:

M0+中断优先级寄存器:

image

16877808079858.png

M4中断优先级寄存器:

image

16877829919049.png

        其中M4所支持的动态优先级范围为0~15(8位中只有高四位[7:4]才有效),而M0+所支持的动态优先级范围则为0~3(8位中只有高两位[7:6]才有效),而且秉承着号越小优先级越高的原则(0最高,15或3为最小),同时也间接解释了为什么复位(-3)、NMI(-2)和HardFault(-1)优先级最高的原因,很简单,人家都是负的了,谁还能比他们高,呵呵,而且这三位中复位优先级最高,NMI其次,HardFault最低(这个最低仅限于这三者)。

下面给出个ARM CMSIS库中关于M0+和M4中断优先级设置的API函数NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)实现供大家来参考:

M0+:

NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)

{

  if(IRQn < 0) {

    SCB->SHP[_SHP_IDX(IRQn)] = (SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFF << _BIT_SHIFT(IRQn))) |

        (((priority << (8 - __NVIC_PRIO_BITS)) & 0xFF) << _BIT_SHIFT(IRQn)); }  /* set Priority for Cortex-M  System Interrupts */

  else {

    NVIC->IP[_IP_IDX(IRQn)] = (NVIC->IP[_IP_IDX(IRQn)] & ~(0xFF << _BIT_SHIFT(IRQn))) |

        (((priority << (8 - __NVIC_PRIO_BITS)) & 0xFF) << _BIT_SHIFT(IRQn)); }   /* set Priority for device specific Interrupts  */

}

M4:

void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)

{

  if(IRQn < 0) {

    SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M  System Interrupts */

  else {

    NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }        /* set Priority for device specific Interrupts  */

}

Attachments

    Outcomes