关于NVIC使用以及M0和M3的异同

前言:目标

1NVIC的示意图和核心点介绍

2可嵌套向量中断控制器NVIC如何使用

3 M0M3的操作有什么异同

4中断向量表的位置

5系统复位时(或者不设置中断时),初始状态是什么

6可嵌套的合理解释

 

  1. NVIC核心点介绍

         NVIC可以看做是CPU的得力干将,负责了CPU所有的外设中断,而内部的异常中断则是有SCB系统控制块来管理

        ARM cortexM3内核共支持256个中断(16个内核+240个中断)和可编程256级中断优先级设置,其中STM32和NXP并没有使用cortexM3内核全部的东西,STM32目前支持的中断共有84个(16个内核+68个外设 ISER0-ISER2),和16级可编程中断优先级(IPR0-IPR15,高四位有效),NXP1788目前支持的中断有56个(16个内核+40个外设(ISER0-ISER1),IPR0-IPR10),和32级可编程中断优先级(IPR高5位有效)

 

中断优先级的概念都是针对“中断通道”的(因为每一个中断通道都对应于一个中断源和中断事件),当该中断通道的优先级确定后,也就确定了该外围设备的中断优先级,并且该设备所能产生的所有类型的中断都享有相同的通道中断优先级。(中断通道其实就是把每一个中断编个号,便于管理)

        对于Cortex 内核要知道几个关键词:可嵌套,优先级(组优先级,子优先级)

 

  1. 可嵌套向量中断控制器NVIC如何使用

首先针对M3 这里主要是以LPC1788为例

  1.        M3 NVIC有哪些寄存器和中断控制函数

ISER0-ISER3      RW中断设置使能寄存器

ICER0-ICER3      RW中断清除使能寄存器

ISPR0-ISPR3      RW中断设置挂起寄存器

ICPR0-ICPR3      RW中断设置清除寄存器

IABR0-IABR3      RO

IPR0 -IPR27      RW中断优先级寄存器其中1788只用到其中的0-10,优先级设置只用到高5

STIR             WO可配置

CMSIS中断控制函数

static __INLINE void NVIC_SetPriorityGrouping(uint32_tPriorityGroup)   设置优先级分组

static __INLINE uint32_t NVIC_GetPriorityGrouping(void)              

static __INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)                     使能中断

static __INLINE void NVIC_DisableIRQ(IRQn_Type IRQn)                    禁能中断

static __INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn)             如果中断挂起,返回IRQ序号

static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn)                 设置IRQ挂起

static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)               清除IRQ挂起状态

static __INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn)                 返回有效中断的IRQ序号

static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_tpriority)设置IRQn优先级

static __INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn)               读取IRQn优先级

static __INLINE void NVIC_SystemReset(void)                             复位系统

 

static__INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_tPreemptPriority, uint32_t SubPriority)

static__INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup,uint32_t* pPreemptPriority, uint32_t* pSubPriority)

static__INLINE uint32_t SysTick_Config(uint32_t ticks)

 

  1. 如何开关中断例程

例程:针对M3

#define PRIORITY_GROP  5             //分组5[7:6]表示抢占式优先级   [5:3]表示子优先级

NVIC_SetPriorityGrouping(PRIORITY_GROP);

 

LPC_SSP0->DMACR = 0x03;              /*使能SSP0TXRXDMA功能      */

NVIC_SetPriority(DMA_IRQn, 17);       /*设置优先级10 001               */

NVIC_EnableIRQ(DMA_IRQn);

 

例程:针对M0这里M0内核似乎没有主优先级的概念,即不能设置优先级组

LPC_USART->IER  =IER_RBR;            /* Enable the RDAinterrupt      */          

NVIC_EnableIRQ(UART_IRQn);        

NVIC_SetPriority(UART_IRQn,1);

     

  1. 代码解析,细节处理

首先是M3

2.3.1   优先级分组:分配组PRIO,子PRIO

写在前面:

  1. LPC1788地址0xE000 0000 -0xE00F FFFF 在系统存储器映射当中,这段地址标注的是私有外设总线,该区包含了NVIC,系统节拍定时器系统控制模块
  2. 设置系统的组优先级主要涉及的SCB中的AIRCR寄存器

对于应用中断和复位控制寄存器的描述是  AIRCR提供了异常模型的优先级分组控制,数据访问的字节序状态,以及系统的复位控制

如要对此寄存器执行写操作,必须将0X05VA写入VECTKEY域,否则写不进去

 

SCB->AIRCR指系统控制模块中的 SCB(0xE000 E008)AIRCR-application interrupt and reset control register

//VECTKEY  [3116]   PRIGROUP[10:8]

#define SCB_AIRCR_VECTKEY_Pos              16

#define SCB_AIRCR_VECTKEY_Msk              (0xFFFFUL << SCB_AIRCR_VECTKEY_Pos)

 

#define SCB_AIRCR_PRIGROUP_Pos              8

#define SCB_AIRCR_PRIGROUP_Msk             (7UL << SCB_AIRCR_PRIGROUP_Pos)

 /*函数的目的就是在合适的位置先清零,然后再写入*/

 

static__INLINE voidNVIC_SetPriorityGrouping(uint32_tPriorityGroup)

{

  uint32_t reg_value;

         /* only values 0..7 are used          */

  uint32_t PriorityGroupTmp = (PriorityGroup& 0x07); 

  /*read old register configuration    */

  reg_value  = SCB->AIRCR;    

/* clear bits to change   */                      

  reg_value &=~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk);

  reg_value =  (reg_value                       |

                (0x5FA <

               (PriorityGroupTmp << 8));  /* Insert write key and priorty group  */

  SCB->AIRCR =  reg_value;

 

}

        现在已经完成了组优先级的设置,那么比如程序中设置的PriorityGrioup5的话,那么代表的是什么意义呢?

       先来说说NVIC中断优先级寄存器IPR0datasheet中它是一个32位的寄存器,具体位域分配是

[0:2]  保留        [7:3]   IP_WDT       

[8-10] 保留        [15:3]  IP_TIMER0

[18-16]保留        [23:19] IP_TIMER0

[26-24]保留        [31:27] IP_TIMER0

看到了对于NVIC中的IPR0寄存器,系统只用了每个Byte(32位看做是4个字节)中的高5位,而PriorityGrioup就是设置拆分这5位中那几位作为组优先级,那几位作为子优先级,datasheet上说是PriorityGrioup指示了二进制的位置,改点IPR_n域分割了独立的组优先级和子优先级,如何分割?

 PRIGROUP[10:8]

二进制点

组优先级域

子优先级域

组优先级个数

子优先级个数

2-b010

Bxxxxx.000

[7:3]

32

1

3-b011

Bxxxx.y000

[7:4]

[3]

16

2

4-b100

Bxxx.yy00

[7:5]

[4:3]

8

4

5-b101

Bxx.yyy000

[7:6]

[5:3]

4

8

6-b110

Bx.yyyy00

[7]

[6:3]

2

16

7-b111

Byyyyyy.00

[7:3]

1

32

 

       这样就清楚了,原来SCB中的AIRCR(应用中断和复位控制寄存器)中的PRIGROUP[10:8]是设置NVIC中的中断优先级IPR中的[7:3]的组和子的域分割的。如果设置5那么就是[5:3]表示子优先级,对照上表

        还有一个细节就是本来NVIC中的IPR32位,但是在程序中的结构中将IP的类型设成了uint8_tIP[240],宽度是8位,便于操作和理解,这个240个中断远远包含了178840个外设的中断

2.3.2  设置中断的优先级:包含异常中断和外设中断

写在前面:可以看出,此函数将IRQn进行了分类,大于0还是小于0,其实当IRQ<0是代表是系统异常中断,只能用SCB中的SHP(系统异常处理优先级寄存器),其实这部分实际中优先级是不动的,设置的话一般用来设置systick,而NVIC->IP是用来设置外设的中断的优先级

datasheet上是:

存储器管理故障       PRI_4管理

总线故障            PRI_5管理

使用故障            PRI_6管理

SVCall             PRI_11管理

PendSV             PRI_14管理

SysTick            PRI_15管理

 

#define__NVIC_PRIO_BITS          5   /*!< Number of Bits used for PriorityLevels          */

比如SysTick_IRQn= -1,它的中断标识用在程序中是以补码的形式纯在((uint32_t)(IRQn) & 0xF)-4 = 11,故SHP[11]是对systick的优先级操作,剩下的就是对priority的向左移动三位,还有就是对于上面的异常中断如Systick这一模块使能后,就可以设置中断优先级了,而外设还多了个

NVIC中的中断使能(外设使能,NVIC外设使能,优先级)

static__INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)

{

  if(IRQn < 0) {

   /* set Priority for Cortex-M System Interrupts */

      SCB->SHP[((uint32_t)(IRQn)& 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); }  

 else{

 /* set Priority for device specific Interrupts  */

      NVIC->IP[(uint32_t)(IRQn)]= ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); 

   }      

}

2.3.3  使能,禁能外设中断

static__INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)

{

  NVIC->ISER[((uint32_t)(IRQn) >> 5)]= (1 << ((uint32_t)(IRQn) & 0x1F)); /* enable interrupt */

}

 

static__INLINE void NVIC_DisableIRQ(IRQn_Type IRQn)

{

  NVIC->ICER[((uint32_t)(IRQn) >> 5)]= (1 << ((uint32_t)(IRQn) & 0x1F)); /* disable interrupt */

}

2.3.4  获取当前的活动的中断以及中断优先级--这个比较实用,

static__INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn)

{

  /* Return 1 if active else 0 */

 return((uint32_t)((NVIC->IABR[(uint32_t)(IRQn) >> 5] & (1<< ((uint32_t)(IRQn) & 0x1F)))?1:0));

}

static__INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn)

{

 

  if(IRQn < 0) {

/* get priority for Cortex-M system interrupts */

   return((uint32_t)(SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] >>(8 - __NVIC_PRIO_BITS)));  }  

 else{

   /*get priority for device specific interrupts */

   return((uint32_t)(NVIC->IP[(uint32_t)(IRQn)]           >> (8 -__NVIC_PRIO_BITS)));  }

 }

2.3.5 使系统复位:这个得看看

static__INLINE void NVIC_SystemReset(void)

{

  /* Ensure alloutstanding memory accesses included buffered write are completed before reset */

  __DSB(); 

  SCB->AIRCR =((0x5FA << SCB_AIRCR_VECTKEY_Pos) |

         SCB_AIRCR_SYSRESETREQ_Msk);

  __DSB(); /* Ensurecompletion of memory access */

  while(1); /* waituntil reset */

}

 

 

小结:有了上述的知识,如果再来一款芯片,关于中断这块的类比,主要看什么呢,如下

       宏观的中断即系统的中断(或者说异常)以及外设的中断,针对系统异常,主要是SCB系统控制块所管理,

看看SCB->SHP(system handler priority)的异常排列(基本上类比于外设的中断向量表)

看看SCB->AIRCR(应用中断和复位控制寄存器)的复位和组优先级的设置

然后看看NVIC的优先级域几位

然后是优先级设置,开关中断等

所有上述总结的对比,对于M0针对LPC1114这款芯片,主要SCB,NVIC

1它的SCB中没有PRIGROUP的设置,即没有组优先级的概念,

2它的SHP向量表中的SYSTICK对应的是SHP[7,],M3SHP[11]

3对于IPR中断优先级寄存器为每个中断提供了两位的优先级域,意味着优先级只能设为0-3,四种优先级级别,没有嵌套的概念

4开关中断和M3一样,只是LPC11U14中在#define __NVIC_PRIO_BITS   设置的是3应该是2,还有就是LPC1113设置的就是2,这个不理解要

查阅和问,经过查阅,就定义为LPC111U14中的优先级域为3

 

 4   系统复位时(或者不设置中断时),初始状态是什么

      针对M3而言,如果不设置中断优先级的话,就无法得知组子优先级的概念,如果优先级都不设置的啊,那么优先级先后以中断号来定

针对M0而言,本身就没有组优先级的概念,所以优先级的设置比较方面,相同的优先级以中断号来定

你可能感兴趣的:(ARM底层)