Cortex-M 内核结构
Author:余生
我们将从一个系统级性能与实时响应能力的视角深入剖析 ARM Cortex-M 内核结构与工作模式。这个角度不仅与前文所讲的 USART、I²C、SPI、ADC、DAC 等外设通信机制密切相关,更与 中断系统(NVIC)、时钟系统、总线架构(AHB/APB) 紧密耦合,是理解 32 位微控制器 “为何能高效、实时地处理外设事件” 的核心所在。
我们将以 “中断驱动的实时响应机制” 为主线,揭示 Cortex-M 如何通过其独特的内核架构(寄存器组、堆栈管理、异常模型、NVIC、总线接口)实现从 “外设产生事件” 到 “CPU 执行中断服务程序(ISR)” 这一过程的极致低延迟、高确定性与高效率。我们将层层递进,从硬件结构到软件行为,从指令周期到系统调度,力求呈现一幅完整、深入、可实践的技术图景。
一:问题的起点 —— 外设事件如何被 CPU 感知?
在前文我们详细讲解了:
- USART 接收到一个字节,产生 RXNE(接收数据寄存器非空)中断。
- ADC 转换完成,EOC(End of Conversion)标志置位,可触发中断。
- I²C 检测到总线错误或地址匹配,发出中断请求。
- 定时器溢出,触发更新中断,可用于 PWM 或周期性任务。
这些外设事件是异步发生的,CPU 不能一直轮询(Polling),否则效率极低。因此,现代微控制器采用 中断机制(Interrupt) 来实现 “事件驱动” 的高效运行。
核心问题:当一个外设(如 USART)发出中断请求时,Cortex-M 内核是如何在最短时间内响应,并跳转到对应的中断服务程序(ISR)执行的?
答案就藏在 Cortex-M 的异常模型、NVIC、堆栈机制和流水线设计中。
二: 的异常模型 —— 中断即 “异常”
Cortex-M 将所有中断和异常统一管理,统称为 “异常(Exception)”。
2.1 异常类型
类型 | 编号 | 来源 | 是否可屏蔽 |
---|---|---|---|
Reset | -3 | 芯片复位 | 否 |
NMI (Non-Maskable Interrupt) | -2 | 不可屏蔽中断 | 否 |
HardFault | -1 | 内核错误(如非法指令、总线错误) | 否 |
可配置异常 | |||
MemManage | 4 | 存储器管理错误 | 是 |
BusFault | 5 | 总线访问错误 | 是 |
UsageFault | 6 | 使用错误(如未对齐访问) | 是 |
SVCall | 11 | 系统服务调用(用于操作系统) | 是 |
Debug Monitor | 12 | 调试监控 | 是 |
PendSV | 14 | 可挂起的系统调用(OS 上下文切换) | 是 |
SysTick | 15 | 系统滴答定时器 | 是 |
外部中断(IRQ) | 16+ | 外设(USART, ADC, TIM, EXTI 等) | 是 |
关键点:
- 所有外设中断都是 IRQ(Interrupt Request),编号从 16 开始。
- Cortex-M 使用 向量表(Vector Table) 存储每个异常的入口地址。
- 响应中断 = 响应一个特定编号的异常。
三:NVIC —— 嵌套向量中断控制器
NVIC(Nested Vectored Interrupt Controller)是 Cortex-M 内核的一部分,负责所有中断的优先级管理、嵌套、挂起与响应。
3.1 中断优先级
Cortex-M 支持 可编程优先级,每个 IRQ 都可以设置一个 8 位优先级值(实际使用位数由芯片厂商决定,如 STM32 通常为 4 位,即 16 级)。
- 数值越小,优先级越高(0 最高)。
- 支持 抢占(Preemption) 和 子优先级(Subpriority):
- 高优先级中断可以抢占正在执行的低优先级 ISR。
- 相同抢占优先级的中断按子优先级或中断号顺序执行(不可抢占)。
3.2 NVIC 寄存器(STM32 示例)
寄存器 | 作用 |
---|---|
NVIC_ISER[0] | 中断使能设置寄存器(Set Enable) |
NVIC_ICER[0] | 中断使能清除寄存器(Clear Enable) |
NVIC_ISPR[0] | 中断挂起设置寄存器(Set Pending) |
NVIC_ICPR[0] | 中断挂起清除寄存器(Clear Pending) |
NVIC_IPR[0~239] | 中断优先级寄存器(每个占 8 位,通常只用高 4 位) |
配置 USART1 中断优先级(底层)
// 使能 USART1 中断
NVIC->ISER[0] |= (1 << (USART1_IRQn & 0x1F));
// 设置优先级(抢占=1, 子优先级=0)
NVIC->IPR[USART1_IRQn] = (1 << 4); // 高 4 位为抢占优先级
2
3
4
四:中断响应过程 —— 从硬件到软件的飞跃
这是 Cortex-M 实时性能的核心体现。我们以 USART 接收中断为例,完整走一遍从 “RXNE 置位” 到 “执行 ISR” 的全过程。
步骤 1:外设产生中断请求
- USART 接收到一个字节,硬件将
SR
寄存器的RXNE
位置 1。 - 如果
CR1
寄存器的RXNEIE
位使能,则 USART 向 NVIC 发出中断请求。
步骤 2:NVIC 仲裁与响应
- NVIC 检查当前中断优先级是否高于正在执行的任务(包括其他 ISR)。
- 如果可以响应,NVIC 通知内核进入 “异常进入” 流程。
步骤 3:异常进入(Exception Entry)—— 硬件自动压栈
这是 Cortex-M 的革命性设计:中断上下文保存由硬件自动完成,无需软件干预,极大减少延迟。
内核自动将以下 8 个寄存器压入当前使用的堆栈(MSP 或 PSP):
SP → | xPSR | ← 程序状态寄存器(含 N,Z,C,V,IPEND,THUMB 等)
| PC | ← 返回地址(下一条将要执行的指令)
| LR | ← 链接寄存器(异常返回地址)
| R12 |
| R3 |
| R2 |
| R1 |
| R0 | ← 通用寄存器
2
3
4
5
6
7
8
关键点:
- 仅需 6 个时钟周期(Cortex-M3/M4),即可完成 8 个寄存器的压栈。
- 这是 Cortex-M 相比传统 MCU(如 8051、AVR)在中断响应速度上的数量级优势。
步骤 4:切换堆栈与模式
Cortex-M 支持两种操作模式和两个堆栈指针:
模式 | 堆栈指针 | 用途 |
---|---|---|
线程模式(Thread Mode) | PSP(Process Stack Pointer) | 用户任务(如 main 函数) |
处理模式(Handler Mode) | MSP(Main Stack Pointer) | 异常处理(ISR) |
- 在异常进入时,内核自动切换到 Handler Mode,使用 MSP。
- 这保证了 ISR 使用独立的堆栈空间,提高可靠性。
步骤 5:获取向量地址,跳转 ISR
- 内核根据异常编号(如 USART1_IRQn = 37),查 向量表(Vector Table)。
- 向量表位于内存起始地址(通常为 0x0000_0000 或可重定位到 0x0800_0000)。
- 读取对应条目的值(即 ISR 函数地址),跳转执行。
// 向量表示例(startup_stm32f10x_hd.s)
Vectors:
DCD Reset_Handler ; 0x0000_0000
DCD NMI_Handler ; 0x0000_0004
DCD HardFault_Handler ; 0x0000_0008
...
DCD USART1_IRQHandler ; 0x0000_0074 (37 * 4)
2
3
4
5
6
7
步骤 6:执行中断服务程序(ISR)
void USART1_IRQHandler(void) {
if (USART1->SR & USART_SR_RXNE) {
uint8_t data = USART1->DR; // 读取数据
// 处理数据...
}
}
2
3
4
5
6
注意:ISR 应尽可能短小精悍,避免复杂运算或阻塞操作。
步骤 7:异常返回(Exception Return)—— 硬件自动出栈
- ISR 执行
BX LR
或POP {PC}
指令。 - 内核检测到 LR 的低 4 位为特定值(如 0xFFFF_FFF9),触发 “异常返回”。
- 硬件自动将之前压栈的 8 个寄存器弹出,恢复现场。
- 切换回 Thread Mode(如果之前是线程模式)。
- 继续执行被中断的代码。
整个中断响应 + 返回过程,硬件自动完成上下文保存与恢复,软件只需写 ISR 逻辑。
五:总线架构与中断延迟 —— AHB 与 APB 的角色
中断响应速度不仅取决于内核,还受 总线架构 影响。
5.1 典型总线结构(STM32F103)
+------------------+
| Cortex-M3 |
| 内核 |
+--------+---------+
|
| ICode Bus / DCode Bus
|
+--------v---------+
| 指令/数据 |
| AHB 总线 |
+--------+---------+
|
+---------+---------+-----------------+
| | | |
+-------v--+ +----v----+ +--v-------+ +-------v-------+
| Flash | | SRAM | | APB2 | | APB1 |
| (ICode) | | (DCode) | | (高速外设)| | (低速外设) |
| | | | | TIM1,ADC | | USART,I2C,TIMx|
+----------+ +---------+ +----------+ +---------------+
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
5.2 中断延迟的关键路径
- 外设到 NVIC:通过 APB 总线,延迟取决于 APB 时钟。
- NVIC 到内核:极短,片内连接。
- 向量表访问:通过 ICode Bus 从 Flash 读取 ISR 地址。
- 如果 Flash 无缓存,可能需要等待(STM32F1 需 6 个等待周期 @72MHz)。
- 建议:将向量表重映射到 SRAM(0x2000_0000),实现 0 等待访问。
- ISR 执行:代码在 Flash 中执行,可能受 Flash 等待周期影响。
优化建议:
- 使能 Flash 预取缓冲(Prefetch Buffer)和 ART Accelerator。
- 将关键 ISR 放入 SRAM 执行(通过
attribute((section(".sramfunc")))
)。
六:工作模式与特权级别
Cortex-M 支持两种操作模式和两种特权级别:
模式 | 特权级别 | 访问权限 |
---|---|---|
线程模式(Thread Mode) | 非特权(User)或 特权(Privileged) | 可配置 |
处理模式(Handler Mode) | 特权(Privileged) | 固定 |
- 处理模式(ISR 中)总是特权级,可访问所有系统寄存器(如 NVIC、SCB)。
- 线程模式 可通过
CONTROL
寄存器切换:CONTROL[0] = 0
:使用 MSP,特权级。CONTROL[0] = 1
:使用 PSP,非特权级。
安全性:操作系统(如 FreeRTOS)利用此机制实现任务隔离,用户任务运行在非特权级,防止非法访问系统资源。
七:SysTick —— 系统滴答定时器
Cortex-M 内核内置 SysTick 定时器,是操作系统节拍(Tick)的基础。
工作原理
- 24 位递减计数器,时钟可选 HCLK 或 HCLK/8。
- 计数到 0 时:
- 自动重载(LOAD 值)。
- 置位
COUNTFLAG
。 - 触发 SysTick 异常(IRQ #15)。
配置 SysTick(1ms 节拍,HCLK=72MHz)
void SysTick_Init(void) {
SysTick->LOAD = 72000 - 1; // 72MHz / 72000 = 1000Hz = 1ms
SysTick->VAL = 0; // 清空当前值
SysTick->CTRL =
SysTick_CTRL_CLKSOURCE_Msk | // 时钟源 = HCLK
SysTick_CTRL_TICKINT_Msk | // 使能中断
SysTick_CTRL_ENABLE_Msk; // 启动
}
// SysTick 异常服务程序
void SysTick_Handler(void) {
// 操作系统增量节拍
// osSystickHandler();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
结语:Cortex-M 实时性能的体系化设计
ARM Cortex-M 内核之所以能在嵌入式领域取得巨大成功,其根本原因在于它通过一系列软硬件协同设计,实现了高性能、低延迟、高可靠性的实时响应能力:
- 统一异常模型:将中断与异常统一管理,简化设计。
- NVIC 优先级管理:支持抢占与嵌套,满足复杂实时需求。
- 硬件自动压栈:6 个周期完成上下文保存,响应速度极快。
- 向量表跳转:直接跳转 ISR,无软件调度开销。
- 双堆栈指针(MSP/PSP):隔离任务与中断,提高安全性。
- 内置 SysTick:为操作系统提供标准节拍源。
这些特性与前文所述的 USART、I²C、SPI、ADC、DAC 等外设完美配合,使得开发者可以轻松实现 “外设中断 → CPU 响应 → 数据处理 → 结果输出” 的高效闭环,构建出响应迅速、稳定可靠的嵌入式系统。
理解 Cortex-M 的这一底层机制,不仅是掌握其工作模式的关键,更是优化系统性能、诊断中断延迟、设计实时操作系统(RTOS)的基石。