LPC824-中断系统NVIC

发布时间:2023-05-25  

LPC824的中断系统非常强大,要用好中断,就必须先了解LPC824的整个中断系统。下面来讨论一下NVIC中断系统。


在LPC8xx系列处理器中,有一个部分被称为“私有外设总线”(Private peripheral bus),它位于Memory map中地址为0xE0000000~0xE0100000的地方,包含有下表中的几个核心外设。

其中的NVIC(Nested Vectored Interrupt Contorller)就是中断系统,被称为“内嵌套向量中断控制器”。它与处理器内核紧密耦合,可实现低中断延迟及对新中断的有效处理。

它具有以下特征:

拥有32路向量中断;每个中断的优先级均可编程设置为0~192(步长64),数值越小优先级越高,0级为最高优先级;支持电平和边沿触发中断;支持中断尾链;拥有一个外部不可屏蔽中断NMI。


NVIC所涉及到的寄存器如下表所示。

从表中可以看出,每个寄存器都是32位的结构,都具有可读可写的属性,复位值都为全0。其中ISER0寄存器是设置中断的使能,32位对应32路中断,值为1使能中断,值为0不使能中断。ICER0寄存器是设置中断的禁能,32位对应32路中断,值为1禁能中断,值为0不禁能。ISPR0寄存器是设置中断的挂起,32位对应32路中断,值为1挂起,值为0不挂起。ICPR0寄存器是清除中断的挂起,32位对应32路中断,值为1清除挂起,值为0不清除挂起。IPR0~7寄存器是设置中断优先级。


下面是NVIC寄存器组所对应的结构体形式(位于头文件core_cm0plus.h中)。

typedef struct
{
  __IOM uint32_t ISER[1U];     
        uint32_t RESERVED0[31U];
  __IOM uint32_t ICER[1U];     
        uint32_t RSERVED1[31U];
  __IOM uint32_t ISPR[1U];     
        uint32_t RESERVED2[31U];
  __IOM uint32_t ICPR[1U];     
        uint32_t RESERVED3[31U];
        uint32_t RESERVED4[64U];
  __IOM uint32_t IP[8U];       
}  NVIC_Type;

因NVIC寄存器组的基址为0xE000E100,所以要将基址指针强制转换为上述结构体,还必须要加上下面的定义。

#define SCS_BASE            (0xE000E000UL)
#define NVIC_BASE           (SCS_BASE + 0x0100UL)
#define NVIC                ((NVIC_Type *) NVIC_BASE )

接下来给出的是上面NVIC32位寄存器所对应的32路中断向量的中断源。

 

为了能描述上面的32路中断源,在C语言中运用了枚举类型,代码如下所示(位于头文件lpc82x.h中)。

typedef enum {
/*---Cortex-M0PLUS Processor Exceptions Numbers---*/
  Reset_IRQn                    = -15,
  NonMaskableInt_IRQn           = -14,
  HardFault_IRQn                = -13,
  SVCall_IRQn                   =  -5,
  DebugMonitor_IRQn             =  -4,
  PendSV_IRQn                   =  -2,
  SysTick_IRQn                  =  -1,
/*---LPC82x Specific Interrupt Numbers---*/
  SPI0_IRQn                     =   0,
  SPI1_IRQn                     =   1,
  UART0_IRQn                    =   3,
  UART1_IRQn                    =   4,
  UART2_IRQn                    =   5,
  I2C1_IRQn                     =   7,
  I2C0_IRQn                     =   8,
  SCT_IRQn                      =   9,
  MRT_IRQn                      =  10,
  CMP_IRQn                      =  11,
  WDT_IRQn                      =  12,
  BOD_IRQn                      =  13,
  FLASH_IRQn                    =  14,
  WKT_IRQn                      =  15,
  ADC_SEQA_IRQn                 =  16,
  ADC_SEQB_IRQn                 =  17,
  ADC_THCMP_IRQn                =  18,
  ADC_OVR_IRQn                  =  19,
  DMA_IRQn                      =  20,
  I2C2_IRQn                     =  21,
  I2C3_IRQn                     =  22,
  PIN_INT0_IRQn                 =  24,
  PIN_INT1_IRQn                 =  25,
  PIN_INT2_IRQn                 =  26,
  PIN_INT3_IRQn                 =  27,
  PIN_INT4_IRQn                 =  28,
  PIN_INT5_IRQn                 =  29,
  PIN_INT6_IRQn                 =  30,
  PIN_INT7_IRQn                 =  31
} IRQn_Type;

从上述代码中可以看出,除了32路中断源外,还加入了编号为负数的、优先级更高的7个中断源。这里先不进行说明,在后面用到时再来讨论。定义好上述代码后,就可以来写中断所需要的函数了。下面就是依据CMSIS规范所定义的13个中断操作函数(位于头文件core_cm0plus.h中)。 

1.允许某个中断
__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
  }
}
2.读取某个中断的使能状态
__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    return((uint32_t)(((NVIC->ISER[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
  }
  else
  {
    return(0U);
  }
}
3.禁止某个中断
__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
    __DSB();
    __ISB();
  }
}
4.读取某个中断的挂起状态
__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
  }
  else
  {
    return(0U);
  }
}
5.把某个中断的挂起状态设为1
__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
  }
}
6.把某个中断的挂起状态清为0
__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
  }
}
7.把某个中断的可配置优先级设为1
__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if ((int32_t)(IRQn) >= 0)
  {
    NVIC->IP[_IP_IDX(IRQn)]  = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)]  & ~(0xFFUL << _BIT_SHIFT(IRQn))) |
       (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn)));
  }
  else
  {
    SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) |
       (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn)));
  }
}
8.读取某个中断的优先级
__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS)));
  }
  else
  {
    return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS)));
  }
}
9.编码优先级
__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority)
{
  uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);
  uint32_t PreemptPriorityBits;
  uint32_t SubPriorityBits;
  PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp);
  SubPriorityBits     = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS));
  return (
           ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) |
           ((SubPriority     & (uint32_t)((1UL << (SubPriorityBits    )) - 1UL)))
         );
}
10.解码优先级
__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority)
{
  uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);
  uint32_t PreemptPriorityBits;
  uint32_t SubPriorityBits;
  PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp);
  SubPriorityBits     = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS));
  *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL);
  *pSubPriority     = (Priority                   ) & (uint32_t)((1UL << (SubPriorityBits    )) - 1UL);
}
11.设置中断向量
__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector)
{
#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U)
  uint32_t *vectors = (uint32_t *)SCB->VTOR;
#else
    uint32_t *vectors = (uint32_t *)0x0U;
#endif
  vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector;
}
12.读取中断向量
__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn)
{
#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U)
  uint32_t *vectors = (uint32_t *)SCB->VTOR;
#else
  uint32_t *vectors = (uint32_t *)0x0U;
#endif
  return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET];
}
13.复位NVIC
__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void)
{
  __DSB();
  SCB->AIRCR  = ((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | SCB_AIRCR_SYSRESETREQ_Msk);
  __DSB();
  for(;;)
  {
    __NOP();
  }
}

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    可能位于内存条正面。 比如下面这张图,通过CPU-Z就可以读取到这款内存的SPD信息,也能看到这款内存的一些参数。包括频率、颗粒制造商、型号、生产日期、时序、频率等。 4.2内存马甲 对了,内存......
    Active Video)和场前肩(Ver Front Porch)这四个参数,场时序的时序图如下图所示 需要注意的有三点: 1、行时序是以”像素”为单位的, 场时序是以”行”为单位的。 2、VGA......
    (0x0000,0000-0x4000,0000),8个bank,每个bank_size为128M。理论上需要2^30(30条地址线)来确定是哪个bank,哪个地址。但是实际上只用到了27条,那么是怎么确定是哪个......
    (0x0000,0000-0x4000,0000),8个bank,每个bank_size为128M。理论上需要2^30(30条地址线)来确定是哪个bank,哪个地址。但是实际上只用到了27条,那么是怎么确定是哪个......
    音量 5、Input Mux 因为不同的板子的麦克风通道不同(在uda1341),用同一驱动,想录音时应用程序应该设置input mux选项。 表明它能选择哪个麦克风通道,当前是哪一个麦克风通道,设置......
    预装载寄存器 他有两个参数,一个设置是哪个通用定时器,一个是使能,比较简单,这里直接设置: TIM_OC2PreloadConfig(TIM2,TIM_OCPreload_Enable); 那么......
    往下面找一下终于会发现点儿不同: 在这里我们发现串口发送调用了一个不同的函数。秘密就在这个函数里: HAL_UART_Transmit_IT 这个函数有三个参数: UART_HandleTypeDef *huart......
    的中断方式呢? 好,我们往下面找一下终于会发现点儿不同: 在这里我们发现串口发送调用了一个不同的函数。秘密就在这个函数里: HAL_UART_Transmit_IT 这个函数有三个参数......
    如果你使用的是ADC1,那么对应的DMA就应该是DMA1的通道1 如果使用的是ADC2或者是ADC3就需要自己去查找手册,看看对应的是哪个DMA的那个通道。 在后面上传的程序中可以看到DMA初始......
    type)做动态内存申请。 第1个参数填写内存区首地址,比如申请的AppMallocDTCM,就填AppMallocDTCM即可。 第2个参数填写申请的字节大小,单位字节。 第3个参数固定填0即可......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>