STM32F1与STM32CubeIDE快速入门中断NVIC与EXTI概述
STM32F1与STM32CubeIDE快速入门-中断、NVIC与EXTI概述
中断、NVIC与EXTI概述
在本文中,我们将讨论 ARM cortex 中断/异常,以及优先级的工作原理。 如何生成中断以及 CPU 如何将上下文切换到 ISR 并返回到主应用程序。 以及如何在STM32Cube IDE中正确配置 NVIC 和 EXTI 并编写高效的中断服务例程处理程序 (ISR) 代码所需的一切。

1、STM32中断介绍
ARM v7 Core 支持多种用于处理异常和中断的强大功能。 其中包括嵌套向量中断控制器 (NVIC)。微编码架构使中断堆栈、进入和退出在硬件中自动完成。 这从 CPU 上卸载了这项工作开销。中断架构和优先级非常灵活且高度可配置以支持 RTOS。STM32中断功能框架如下:

1)STM32微控制器运行模式
当发生异常时,STM32微控制器模式可以改变。 它可以处于以下模式之一:
- 线程模式:在重置时进入。
- 处理程序模式:在所有其他异常时进入。

2)微编码(Micro-Coded)中断
中断入口和出口是硬件实现的,以减少延迟并加快响应速度。
- 硬件自动保存和恢复处理器上下文
- 硬件允许延迟确定最高优先级的挂起中断
- 硬件允许在不完全恢复/保存处理器上下文的情况下处理另一个挂起的中断(此功能称为尾链(Tail-Chaining))
3)异常类型
异常可以由各种事件触发,包括:
- 内部异常:由系统内部源而非任何外部硬件或外围设备触发的异常。 这包括:
- 重置:这是当处理器退出重置状态时执行的处理程序例程,无论来源是什么。
- **系统服务调用 (SVC)**:类似于其他 ARM 内核上的 SVC 指令。 它允许非特权软件进行系统调用。 这为关键系统功能提供保护。
- **挂起系统调用 (PendSV)**:与 SVC 一起运行以简化 RTOS 开发,因为它旨在成为 RTOS 使用的中断。
- **不可屏蔽中断 (NMI)**:顾名思义,不能禁用此中断。 如果其他异常处理程序发生错误,则会触发 NMI。 除重置异常外,它在所有异常中具有最高优先级。
- 外部异常

4)故障异常
一些系统异常用于发出信号并处理特定故障。 故障异常有几个类别,其中包括:
- 总线故障
- 使用故障
- 内存管理故障
- 硬故障
5)异常服务模型
在复位状态,所有中断都被禁用。 处理器开始以低于最低可编程优先级的基本执行优先级执行代码指令,因此任何启用的中断都可以抢占处理器。
当启用的中断被置位时,相应的ISR处理程序会为该中断提供服务。 处理器以中断的执行优先级运行处理程序。 当ISR完成时,恢复原来的优先级。
当启用的中断以较低或相等的优先级置位时,中断将挂起运行。
中断嵌套总是启用的,要禁用它,只需将所有中断设置为相同的优先级。
6)异常处理流程
- 当异常发生时,当前指令流停止,处理器访问异常向量表。
- 该异常的向量地址是从向量表中加载的。
- 异常处理程序开始在处理程序模式下执行。
- 异常处理程序返回到 main(假设没有进一步的嵌套)。

7)重置(复位)流程
- 发生复位时(复位输入有效)。
- MSP(主堆栈指针)寄存器从地址 0x00 加载初始值。
- 复位处理程序地址从地址 0x04 加载。
- 重置处理程序在线程模式下执行。
- 重置处理程序分支到主程序。
8)异常状态
每个异常都可以处于以下状态之一:
- Inactive:未挂起也不活动。
- Pending:异常事件已被触发,但处理程序尚未执行。
- Active:异常处理程序已经开始执行,但还没有结束。 中断嵌套允许一个异常中断另一个异常处理程序的执行。 在这种情况下,两个异常都处于活动状态。
- Active And Pending:处理器正在处理异常,并且存在来自同一源的未决异常。

2、中断尾链(Tail-Chaining)加速
当中断(异常)被触发时,主(前台)代码上下文被保存(推送)到堆栈中,处理器分支到相应的中断向量以开始执行 ISR 处理程序。 在 ISR 结束时,堆栈中保存的上下文被弹出,以便处理器可以恢复主(前台)代码指令。 但是,如果新的异常已经挂起,则上下文推送和弹出将被跳过。 并且处理器处理第二个ISR没有任何额外的开销。 这称为“尾链”。
在Cortex-M3/M4处理器上需要6个周期。 这是性能上的巨大加速,并大大提高了中断响应时间(减少了中断延迟)。 下面是一个示例,说明当 CPU 在服务第一个中断请求(IRQ1)时收到第二个中断请求(IRQ2)时会发生什么。

3、中断延迟到达加速
ARM 内核可以在另一个异常的“异常进入阶段”(堆栈调用者寄存器和获取要执行的ISR例程向量)中检测到更高优先级的异常。 在此期间检测到“迟到”中断。 可以获取和执行更高优先级的 ISR,但可以跳过已经完成的上下文保存。 这减少了更高优先级中断的延迟,并且在延迟到达的异常处理程序完成后,处理器可以尾链到将要服务的初始异常(低优先级)。

即使在异常条目序列开始之后,挂起的高优先级异常也会在已经挂起的低优先级异常之前处理。 优先级较低的异常在优先级较高的异常之后处理。
4、中断抢占先机
当任务被放弃(被中断)以处理异常时,就会发生抢占。 当前运行的指令流被称为被抢占。 当具有相同优先级的多个异常未决时,首先处理具有最低异常编号的异常。 一旦异常处于活动状态并由处理器处理,只有具有更高优先级的异常才能抢占它。
考虑以下示例,其中以不同的优先级触发了3个异常/中断。IRQ1抢占IRQ2并强制IRQ3挂起直到IRQ1完成。IRQ1 ISR完成后ISR2 在 IRQ1 抢占它时从它停止的地方继续。 最后,在 ISR2 完成后,ISR3 开始执行。 并将上下文恢复到主程序(前台)

5、中断向量表

表中的第一个条目(最低地址)包含初始 MSP。 所有其他地址都包含到异常处理程序 (ISR) 开始的向量(地址),每个地址都是 4 字节宽。 该表最多有 496 个外部中断,具体取决于每个特定目标的实现。
通过改变向量表偏移寄存器的值,可以很容易地在内存中重新定位中断向量表。 中断/异常向量表通常位于启动代码文件中。
1 | ; Vector Table Mapped to Address 0 at Reset |
STM32F10xx的中断向量表如下:

6、中断堆栈、ISR、返回
1)中断堆栈(上下文保存)
主程序栈用于存储接收中断前的程序状态。 堆栈指针 (SP) 在推送操作时自动递减,并且始终指向非空值。 堆栈是 8 字节对齐的,如果需要,可以插入填充。
接收中断信号时:
- a)只要不是多周期指令,处理器就会完成当前指令
- b)处理器可能会放弃多周期指令来处理异常/中断,然后它会重新启动被放弃的多周期指令。 或者,处理器可以停止多周期指令并去处理异常,然后返回继续执行。 多周期指令包括:除法、乘法、加载/存储双精度等。
- c)处理器状态(上下文)会自动保存到堆栈中。 压入八个寄存器(PC、R0-R3、R12、LR、xPSR)。
- d)在上下文保存期间或之后,从异常/中断向量表中加载相应 ISR 的地址。
- e)链接寄存器修改为中断后返回。
- ISR 的第一条指令开始由 CPU 执行。 对于 Cortex-M3/M4,此过程所需的整个延迟为 12 个周期。 但是,如果发生延迟到达或尾链,则 IRQ 延迟会得到改善。
2)中断服务程序 (ISR) 处理
如果需要,ISR 处理程序应该清除中断源(有些不需要像 SysTick 那样清除)。
中断嵌套不会影响 ISR 的写入方式,但应注意可能发生的主堆栈溢出。
ISR C 代码应该以清晰的方式编写,易于处理器阅读和执行。 鉴于每秒要处理数百或数千次某些异常/中断。 因此它必须运行得如此之快,并且在 ISR 处理程序中不允许延迟,除非它是几微秒并且背后有强有力的推理和理由。
3)从 ISR 返回(上下文恢复)
当异常 (ISR) 处理程序完全执行并且没有其他中断待处理时,CPU 将恢复主(前台)应用程序代码的上下文。提取并执行 EXC_RETURN 指令以恢复 PC 并弹出 CPU 寄存器。
如果有其他中断挂起,则优先服务最高优先级,放弃上下文恢复以加速中断响应。 这是前面讨论的尾链功能。
如果上下文恢复过程被中断,它就会被放弃。 并且新的 ISR 无需保存上下文即可开始执行,因为它已经被压入堆栈。
ARM Cortex-M3/M4 上的中断返回(上下文恢复)需要 10 个时钟周期。
7、异常优先级概述
基本系统执行优先级低于最低可编程优先级。因此,任何启用的中断在被触发时都会抢占主代码执行。之后,将执行相应的 ISR。
软件中有几种方法可以更改主代码执行优先级,使其高于线程模式或当前活动异常的默认优先级。这个过程称为优先级提升。它在许多情况下都具有优势,尤其是在 RTOS 中。当您需要在不被任何源中断的情况下执行某些逻辑时。
内部和外部异常表可在数据表中找到。一些异常/中断具有固定的优先级,不能以编程方式更改。并且优先级编号越低,优先级越高。优先级存储在一个字节宽的寄存器中,该寄存器在复位时被清除 (0x00)。如果两个或多个异常/中断具有相同的优先级值,则优先级顺序因此根据异常编号本身确定(较低的异常编号具有较高的优先级)。
异常优先级如下表:

8、中断优先级和优先级分组
每个异常/中断都关联了一个 8 位优先级寄存器。 但并非所有位都用于设置优先级。 比如,STM32F103C8 MCU 只有 16 个优先级,这意味着 4 个 MSB 位用于设置优先级。 如果需要,这些位可以分为两组,您可以在其中为每个抢占优先级创建子优先级。 仅当组优先级相同时才使用子优先级。
当两个或更多相同优先级中断发生时,子优先级很有用。 然后,将首先处理具有更高子优先级的那个。 如果两个异常/中断的优先级完全相同,则首先处理向量编号较低的异常/中断。

9、EXTI(外部中断)控制器
外部中断/事件控制器由连接线设备中的多达 20 个边缘检测器或其他设备中的 19 个边缘检测器组成,用于生成事件/中断请求。 每条输入线都可以独立配置以选择类型(事件或中断)和相应的触发事件(上升或下降或两者兼有)。 每条线也可以独立屏蔽。 挂起寄存器维护中断请求的状态线。
EXTI控制器的主要特点如下:
- 每个中断/事件线上的独立触发和屏蔽
- 每条中断线的专用状态位
- 生成多达 20 个软件事件/中断请求
- 检测脉冲宽度低于 APB2 时钟周期的外部信号

要产生中断,应配置并启用中断线。 这是通过使用所需的边沿检测对两个触发寄存器进行编程,并通过向中断屏蔽寄存器中的相应位写入“1”来启用中断请求来实现的。 当外部中断线上出现所选边沿时,产生中断请求。 对应于中断线的挂起位也被设置。 该请求通过在待处理寄存器中写入“1”来复位。
- 要将这 20 条线路配置为中断源,请使用以下步骤:
- 配置 20 条中断线的屏蔽位 (EXTI_IMR)
- 配置中断线的触发选择位(EXTI_RTSR 和 EXTI_FTSR)
- 配置控制映射到外部中断控制器 (EXTI) 的 NVIC IRQ 通道的启用和屏蔽位,以便可以正确确认来自 20 条线路之一的中断
EXTI 外部中断 GPIO 映射如下:
GPIO 通过以下方式连接到 16 个外部中断/事件线:

其他四条 EXTI 线路的连接方式如下:
- EXTI线16连接到 PVD 输出
- EXTI线17接RTC报警事件
- EXTI线18接USB唤醒事件
- EXTI线19连接到以太网唤醒事件
文章来源: https://iotsmart.blog.csdn.net/article/details/123196987