下面让我们在Kinetis KL26芯片上来实际操作一把吧。KL26芯片为ARM Cortex M0+内核。
  硬件:FRDM_KL26Z
  软件:FRDM-KL26Z_SC_Rev_1.0 + IAR7.4
1) 先做第一个实验:程序中开启了两个定时器,一个为TPM2,周期10ms,另外一个PIT,周期1s。TPM2中断中断里打印一个字符X,PIT中断里while(1)循环,不断打印~~~~~~~。
int main (void)
{
    char ch;
    
    printf("\n\r\n\r*** Running the NVIC_Test project ***\n\r");
    
    enable_irq(INT_TPM2-16);
    init_TPM2();
    
    enable_irq(INT_PIT-16);
    init_PIT();
    
    while(1)
    {
      ch = in_char();
      out_char(ch);
    } 
}
 /*   10 ms     */
void init_TPM2(void)
{
  SIM_SCGC6 |= (SIM_SCGC6_TPM2_MASK );
  SIM_SOPT2 |= SIM_SOPT2_TPMSRC(1);           //input clock source is MCGPLL/2=24MHz
  TPM2_SC = TPM_SC_DMA_MASK | TPM_SC_PS(3);   //Divide by 3M=24M/8
  TPM2_CNT = 0;
  TPM2_MOD = 30000;                //timer interrupt triger with 100Hz
  TPM2_SC |= TPM_SC_TOIE_MASK;    // enable Interrupt
  TPM2_SC |= TPM_SC_CMOD(1);    //start the counter
}
void TPM2_IRQHandler(void)
{   
  if (TPM2_SC & TPM_SC_TOF_MASK ) 
      TPM2_SC |=  TPM_SC_TOF_MASK;   // Clear Flag     
   printf("X"); 
}
 /*   1 s     */
void init_PIT(void) 
{
    SIM_SCGC6 |= SIM_SCGC6_PIT_MASK;                                
    PIT_MCR &= ~(1 << PIT_MCR_MDIS_SHIFT);                                                                 
    PIT_LDVAL0 = 24000000;          // 1 hz                                     
    PIT_TFLG0 |= PIT_TFLG_TIF_MASK;                                    
    PIT_TCTRL0 |= PIT_TCTRL_TIE_MASK | PIT_TCTRL_TEN_MASK;              
}
void PIT_IRQHandler(void) 
{
  if(PIT_TFLG0 & PIT_TFLG_TIF_MASK) 
     PIT_TFLG0 |= PIT_TFLG_TIF_MASK;  // Clear Flag                                 
   while(1)
   {
     printf("~~~~~~~~~~~~~~~"); 
   }
}
该实验现象如下:

刚开始一直打印X,也就是不断进入TPM2中断服务,然后就一直打印~,即进到PIT中断里,就再也出不来了。
出现该现象的原因是:TPM2和PIT的中断优先级是一样的(默认都是0),所以当进入到PIT中断服务后,由于while(1)原因,即使TPM2中断来了也无法响应。
2)让我们做第二个实验,加入优先级设置,将TPM2 的优先级设置为1,PIT的优先级设置为2。数字越小,表示优先级越高。
    enable_irq(INT_TPM2-16);
    set_irq_priority(INT_TPM2-16,1);
    init_TPM2();
    
    enable_irq(INT_PIT-16);
    set_irq_priority(INT_PIT-16,2);
    init_PIT();
理论来说TPM2中断会打算PIT中断,可是当我们观察实验现象时却发现,还是一直在PIT中断里,和第一个试验想象没有区别。

这到底是怎么回事??? 理论与实际不符啊!!!!!
经过调试才发现,原来是代码里的set_irq_priority 函数压根就没起作用,有图为证:
这是调用set_irq_priority 函数之前的寄存器值:

这是执行完set_irq_priotiy 函数之后的寄存器值:

该函数为什么不起作用呢?问题出在最后两条语句上,原因在于Cortex-M0/M0+ 中的NVIC寄存器只支持字传输,不能单字节的去操作。
void set_irq_priority (int irq, int prio)
{   
    /*irq priority pointer*/
    uint8 *prio_reg;
    uint8 err = 0;
    uint8 p = 0;
    
    /* Make sure that the IRQ is an allowable number. Right now up to 32 is 
     * used.
     *
     * NOTE: If you are using the interrupt definitions from the header
     * file, you MUST SUBTRACT 16!!!
     */
    if (irq > 32)
    {
        printf("\nERR! Invalid IRQ value passed to priority irq function!\n");
        err = 1;
    }
    if (prio > 3)
    {
        printf("\nERR! Invalid priority value passed to priority irq function!\n");
        err = 1;
    }
    
    if (err != 1)
    {
        /* Determine which of the NVICIPx corresponds to the irq */
        p = irq / 4;
        prio_reg = (uint8 *)((uint32)&NVIC_IP(p));
        /* Assign priority to IRQ */
        *prio_reg = ( (prio&0x3) << (8 - ARM_INTERRUPT_LEVEL_BITS) );             
    }
}
针对这个问题,有两种途径去解决:
1) 修改set_irq_priority 函数,这个写法不唯一,有一点必须注意的是必须按字操作,也就是一下写四字节数据。
void set_irq_priority (int irq, int prio)
{   
    /*irq priority pointer*/
    //uint8 *prio_reg;
    uint32 prio_reg;  // wenxue
    uint8 err = 0;
    uint8 p = 0;
    
    /* Make sure that the IRQ is an allowable number. Right now up to 32 is 
     * used.
     *
     * NOTE: If you are using the interrupt definitions from the header
     * file, you MUST SUBTRACT 16!!!
     */
    if (irq > 32)
    {
        printf("\nERR! Invalid IRQ value passed to priority irq function!\n");
        err = 1;
    }
    if (prio > 3)
    {
        printf("\nERR! Invalid priority value passed to priority irq function!\n");
        err = 1;
    }
    
    if (err != 1)
    {
        /* Determine which of the NVICIPx corresponds to the irq */
        p = irq / 4;
      //  prio_reg = (uint8 *)((uint32)&NVIC_IP(p));
        prio_reg = *(uint32 *)((uint32)&NVIC_IP(p));
        
        prio_reg = prio_reg  & ( ~(0xFFUL <<  ((irq &  0x03UL) * 8UL)) );
          
        prio_reg =  prio_reg |  (((prio << (8 - 2)) & (uint32_t)0xFFUL) << ((irq &  0x03UL) * 8UL) );
        
        *(uint32 *)((uint32)&NVIC_IP(p)) =prio_reg ;
                     
    }
}
2) 最简单最可靠的是调用CMSIS 标准接口函数NVIC_SetPriority
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if((int32_t)(IRQn) < 0) {
    SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) |
       (((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn)));
  }
  else {
    NVIC->IP[_IP_IDX(IRQn)]  = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)]  & ~(0xFFUL << _BIT_SHIFT(IRQn))) |
       (((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn)));
  }
}
修改完set_irq_priority 函数之后,我们观察调试结果,这时优先级确实做了改变

再次观察最后的实验现象,结果如下:

我们可以看到TPM2中断会不断打断PIT中断。
总结如下:
1) 大家在使用FRDM-KL26Z_SC_Rev_1.0 代码时,注意set_irq_priority 函数有bug的,它起不了实际作用。
2) ARM Cortex-M0/M0+中断抢占只需要设置好优先级即可,优先级高的会抢占优先级低的中断。
补充说明:
Cortex-M3/M4里面涉及到一个优先级分组概念,在Cortex-M0/M3 里面是没有的。
上述测试代码如下:http://www.eeskill.com/file/id/40480