×
单片机 > 单片机程序设计 > 详情

LPC1114外部中断

发布时间:2020-06-06 发布时间:
|
外部中断作为处理器响应外部事件的通道,在控制系统中起着非常重要的作用。下面就来讨论一下LPC1114外部中断的使用情况。

LPC1114的每一个引脚都可以响应一个外部中断,所以有多少个引脚就有多少个外部中断。但LPC1114的中断系统非常强大,外部中断只是它其中的一小部分。因此,要用好外部中断,就必须先来了解LPC1114的整个中断系统。下面就来看一下它的中断系统。

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

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

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

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

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

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

typedef struct
{
  __IO uint32_t ISER[1];                 /*!< Offset: 0x000 (R/W)  Interrupt Set Enable Register           */
       uint32_t RESERVED0[31];
  __IO uint32_t ICER[1];                 /*!< Offset: 0x080 (R/W)  Interrupt Clear Enable Register          */
       uint32_t RSERVED1[31];
  __IO uint32_t ISPR[1];                 /*!< Offset: 0x100 (R/W)  Interrupt Set Pending Register           */
       uint32_t RESERVED2[31];
  __IO uint32_t ICPR[1];                 /*!< Offset: 0x180 (R/W)  Interrupt Clear Pending Register         */
       uint32_t RESERVED3[31];
       uint32_t RESERVED4[64];
  __IO uint32_t IP[8];                   /*!< Offset: 0x300 (R/W)  Interrupt Priority Register              */
}  NVIC_Type;

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

#define SCS_BASE            (0xE000E000UL)                            /*!< System Control Space Base Address */
#define NVIC_BASE           (SCS_BASE +  0x0100UL)                    /*!< NVIC Base Address                 */
#define NVIC                ((NVIC_Type      *)     NVIC_BASE     )   /*!< NVIC configuration struct          */

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

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

typedef enum IRQn
{
/******  Cortex-M0 Processor Exceptions Numbers ***************************************************/
  NonMaskableInt_IRQn           = -14,      /*!< 2 Non Maskable Interrupt                         */
  HardFault_IRQn                = -13,      /*!< 3 Cortex-M0 Hard Fault Interrupt                 */
  SVCall_IRQn                   = -5,       /*!< 11 Cortex-M0 SV Call Interrupt                   */
  PendSV_IRQn                   = -2,       /*!< 14 Cortex-M0 Pend SV Interrupt                   */
  SysTick_IRQn                  = -1,       /*!< 15 Cortex-M0 System Tick Interrupt               */
/******  LPC11xx/LPC11Cxx Specific Interrupt Numbers **********************************************/
  WAKEUP0_IRQn                  = 0,        /*!< All I/O pins can be used as wakeup source.       */
  WAKEUP1_IRQn                  = 1,        /*!< There are 13 pins in total for LPC11xx           */
  WAKEUP2_IRQn                  = 2,
  WAKEUP3_IRQn                  = 3,
  WAKEUP4_IRQn                  = 4,   
  WAKEUP5_IRQn                  = 5,        
  WAKEUP6_IRQn                  = 6,        
  WAKEUP7_IRQn                  = 7,        
  WAKEUP8_IRQn                  = 8,        
  WAKEUP9_IRQn                  = 9,        
  WAKEUP10_IRQn                 = 10,       
  WAKEUP11_IRQn                 = 11,       
  WAKEUP12_IRQn                 = 12,       
  CAN_IRQn                      = 13,       /*!< CAN Interrupt                                    */
  SSP1_IRQn                     = 14,       /*!< SSP1 Interrupt                                   */
  I2C_IRQn                      = 15,       /*!< I2C Interrupt                                    */
  TIMER_16_0_IRQn               = 16,       /*!< 16-bit Timer0 Interrupt                          */
  TIMER_16_1_IRQn               = 17,       /*!< 16-bit Timer1 Interrupt                          */
  TIMER_32_0_IRQn               = 18,       /*!< 32-bit Timer0 Interrupt                          */
  TIMER_32_1_IRQn               = 19,       /*!< 32-bit Timer1 Interrupt                          */
  SSP0_IRQn                     = 20,       /*!< SSP0 Interrupt                                   */
  UART_IRQn                     = 21,       /*!< UART Interrupt                                   */
  Reserved0_IRQn                = 22,       /*!< Reserved Interrupt                               */
  Reserved1_IRQn                = 23,       
  ADC_IRQn                      = 24,       /*!< A/D Converter Interrupt                          */
  WDT_IRQn                      = 25,       /*!< Watchdog timer Interrupt                         */  
  BOD_IRQn                      = 26,       /*!< Brown Out Detect(BOD) Interrupt                  */
  FMC_IRQn                      = 27,       /*!< Flash Memory Controller Interrupt                */
  EINT3_IRQn                    = 28,       /*!< External Interrupt 3 Interrupt                   */
  EINT2_IRQn                    = 29,       /*!< External Interrupt 2 Interrupt                   */
  EINT1_IRQn                    = 30,       /*!< External Interrupt 1 Interrupt                   */
  EINT0_IRQn                    = 31,       /*!< External Interrupt 0 Interrupt                   */
} IRQn_Type;

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

1.允许某个中断或异常

static __INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)
{
  NVIC->ISER[0] = (1 << ((uint32_t)(IRQn) & 0x1F));
}

2.禁止某个中断或异常

static __INLINE void NVIC_DisableIRQ(IRQn_Type IRQn)
{
  NVIC->ICER[0] = (1 << ((uint32_t)(IRQn) & 0x1F));
}

3.读取某个中断或异常的挂起状态

static __INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn)
{
  return((uint32_t) ((NVIC->ISPR[0] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0));
}

4.把某个中断或异常的挂起状态设为1

static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn)
{
  NVIC->ISPR[0] = (1 << ((uint32_t)(IRQn) & 0x1F));
}

5.把某个中断或异常的挂起状态清为0

static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)
{
  NVIC->ICPR[0] = (1 << ((uint32_t)(IRQn) & 0x1F)); /* Clear pending interrupt */
}

6.把某个中断或异常的可配置优先级设为1

static __INLINE void 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)); }
  else {
    NVIC->IP[_IP_IDX(IRQn)] = (NVIC->IP[_IP_IDX(IRQn)] & ~(0xFF << _BIT_SHIFT(IRQn))) |
        (((priority << (8 - __NVIC_PRIO_BITS)) & 0xFF) << _BIT_SHIFT(IRQn)); }
}

7.读取某个中断或异常的优先级

static __INLINE uint32_t NVIC_GetPriority(IRQn_Type IRQn)
{

  if(IRQn < 0) {
    return((uint32_t)((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) >> (8 - __NVIC_PRIO_BITS)));  } /* get priority for Cortex-M0 system interrupts */
  else {
    return((uint32_t)((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) >> (8 - __NVIC_PRIO_BITS)));  } /* get priority for device specific interrupts  */
}

8.复位NVIC

static __INLINE void NVIC_SystemReset(void)
{
  __DSB();                                                     /* Ensure all outstanding memory accesses included buffered write are completed before reset */
  SCB->AIRCR  = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | SCB_AIRCR_SYSRESETREQ_Msk);
  __DSB();                                                     /* Ensure completion of memory access */
  while(1);                                                    /* wait until reset */
}

在上述函数中有几点要说明一下,一是数组的引用其取值只能是0(即第一个元素),这是因为在结构体定义中只定义了一个数组元素,且由于需要利用数组的地址连续性来对映CPU物理地址,所以也不能将其定义为一个普通变量;二是关键字“__INLINE ”在头文件core_cm0.h中已做了宏定义“#define __INLINE  __inline”,__inline是通知编译器其后面的函数为内联形式;三是中断源IRQn要与0x1F与一下,是为了屏蔽高27位的值,因为中断源的最大值只到31,所以只用了32位中的低5位(31的二进制是11111,十六进制是0x1F);四是在函数的参数中,由于引入了枚举类型,所以可以在调用函数的时候,在参数部分可直接使用枚举中的名称,这样就可以省去记忆32个中断源在32位寄存器中的对应位置,便于书写和阅读。例如,要开启端口0的外部中断,执行程序“NVIC_EnableIRQ(EINT0_IRQn)”即可。

上述就是LPC1114中的整个中断系统,即“内嵌套向量中断控制器”。可以看出,它控制着整个处理器32路中断源的使能与挂起等8个动作,功能非常强大。但做为外部中断的端口中断源却只有4个,即EINT0_IRQn、EINT1_IRQn、EINT2_IRQn、EINT3_IRQn四个。而每一个端口又对应有12个引脚(端口3为6个)又都可以产生外部中断,那怎么来判断是那个引脚上申请的中断呢?这就需要借助前面“通用输入/输出端口”部分介绍过的MIS寄存器了。在外部中断响应的服务程序内,判别MIS寄存器的各个位,值为1的位所对应的就是触发本次外部中断的引脚。

和所有的单片机一样,在中断响应后,程序指针会跳转到相应的中断向量入口处去执行中断服务程序,而在C语言中则是以特定形式的中断入口函数来呈现。比如EINT0_IRQn、EINT1_IRQn、EINT2_IRQn、EINT3_IRQn四个端口的外部中断入口函数分别如下:

void PIOINT0_IRQHandler(void)
{
  端口0的中断服务程序部分
}
void PIOINT1_IRQHandler(void)
{
  端口1的中断服务程序部分
}
void PIOINT2_IRQHandler(void)
{
  端口2的中断服务程序部分
}
void PIOINT3_IRQHandler(void)
{
  端口3的中断服务程序部分
}
上述函数的名称是不能改变的,它标志着特定的中断入口,除了四个外部中断以外的其它中断源,也有各自的中断入口函数,它们都位于起动文件“startup_LPC11xx.s”中,在以后用到时再讨论,这里就不给出了。

下面来讨论一个外部中断的例子,要求使用外部中断来实现按键控制LED的亮灭。程序代码如下(假设KEY接在GPIO1.9,LED接在GPIO1.0):

#include
//=================端口1的外部中断服务程序=====================
void PIOINT1_IRQHandler(void)
{
 if((LPC_GPIO1->MIS&0x200)==0x200)                              //检测是否是GPIO1.9引脚上的中断
  {
   LPC_GPIO1->MASKED_ACCESS[1] = 0;                        //开启LED
   while(LPC_GPIO1->MASKED_ACCESS[512] != 0x200); //等待GPIO1.9引脚按键释放
   LPC_GPIO1->MASKED_ACCESS[1] = 1;                        //关闭LED
  }
 LPC_GPIO1->IC |= 0x200;                                                  //清除GPIO1.9引脚上的中断标志
}
//==========================主程序============================
int main(void)
{
 LPC_SYSCON->SYSAHBCLKCTRL |= (1<<16);      //使能IOCON时钟
 LPC_IOCON->R_PIO1_0 = 0XD1;                           //把芯片上的33脚设置为GPIO1.0功能
 LPC_SYSCON->SYSAHBCLKCTRL &= ~(1<<16);  //禁能IOCON时钟
 LPC_GPIO1->DIR &= ~(1<<9);                               //设置GPIO1.9为输入方向
 LPC_GPIO1->DIR |= (1<<0);                                   //设置GPIO1.0为输出方向
 LPC_GPIO1->MASKED_ACCESS[1]  = 1;              //输出高电平,关闭LED
 LPC_GPIO1->IS &= ~(1<<9);                                 //选择中断为边沿触发
 LPC_GPIO1->IEV &= ~(1<<9);                               //选择下降沿触发
 LPC_GPIO1->IE |= (1<<9);                                    //设置中断P1.9不被屏蔽
 NVIC_EnableIRQ(EINT1_IRQn);                            //使能GPIO1中断
 while(1)
 {
  ;
 }
}

把上述程序编译后下载到LPC1114中,给系统上电,可以看出在按下KEY后LED亮,放开KEY后LED灭,达到了使用外部中断控制的目的。

最后说明一点,如果需要打开或关闭中断“总中断”,可调用“__enable_irq();和__disable_irq();”来实现,它们是通过调用汇编语言来实现这一操作的,具体的原型在头文件“core_cmFunc.h”中,可自行查看,这里就不详述了。




『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
STM32中断向量表的位置.重定向