boot compile
ARM Cortex M
NVIC(Nested Vectored Interrupt Controller) & ISR(Interrupt Service Routine)
在 ARM Cortex-M 架构中,NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)与 ISR(Interrupt Service Routine,中断服务函数)构成了整个嵌入式系统实时响应和事件处理的核心硬件与软件基石
NVIC
NVIC 是 Cortex-M 处理器内核的一个紧密耦合组件(Core-Coupled),它直接与内核相连,从而实现了极低的中断响应延迟。
- 核心特性
- 嵌套支持(Nested):支持中断嵌套。高优先级的中断可以打断正在执行的低优先级中断服务函数(ISR)。
- 向量化中断(Vectored):硬件直接根据中断源查找对应的中断向量(即 ISR 地址),无需软件编写复杂的查询代码(如传统 8051 的轮询或单一入口分发)。
- 自动保存上下文(Hardware Stacking):当进入中断时,硬件会自动将核心寄存器(R0-R3、R12、LR、PC、xPSR)压入堆栈,退出时自动弹出,极大简化了汇编/C语言的中断编写。
- 尾链技术(Tail-Chaining):当一个中断紧接着另一个中断发生时,硬件跳过弹栈和压栈的过程,直接切换到下一个 ISR,显著减少了中断切换开销。
- 迟到中断(Late Arrival):如果一个高优先级中断在一个低优先级中断进入压栈阶段时到达,NVIC 会自动优先处理高优先级中断。
- NVIC 的优先级管理机制:
Cortex-M 的 NVIC 支持可配置的中断优先级。优先级的数值越小,逻辑优先级越高
优先级分组(PRIGROUP):
中断优先级寄存器(IPR)通常将 8 位寄存器的高几位用于配置优先级。Cortex-M 通过 AIRCR 寄存器的 PRIGROUP 字段将优先级划分为两部分:
- 抢占优先级(Preemption Priority):决定是否可以发生中断嵌套。高抢占优先级可以打断低抢占优先级的中断。
- 子优先级(Subpriority):当多个相同抢占优先级的中断同时发生时,子优先级高的先响应;但子优先级不能互相嵌套。
优先级配置示例:
通常使用 CMSIS 标准库函数进行配置:
// 设置优先级分组(例如:抢占优先级占 3 位,子优先级占 1 位) |
ISR
ISR 是当特定中断触发时由 CPU 执行的实际代码段。
- 完整的生命周期流程
| 阶段 | 动作 | 说明 |
|---|---|---|
| 1. 触发与采样 | 外设产生中断信号,中断标志位置位 | NVIC 采样到信号。 |
| 2. 仲裁与向量获取 | 硬件优先级比较与寻址 | NVIC 比较当前活动中断的优先级,从中断向量表(Vector Table)中读取对应 ISR 的入口地址。 |
| 3. 自动压栈 | 硬件保护现场 | “硬件自动将 R0-R3, R12, LR, PC, xPSR 压入当前堆栈(PSP 或 MSP)。” |
| 4. 执行 ISR | 运行用户代码 | 执行用户编写的 xxx_IRQHandler() 函数。 |
| 5. 退出与恢复 | EXC_RETURN 触发 | ISR 执行完毕,通过特殊的返回机制(如触发异常返回值 EXC_RETURN),硬件自动出栈,恢复现场,CPU 返回被打断的主程序或低优先级中断。 |
IRQ Pending
清除中断挂起状态(Clear Pending State)
当一个中断触发时,硬件会将其标记为“挂起(Pending)”状态,表示“有一个中断正在排队等待 CPU 处理”。执行 ClearPending 操作就是强行将这个挂起标志清除(清零),让中断控制器认为该中断没有发生过,或者不再排队等待。
为什么需要手动清除挂起标志?
在正常情况下,当 CPU 进入中断服务函数(ISR)时,硬件通常会自动清除该中断的挂起标志。但在以下特殊场景中,开发者需要手动调用清除挂起或使用相关 API:
- 在使能中断前防止“历史遗留”中断触发:
如果在代码中动态开启某个外设或外部中断(使用 NVIC_EnableIRQ),而在此之前引脚上已经产生过脉冲或噪声,中断控制器里可能已经残留了一个“挂起”的标志。此时如果直接开中断,程序会立刻跳进中断服务函数。因此,在开启中断前通常会先调用清除挂起函数(如 NVIC_ClearPendingIRQ)来“洗白”状态。 - 软件主动触发或手动控制中断流:
在某些多任务操作系统(如 FreeRTOS 的 PendSV 或 SysTick 处理)或复杂的软件触发中断逻辑中,可能需要通过软件置位(SetPending)或清零(ClearPending)来精细控制中断的调度和响应。 - 外设状态改变后的清理:
当外设的硬件标志位未能在进入中断时由硬件自动完全清除,或者由于时序问题导致中断重复触发时,软件干预清除挂起可以避免死循环或误触发。
中断关闭
关闭全局中断的底层内联汇编函数
__STATIC_FORCEINLINE void __disable_irq(void) |
核心指令:cpsid i
代码中最关键的部分是内联汇编字符串 “cpsid i”:
CPS:Change Processor State(改变处理器状态)。
ID:Interrupt Disable(禁用中断)。
i:代表 IRQ(Interrupt Request,中断请求)。
把它们连起来看,cpsid i 的字面意思就是:“修改处理器状态,禁用所有可屏蔽的 IRQ 中断”。
当处理器执行到这行指令时,硬件层面的 PRIMASK 寄存器 会被置为 1。在 ARM Cortex-M 架构中,当 PRIMASK = 1 时,所有具有可配置优先级的异常和中断(即普通的硬件中断)都会被屏蔽,CPU 将不再响应外部的中断请求。修饰符的含义
__ASM(或 __asm)
告诉编译器:“接下来我要写汇编代码了,请用汇编器来处理。”volatile
告诉编译器:“不要对这段汇编代码进行任何优化!”
如果没有 volatile,编译器可能会认为这行代码对 C 变量的值没有直接影响,从而在代码优化时把它删掉或重排位置,这在底层驱动中是灾难性的。: : : “memory”(Clobber List / 破坏描述符)
告诉编译器:这条汇编指令执行后,可能会改变内存中的数据状态。
这会强制编译器在执行这行汇编之前,把寄存器里缓存的变量值写回内存(Flush Memory),防止中断关闭期间发生数据不一致。

