如何在Cortex M0的CMSIS中实现中断处理程序?


9

我有一个LPC1114套件。最近几天,我一直在研究Cortex M0的CMSIS实现,以发现其中的工作方式。到目前为止,我了解了每个寄存器的映射方式以及如何访问它们。但是我仍然不知道如何在其中实现中断。我对CMSIS中的中断所了解的只是在启动文件中提到了一些中断处理程序名称。而且我可以通过简单地使用启动文件中提到的相同名称编写C函数来编写自己的处理程序。令我感到困惑的是,在用户指南中,所有的GPIO都可以用作外部中断源。但是启动文件中仅提及4个PIO中断。所以告诉我:

  1. 如何为其他GPIO实现外部中断处理程序?
  2. CMSIS中的中断表在哪里映射?
  3. NVIC和AVR / PIC中的中断实现之间的主要区别是什么?(除了NVIC可以映射到闪存中的任何位置)

Answers:


14

除Igor的出色回答外,还提供以下信息。

从C编程的角度来看,中断处理程序在cr_startup_xxx.c文件(例如,LPC1343的cr_startup_lpc13.c文件)中定义。在那里将所有可能的中断处理程序定义为WEAK别名。如果您没有为中断源定义自己的XXX_Handler(),则将使用此文件中定义的默认中断处理函数。链接器将从cr_startup_xxx.c选出要包含在最终二进制文件中的函数以及中断向量表

来自端口的GPIO中断的示例在gpio.c的演示文件中显示。每个GPIO端口有一个中断输入到NVIC。可以启用/禁用端口中的每个位,以在该端口上产生中断。例如,如果需要在端口PIO1_4和PIO1_5上进行中断,则可以在GPIO0IE中启用各个PIO1_4和PIO1_5中断位。当您的PIOINT0_Handler()中断处理函数触发时,您可以通过读取GPIO0RIS寄存器并适当地处理中断来确定是哪个PIO1_4中断或PIO1_5(或两个中断)挂起。


10

(请注意,第1点和第2点是实现细节,而不是体系结构限制。)

  1. 在较大的NXP芯片(例如LPC17xx)中,有几个专用的中断引脚(EINTn)具有自己的中断处理程序。其余的GPIO必须使用一个公共中断(EINT3)。然后,您可以查询中断状态寄存器,以查看触发中断的引脚。
  2. 我对LPC11xx不太熟悉,但似乎每个GPIO端口都有一个中断。您将再次必须检查状态寄存器以找出特定的引脚。还有多达12个引脚可以用作唤醒源。我不确定您是否可以劫持它们作为常规中断(即它们可能仅在睡眠状态下才被触发)。
  3. 默认处理程序表位于地址0(在闪存中)。第一个条目是SP寄存器的复位值,第二个条目是复位向量,其他条目是其他异常和中断向量。前几个是由ARM修复的(例如NMI和HardFault),其余的是特定于芯片的。如果需要在运行时更改向量,则可以将其重新映射到RAM(首先需要复制表)。在LPC11xx中,重新映射固定在SRAM(0x10000000)的开头,其他芯片可以更灵活。
  4. NVIC已针对有效的中断处理进行了优化:
    • 每个中断的可编程优先级为0-3。较高优先级的中断优先于较低优先级的中断(嵌套)。当较高优先级的中断完成后,将继续执行较低优先级的中断。
    • 在中断进入时自动堆叠处理器状态;这允许直接在C语言中编写中断处理程序,并且不需要汇编包装。
    • 尾链:不再立即弹出并推送状态,而是立即处理下一个挂起的中断
    • 较晚到达:如果在堆栈处理器状态时出现了更高优先级的中断,则该中断将立即执行,而不是先前未决的中断。

既然您熟悉PIC,请查看此应用笔记: 从PIC单片机移植到Cortex-M3

它是关于M3的,但是大多数要点也适用于M0。


8

奥斯丁和伊戈尔的答案足够详细。但是,我想用另一种方式回答它,也许您发现它有所帮助。

LPC11xx(Cortex-M0)的GPIO引脚有4个电平,从GPIO0.0到GPIO0.n的所有引脚共享相同的中断号,从GPIO3.0到GPIO3.m的所有引脚共享相同的中断号。

在LPC11xx中初始化GPIO中断有六个步骤

  1. 通过修改引脚连接块寄存器来设置引脚功能。
  2. 通过修改GPIO数据方向寄存器设置引脚方向(输入默认值)。
  3. 为每个单独的引脚设置中断,您必须转到GPIO中断屏蔽寄存器GPIOnIE并将该位(对应于该引脚)设置为1。
  4. 通过修改GPIO中断检测寄存器GPIOnIBE和GPIOnIS,将中断设置为上升沿或下降沿,或同时设置为上升沿和下降沿。
  5. 使用CMSIS功能在嵌套向量中断控制中启用PIO_0 / PIO_1 / PIO_2 / PIO_3的中断源。
  6. 通过使用CMSIS功能设置中断优先级。

代码实现。您需要两个函数:一个函数在上述步骤中初始化6个,第二个函数是中断处理程序,该函数必须与启动代码startup_LPC11xx.sfile中定义的处理程序同名。名称是从PIOINT0_IRQHandlerPIOINT3_IRQHandler。如果使用其他名称,则必须在启动文件中更改名称。

/*Init the GPIO pin for interrupt control */
void GPIO_Init(){
    LPC_IOCON-> =..              //Pin configuration register
    LPC_GPIO1->FIODIR = ...      //GPIO Data direction register
    LPC_GPIO1->FIOMASK = ..      //GPIO Data mask register - choose  the right pin
    LPC_GPIO1->GPIOnIE = ..      //Set up falling or rising edge 
    NVIC_EnableIRQ(PIO_1);       //Call API to enable interrupt in NVIC
    NVIC_SetPriority(PriorityN); //Set priority if needed
}


/*Must have the same name as listed in start-up file startup_LPC11xx.s */
void PIOINT1_IRQHandler(void){
   //Do something here
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.