【STM32】中断软件分支处理( NVIC 和 GIC)

发布于:2025-09-05 ⋅ 阅读:(22) ⋅ 点赞:(0)

中断软件分支处理:NVIC 和 GIC 是嵌入式系统和操作系统内核中非常关键的内容,尤其在 Cortex-MCortex-A 架构中有很大差别。本篇博客将分别介绍二者之间的关系。这部分内容涉及ARM 架构中断系统的底层机制,也是理解嵌入式系统、操作系统内核、驱动开发的关键知识。所以在我们接下来的文章向下一个阶段推进之前,我必须以系统化的方式向您详细介绍这二者的本质、区别与联系。

一、什么是中断软件分支处理?

中断软件分支(Software Interrupt Vectoring)的定义:

CPU 收到中断信号后,不通过硬件自动跳转,而是通过软件跳转到对应中断服务函数的机制。


详细点说:
中断发生后,CPU 不直接跳转到某个固定地址处理函数,而是进入统一入口,由软件读取中断号,再“查表跳转”到具体中断服务函数(ISR) 的机制。

一些前置概念先导:

中断					CPU 在执行程序时,被外部或内部事件打断,转去执行专门处理函数的机制
中断控制器(IC)		硬件模块,管理多个中断源,决定中断优先级、分发给哪个 CPU
中断向量表			存储中断处理函数地址的表,CPU 通过它跳转到对应中断处理逻辑
软件分支处理			CPU 进入统一异常入口后,由软件识别中断号并跳转到对应处理函数
中断软件分支详细示意流程:
graph TD
  A[外设产生中断] --> B[NVIC or GIC]

  B --> |Cortex-M| C[NVIC 处理]
  C --> D[CPU 跳转向量表]
  D --> E[ISR 执行]

  B --> |Cortex-A| F[GIC 分发]
  F --> G[CPU 进入统一入口]
  G --> H[读取中断号]
  H --> I[软件查表跳转 ISR]

这种方式便于用户空间自定义中断行为,常用于高级操作系统如 Linux


中断处理有两种方式:

中断控制器(IC):中断控制器是 CPU 的“中断信号调度中心”。它决定了:哪个中断优先响应?哪个 CPU 核来处理?中断号是多少?而这时候便出现了两大主流:1、NVIC(Cortex-M 内建);2、GIC(Cortex-A 外设)。

  • 硬件向量分支(如 Cortex-M)
    • CPU 自动从 中断号 × 4 + 向量基地址 中读取跳转地址
    • 由硬件完成中断分发和跳转
    • 使用 NVIC(Nested Vectored Interrupt Controller)
  • 软件分支派发(如 Cortex-A)
    • 所有中断统一进入一个入口地址(如 __irq_svc
    • 由软件读取中断号(从中断控制器),通过查表跳转到对应 ISR
    • 使用 GIC(Generic Interrupt Controller)

中断控制器架构分层模型
+-----------------------------+
|       外设设备(GPIO/UART)|
+-----------------------------+
             │
             ▼
+-----------------------------+
| 中断控制器(NVIC / GIC)     |
| - 优先级控制                |
| - 中断屏蔽                  |
| - 中断号分配                |
| - 目标 CPU 分发(GIC)       |
+-----------------------------+
             │
             ▼
+-----------------------------+
|    CPU 异常入口(向量表)   |
| - NVIC:自动跳转           |
| - GIC:统一入口 + 软件分派  |
+-----------------------------+
             │
             ▼
+-----------------------------+
| 中断服务函数(ISR)        |
+-----------------------------+

二、NVIC(Nested Vectored Interrupt Controller)

适用于 ARM Cortex-M 系列(如 STM32) 的中断控制器。

✅ NVIC 特点:

内嵌在 Cortex-M 内核中,处理速度快
每个中断有唯一入口地址(ISR),在向量表中定义
自动保存/恢复上下文(部分寄存器)
特性 说明
内置于 Cortex-M 内核 无需外部中断控制器
支持中断嵌套 优先级分组机制
编号由芯片厂商定义 通常 IRQn_Type 枚举
中断入口固定 硬件自动跳转,无需软件分发
优先级控制 支持抢占优先级和响应优先级

✅ NVIC 相关函数(CMSIS):

NVIC_EnableIRQ(IRQn);         // 开启中断
NVIC_DisableIRQ(IRQn);        // 关闭中断
NVIC_SetPriority(IRQn, prio); // 设置优先级

✅ 向量表结构(以 STM32 为例):

const void* vector_table[] __attribute__((section(".isr_vector"))) = {
    (void*) &__StackTop,
    Reset_Handler,
    NMI_Handler,
    HardFault_Handler,
    ...
    TIM2_IRQHandler,  // IRQ #28
};

📘 NVIC 示例:STM32 定时器中断

// 向量表中注册 ISR
void TIM2_IRQHandler(void) {
    HAL_TIM_IRQHandler(&htim2);
}

// NVIC 设置优先级 & 使能中断
NVIC_SetPriority(TIM2_IRQn, 1);
NVIC_EnableIRQ(TIM2_IRQn);

✅ NVIC 中断流程图(硬件向量跳转):

外设产生中断 (如 TIM2)
         ↓
NVIC 确认中断未屏蔽 → 比较优先级
         ↓
CPU 自动跳转到向量表对应地址
         ↓
执行 ISR(例如 TIM2_IRQHandler)
         ↓
中断返回 → 恢复主流程

三、GIC(Generic Interrupt Controller)

适用于 ARM Cortex-A 系列(如 A7、A53、A55) 的中断控制器,主要用于运行 Linux、Android 的多核系统。

请添加图片描述

✅ GIC 架构组成(通常为 GICv2/v3):

组件 说明
Distributor(GICD) 接收中断请求,决定目标 CPU
CPU Interface(GICC) 每个核对应的本地接口,控制本地中断处理
SGI 软件生成中断
PPI 私有外设中断(每核独享)
SPI 共享外设中断(所有核共享)

中断类型:

类型 编号范围 说明
SGI(软件中断) 0–15 用于核间通信
PPI(私有中断) 16–31 针对当前核
SPI(共享中断) 32–1019 外设中断,多个核共享

GIC 配置步骤(简化流程):

1. 读取中断号 GICC_IAR
2. 查表跳转 ISR(软件分支)
3. 执行中断服务函数
4. 写 GICC_EOIR 通知中断处理完毕

Linux 中的中断处理就是基于此机制:irq_desc[] → handle_irq()

✅ 中断流程(GIC + Linux):

  1. 外设产生中断 → GICD 接收
  2. GIC 判断目标 CPU → 投递中断
  3. CPU 接收中断 → 进入 __irq_svc
  4. 内核调用 gic_read_iar() 获取中断号
  5. 查找中断描述符 irq_desc[]
  6. 调用对应 ISR(驱动注册的 handler)
1. __irq_svc: 汇编级中断入口
2.arch_irq_handler_default()
3.gic_handle_irq()
4. → read GICC_IAR → 得到中断号
5.generic_handle_irq(irq)
6. → irq_desc[irq].handle_irq()
7. → ISR(驱动注册的中断处理函数)
8. → write GICC_EOIR 表示中断结束

在 Linux 内核中:

// Linux 中断通用框架
__irq_svc:         // 异常向量入口
    -> entry.S
    -> irq_handler
    -> gic_handle_irq()
    -> generic_handle_irq()
    -> irq_desc[irq].handle_irq()
    -> 驱动注册的 ISR

即:Linux 通过统一的中断入口 gic_handle_irq() 获取中断号 → 查表 → 跳转到目标 驱动注册的中断处理函数。

✅ GIC 中断流程图(软件分派):
外设产生中断(如 UART1)
         ↓
GIC Distributor 接收中断请求
         ↓
确定目标 CPU(如 CPU0)
         ↓
CPU0 收到中断 → 跳转到统一入口地址(__irq_svc)
         ↓
软件读取中断号(GICC_IAR)
         ↓
调用 irq_desc[irq].handler → 驱动注册函数
         ↓
处理中断逻辑 → 写 GICC_EOIR 结束
         ↓
返回用户态
GIC 架构图(简化)
+----------------------+
|     GIC Distributor  |
|   - 接收中断          |
|   - 目标 CPU 核      |
|   - SPIs(共享中断)  |
+----------------------+
         |
+--------+--------+
| CPU Interface 0 |
| CPU Interface 1 |
| ...             |
+-----------------+

每个 CPU 核 ↔ GICC ↔ GICD ↔ 外设

📘 GIC 示例:Linux 驱动中注册中断

// 设备驱动中注册 IRQ
int irq = platform_get_irq(pdev, 0);
request_irq(irq, my_irq_handler, 0, "my_device", NULL);

irqreturn_t my_irq_handler(int irq, void *dev) {
    printk("Interrupt received!\n");
    return IRQ_HANDLED;
}

硬件向量(NVIC)vs 软件分支(GIC)对比
对比项 NVIC(Cortex-M) GIC(Cortex-A)
适用架构 Cortex-M 系列 Cortex-A 系列
中断数量 一般 < 256 可达 1020 个
工作模式 (CPU 自动跳转 ISR)硬件向量跳转 (需要代码处理)软件跳转(中断分发)
统一入口 → 查表
响应速度 更快 稍慢但灵活
多核支持 不支持 支持分发到不同 CPU 核
中断类型 外设中断 SGI / PPI / SPI
驱动复杂度 高,需内核支持
常用系统 裸机开发、RTOS Linux、Android 系统

那么,从发者视角出发:何时使用哪种?

开发场景 使用方式 控制器
STM32 裸机 / FreeRTOS NVIC + 硬件向量表 NVIC
Cortex-A + Linux GIC + 软件中断派发 GIC
多核系统 GIC 分发到指定 CPU GIC
安全系统(TrustZone) GIC 支持 Secure/Non-Secure 中断 GICv2/3/4

通过对比可见:

  • NVIC 适用于单核 MCU,如 STM32、GD32
  • GIC 适用于多核 SoC,如 RK3568、i.MX6、A7
  • 软件分派方式是 Linux 内核中断的核心机制

NVIC 是硬件自动跳转中断处理函数;GIC 需要软件读取中断号后手动跳转。中断软件分派是 GIC 的核心特征,NVIC 则通过硬件向量表处理。

综上,

NVIC 是硬件帮你跳转,GIC 是你自己跳转。中断软件分支是“跳转方式的选择”,NVIC 和 GIC 是“分发机制的差异”。
Cortex-M 使用 NVIC,靠硬件向量跳转;Cortex-A 使用 GIC,依赖软件中断分派。

在后续的学习路径中,不同阶段需要掌握的内容有:

阶段				内容								建议平台
初级				NVIC、CMSIS 中断函数				STM32 / GD32
提高				GIC 配置、SGI 使用				Cortex-A7 / RK3568
系统级			Linux 中断子系统源码分析			STM32MP157 / IMX6ULL
驱动级			request_irq / 中断注册机制		Linux 驱动开发

熟练掌握和运用 中断机制是成为驱动工程师的基础,所以进入Linux驱动前要先介绍这部分核心的内容。

以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!


网站公告

今日签到

点亮在社区的每一天
去签到