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

电器工程 手臂 中断
2022-01-19 17:04:26

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

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

以下信息是 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(或两者)中断处于待处理状态。

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

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

由于您熟悉 PIC,请查看此应用说明: 从 PIC 微控制器迁移到 Cortex-M3

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

奥斯汀和伊戈尔的答案足够详细。但是,我想以另一种方式回答它,也许您觉得它有帮助。

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

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

  1. 通过修改 Pin Connection Block Registers 来设置 pin 功能。
  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
}