GPIO不只是点灯:STM32输入输出 + EXTI + NVIC彻底讲透
文章摘要
很多人学 STM32,都停留在点灯、串口、定时器阶段,程序能跑,外设能用,但一到面试官问“GPIO 怎么配置外部中断?”、“NVIC 抢占优先级和响应优先级怎么理解?”就慌了手脚。
本篇博客以面试题为导向,从 GPIO 的输入输出模式、开漏/推挽区别,到 EXTI 外部中断配置流程、NVIC 优先级管理,带你完整梳理底层原理和面试答题思路。
看完这篇,你不仅能回答 GPIO 面试高频题,还能顺利把GPIO → EXTI → NVIC的链路讲清楚,面试稳如老狗!
文章目录
本栏系列文章
STM32是怎么跑起来的?启动流程 + 时钟树一次讲透(面试高频)
其他专栏精品文章
大厂嵌入式代码规范(二):命名规范与数据结构
单片机开发环境搭建看这里
SLAMCraft:自主导航机器人DIY(一)如何设计一个自己的SLAM机器人
前言
很多人学 STM32,都是从点灯、串口、定时器开始的。程序能跑,外设也能用,但一到面试官问下面这些问题就开始卡壳:
- “STM32 GPIO 输入输出模式都有哪些?空闲状态是什么?”
- “GPIO 如何触发外部中断?EXTI 怎么配置?”
- “NVIC 的抢占优先级和响应优先级是什么?怎么理解?”
- “开漏和推挽输出有什么区别?什么时候用哪一个?”
归根结底,这些问题都指向两件事,把这两件事弄明白,GPIO 面试题就不再是难点:
- GPIO 的工作原理和模式
- STM32 的中断体系(EXTI + NVIC)
一、STM32 GPIO 输入输出模式都有哪些?
STM32 GPIO 主要分 输入模式 和 输出模式,但原理上和大部分 MCU(如 AVR、NXP、TI)类似。理解底层原理,可以在面试中脱颖而出。
1. GPIO 的通用原理
GPIO 本质上是一个可配置的 数字开关,通过 P/N MOS 管 控制电平输出。
- 输入状态由 上下拉电阻 或 浮空 决定。
- 浮空输入容易受噪声影响,因此 MCU 通常提供上拉或下拉选项。
- 输入端通过 施密特触发器 做波形整形,避免按键抖动导致误触发。
2. GPIO 输入模式
| 模式 | 空闲电平 | 应用场景 |
|---|---|---|
| 上拉输入 | 高 | 按键、开关 |
| 下拉输入 | 低 | 按键、开关 |
| 浮空输入 | 不确定 | 外部信号读取,需要注意抖动 |
| 模拟输入 | 不限 | ADC/DAC 输入 |
浮空输入如果没有外部上拉/下拉,很容易受到干扰,导致状态不稳定。
3. GPIO 输出模式
| 模式 | 描述 | 面试可回答场景 |
|---|---|---|
| 推挽输出 | 高低电平都能输出,驱动能力强 | LED、蜂鸣器、继电器 |
| 开漏输出 | 只能输出低电平,需要外部上拉 | I2C SDA、多主总线 |
| 推挽复用 | 外设复用功能 | PWM 输出、USART TX |
| 开漏复用 | 外设复用功能 | I2C SDA 等 |
区分推挽和开漏输出,并结合实际应用举例,比如 I2C 总线用开漏,控制 LED 用推挽。
二、GPIO 怎么配置成外部中断?
面试常问:“假设要用按键触发中断,你怎么配置 GPIO 和 EXTI?”
2.1 EXTI 是什么?
标准回答流程:
- 初始化 GPIO 为输入
- 开启 GPIO 时钟,并映射到 EXTI 线
- 配置 EXTI 触发方式(上升沿 / 下降沿 / 双沿)
- 配置 NVIC 中断优先级并使能
- 编写 ISR,并清除中断标志
示例代码:
// GPIOE Pin2 映射到 EXTI_Line2
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line2;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
void EXTI2_IRQHandler(void) {
if(EXTI_GetITStatus(EXTI_Line2) != RESET) {
// 中断逻辑
EXTI_ClearITPendingBit(EXTI_Line2);
}
}
2.1 EXTI 是什么?
EXTI 全称 External Interrupt/Event Controller,即外部中断/事件控制器。
作用:把 GPIO 的输入变化转换成中断请求(IRQ),每条 EXTI 线可连接 一个 GPIO 作为触发源(可映射多个 GPIO,但一次只能选一个)。
支持触发方式:上升沿 / 下降沿 / 双沿
2.2 EXTI 线与 GPIO 映射
- EXTI0-EXIT15对应 GPIO0~GPIO15
- 特殊线:EXTI16 → PVD,EXTI17 → RTC Alarm,EXTI18 → USB Wakeup

2.3 EXTI 配置流程
1、初始化 GPIO 为输入
2、使能 GPIO 时钟
3、配置 EXTI 线与 GPIO 映射
4、设置触发方式:上升沿 / 下降沿 / 双沿
5、配置 NVIC 优先级,并使能中断
6、编写 ISR,并清除挂起标志
// GPIO 输入初始化
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
// 映射 GPIOE Pin2 到 EXTI2
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);
// 配置 EXTI 触发方式
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line2;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
// NVIC 配置
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
// 中断服务函数
void EXTI2_IRQHandler(void) {
if(EXTI_GetITStatus(EXTI_Line2) != RESET) {
// 处理中断逻辑
EXTI_ClearITPendingBit(EXTI_Line2);
}
}
三、NVIC 中断管理(Nested Vectored Interrupt Controller)
面试常问:“NVIC 是啥?抢占优先级和响应优先级怎么理解?怎么配置?”
3.1、NVIC 是什么?
-
NVIC 全称 Nested Vectored Interrupt Controller,中文叫“嵌套向量中断控制器”
-
集成在 Cortex-M3 内核中
-
功能:
- 管理 MCU 外部中断(IRQ)和可配置的核心异常
- 决定中断优先级和嵌套执行顺序
- 支持抢占式中断,高优先级可以打断低优先级
-
面试速记:“NVIC 让 MCU 知道哪个中断先跑,并允许高优先级中断打断低优先级中断,是 Cortex-M3 的中断调度核心。”
3.1、NVIC 优先级体系
NVIC 的优先级由 4 位有效优先级控制(STM32F1 系列)。优先级分为两部分:
1、抢占优先级(Preemption Priority) :决定高优先级中断是否能打断低优先级中断。
2、响应优先级(Subpriority / Response Priority) :抢占优先级相同时,决定先执行哪个中断。
NVIC 分组(Priority Group)
- NVIC 优先级 4 位有效位,可以划分为抢占位和响应位
- STM32 提供 5 种分组方式:
| 分组宏 | 抢占位 | 响应位 | 备注 |
|---|---|---|---|
| NVIC_PriorityGroup_0 | 0 | 4 | 没有抢占位,全用响应位 |
| NVIC_PriorityGroup_1 | 1 | 3 | 1 位抢占 + 3 位响应 |
| NVIC_PriorityGroup_2 | 2 | 2 | 平衡分配 |
| NVIC_PriorityGroup_3 | 3 | 1 | 抢占位多,嵌套能力强 |
| NVIC_PriorityGroup_4 | 4 | 0 | 全用抢占位,无响应位 |
NVIC_PriorityGroupConfig() 只调用一次,后续所有中断都基于此分组。
2.2 总优先级计算
总优先级 = (抢占优先级 << 响应位数) | 响应优先级
- 例:分组 PriorityGroup_2(2 位抢占 + 2 位响应)
- 抢占位有效值:0~3
- 响应位有效值:0~3
- 总优先级 = 0~15
配置超出有效位数的优先级会被截断。
2.3 分组对配置生效的影响
-
分组 0(0 抢占位 + 4 响应位)
- 抢占优先级设置无效
- 响应优先级有效范围 0~15
- 配置抢占优先级会被忽略
-
分组 2(2 抢占位 + 2 响应位)
- 抢占位有效值:0~3
- 响应位有效值:0~3
- 超出范围会截断到最大值
分组 2,如果你给响应优先级设置 5,实际只生效 3。
2.4 核心异常与外部中断关系
| 类型 | 可配置 | 优先级范围 | 示例 |
|---|---|---|---|
| 核心异常 | 部分可配置 | 0~15 | SysTick、PendSV 可配置;NMI/HardFault 固定最高不可改 |
| 外部中断 | 可配置 | 0~15 | EXTI0~EXTI15,USART1/2 等 |
- 核心异常也使用 4 位有效位
- NMI / HardFault 是固定最高优先级,不可屏蔽
- 其他核心异常可通过 SHPR 寄存器配置
四、NVIC 配置流程
面试常问:“NVIC 怎么配置?举个例子。”
4.1 配置分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 2 位抢占 + 2 位响应
4.2 配置中断
以 USART1 和 EXTI2 为例:
// USART1
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
// EXTI2
NVIC_InitStruct.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0; // 高于 USART1
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
其中: EXTI2 抢占优先级更高,可以打断 USART1,同抢占优先级时,响应优先级决定先执行哪一个。
五、NVIC 嵌套中断原理
低优先级中断执行时,高优先级中断到来:
-
CPU 保存当前上下文(PC、寄存器)
-
跳转执行高优先级 ISR
-
完成后恢复低优先级中断
-
同级抢占优先级 → 按响应优先级执行
-
不可屏蔽异常(NMI/HardFault)永远优先
小结
STM32 GPIO + EXTI + NVIC,看似零散的知识,其实是一条完整的链路:GPIO 控制信号 → EXTI 生成中断 → NVIC 调度执行。面试官问你时,不在乎你会调用多少函数,而在乎你能否把这条链路讲顺、讲清楚。
记住几个关键点:
1. GPIO 模式:输入/输出/复用,空闲电平和应用场景必须会讲。
2. EXTI 配置:映射、触发方式、NVIC 配置、ISR 清标志位缺一不可。
3. NVIC 优先级:分组决定抢占/响应位,核心异常和外部中断优先级要分清。
一句话总结:理解底层原理 + 掌握配置流程 + 知道优先级逻辑 = 面试高分利器。 如果你能把 GPIO → EXTI → NVIC 链路讲顺,面试官面前稳如老狗。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)