1、基础概念:

中断系统(NVIC)是 STM32 的关键部分  ,因为 STM32 的高实时性能很大程度上依赖 NVIC(中断控制器)

NVIC(Nested Vectored Interrupt Controller)
中文名叫:嵌套向量中断控制器

它是 ARM Cortex-M 内核里一个专门负责管理中断的模块
STM32 的所有外设(比如 USART、TIM、ADC、EXTI)如果要触发中断,都必须经过 NVIC

NVIC 的作用(你可以记住4个字)

👉 “管、排、优、嵌”

功能 说明
理中断 决定哪个中断能触发、哪个被屏蔽
队响应 多个中断同时发生时,按优先级处理
先级设置 每个中断都有优先级,可高可低
套执行 允许高优先级中断打断低优先级中断执行

中断分类:

内部中断/内核中断 :

  • 芯片内部/内核中断内容,也可以称之为【Exception 异常】。程序运行 正常过程中,出现的非正常情况。例如 RESET 按键,内存溢出,硬件问题。。。内部/内核 中断重点关注【系统嘀嗒 SysTick】。

外部中断:

  • 片上外设中断,STM32 片上外设大多数都有对应的中断情况,例如 USART 数据发送完 成中断,数据接收中断,数据总线空闲中断,数据错误中断。。。大多数中断都是片上 外设完成特定任务,指定条件触发... 需要对应外设 CR SR 两个寄存器配合使用。CR 用于控制中断是否开启 IESR 是判断触发的中断是哪一个。
  • EXTI 外部中断控制线,MCU 提供给引脚连接的外部设备,例如 SR602,外部压力传感 器,烟雾传感器。利用基本的【高低电平切换出现的边沿情况】进行中断触发。扩宽外 部中断控制。

中断触发条件:

STM32 中的中断源主要分为以下三大类:

类别 来源 举例
① 外部中断(EXTI) 外部引脚电平或边沿变化

按键、光敏模块、传感器信号等

上升沿触发(信号从 0 → 1)下降沿触发(信号从 1 → 0)双边沿触发(任意变化都触发)

② 外设中断 外设内部事件触发 USART、TIM、ADC、DMA 等
③ 系统异常中断 内核事件触发 SysTick、HardFault、NMI 等

中断中的常用概念

  • 断点:在 MCU 中注册的中断触发位置,一般对应中断触发条件,触发寄存器标志位变化。 需要通过 CR 寄存器对当前模块中断进行使能 IE ==> Interrupt Enable
  • 中断源:当前中断触发之后,对应的中断处理函数 IRQHandler
  • 压栈:中断触发,此时【中断处理函数 IRQHandler】称为前台函数,中断触发位置函数成为 【后台函数】

中断压栈行为:

 IRQn IRQHandler 解释

IRQn 是中断的“编号”(Interrupt Request Number):它是 NVIC(中断控制器)用来识别中断源的编号或枚举名

IRQHandler 是中断服务函数(ISR):

  • IRQHandler = Interrupt Request Handler
  • 表示对应中断的 中断服务函数(ISR, Interrupt Service Routine)

IRQHandler 和 IRQn 一一对应,当前在 MCU 中注册了 IRQn 中断请求编号,内核在触发中

断之后,会自动调用对应 IRQn 的 IRQHandler 处理函数。当某个中断发生后,NVIC 会自动跳转执行这个函数。IRQn 是中断的 “编号”,IRQHandler 是中断的 “函数入口”。

中断优先级:

在 STM32 中,中断优先级分为两类:

类型 中文名 作用
Preemption Priority 占先优先级(可抢占优先级) 决定 能不能打断别的中断
Subpriority 次级优先级(响应优先级) 决定 相同占先优先级中,谁先执行

假设有两个中断:

  • 中断A:占先优先级 = 1,次级优先级 = 0

  • 中断B:占先优先级 = 2,次级优先级 = 0

👉 当 中断A 正在执行时,中断B 发生了:
由于 A 的占先优先级 比 B 高(1 < 2),
所以 B 不会打断 A,必须等 A 执行完再执行 B。

反过来,如果 中断B 正在执行时,中断A 触发,
A 的占先优先级更高,因此 A 会打断 B 执行(即“抢占”)。

当占先优先级相同,次级优先级决定谁先被响应。次级优先级小的先执行,大的后执行。

 优先级分组(非常关键)

STM32 用 NVIC_PriorityGroupConfig()NVIC_SetPriorityGrouping() 来设置优先级分组。
优先级分组决定了 多少位用于占先优先级,多少位用于次级优先级

分组 占先优先级位数 次级优先级位数 说明
0 0 4 全是次级优先级,不能抢占
1 1 3 1位抢占 + 3位次级
2 2 2 2位抢占 + 2位次级
3 3 1 3位抢占 + 1位次级
4 4 0 全是占先优先级,没有次级(最多抢占层次)

STM32 通常是 4bit 优先级字段,总共 16 级可用。

EXTI 外部中断控制线开发

EXTI = External Interrupt / Event Controller(外部中断/事件控制器)

EXTI 的作用是让 外部引脚(GPIO) 也能触发中断或事件。EXTI 就是让 MCU “听得见” 外部信号变化(比如按钮、传感器信号)
然后触发中断去执行响应动作的模块。

2、相关寄存器编写:

EXTI开发所需的必要内容:

  • EXTI 外部中断控制线配置对应的 GPIO 引脚。
  • EXTI 边沿检测,上升沿和下降沿控制。
  • EXTI 中断使能

开发流程:
🧩 第1步:开启时钟

🧩 第2步:配置 GPIOA0 为输入模式

🧩 第3步:将 GPIOA0 映射到 EXTI0

🧩 第3步:将 GPIOA0 映射到 EXTI0

🧩 第5步:配置 NVIC(中断控制器)

🧩 第6步:编写中断服务函数

AFIO(Alternate Function I/O)复用功能输入输出控制器。

AFIO 是 STM32 芯片里一个专门控制 GPIO 引脚功能映射和复用的外设模块。

功能 说明
1️⃣ GPIO 复用控制 让一个引脚选择不同的外设功能(如 USART_TX、SPI_MOSI 等)
2️⃣ 外部中断映射 让 EXTI0~EXTI15 能和不同端口的 GPIO 引脚绑定(如 PA0、PB0、PC0 等)
3️⃣ JTAG/SWD 配置 控制调试接口(JTAG/SWD)的引脚使用情况(比如释放 PA15、PB3、PB4)

以AFIO_EXTICR1为例:

例如:EXTI1配置0000 == > PA1;EXTI1 配置 0100 ==> PE1;EXTI1 配置 0010 ==> PC1。

AFIO_EXTICR1 控制 EXTI0~EXTI3外部中断控制线,

上升沿/下降沿触发选择寄存器:

以KEY_UP按键为例,希望按下KEY_UP产生外部中断,

按下时,WK_UP 从 0V → 3.3V,电平上升。因此,中断应设置为 上升沿触发(rising edge)

(如果按键按下之后提供给MCU电平为低电平,GPIO默认为高电平模式,选择下降沿触发。)

EXTI中断使能

需要开启EXTI外部中断线中断触发支持,需要通过两个寄存器配合完成。分别是EXTI_IMR和EXTI_PR。

EXTI_IMR(中断屏蔽寄存器)(IMR = Interrupt Mask Register)是否开发当前对应外部中断线的中断。

EXTI_PR(挂起寄存器)(PR = Pending Register)

中断发生后,该位会被硬件置 1,表示该线路有“中断挂起”。必须手动清零(写 1 清零)。

  • 当对应的外部事件发生(选定的上升/下降沿出现)时,硬件会把 EXTI_PR 寄存器对应位写为 1(表示该线有中断“挂起”)。该位不会自动清除,必须由软件清除

  • 必须在中断处理流程中清除该位,否则中断标志一直存在,NVIC 会认为中断仍然挂起,CPU 可能会持续(或再次)进入该中断处理函数,导致“死循环中断”或系统卡住。

  • 清除可以在 ISR 开头 也可以在 处理逻辑结束前,两种做法都会被用到

其他EXTI寄存器

EXTI_EMR(事件屏蔽寄存器):控制是否允许产生事件信号(Event,不进入 NVIC,但可触发内部外设反应,比如触发 ADC 转换)。

EXTI_SWIER(软件中断寄存器)通过软件设置该位,可以人为触发一次中断。用于测试或调试。

3、代码编写:

编写按键中断:

前期分析工作,根据原理图得

代码:

void Key_Init(void)
{
	//开启时钟使能
	RCC->APB2ENR |= GPIOA_RCC_APB2_CLOCK_ENABLE;
	RCC->APB2ENR |= GPIOE_RCC_APB2_CLOCK_ENABLE;
	
	/**
	设置为 GPIO 上拉下拉输入模式
	*/
	//  KEY_UP PA0
	GPIOA->CRL &= ~(0x0F);
	GPIOA->CRL |= PULL_UP_OR_PULL_DOWN;
	//利用 ODR 低电平明确限制下拉输入
	GPIOA->ODR &= ~(0x01);
	// KEY0 PE4  KEY1 PE3   KEY2  PE2
 	GPIOE->CRL &= ~(0x0FFF << 8);
	GPIOE->CRL |= (PULL_UP_OR_PULL_DOWN << 16);
	GPIOE->CRL |= (PULL_UP_OR_PULL_DOWN << 12);
	GPIOE->CRL |= (PULL_UP_OR_PULL_DOWN << 8);
	//利用 ODR 高电平明确限制上拉输入
	GPIOE->ODR |= (0x07 << 2);  
}

void Key_EXTI_Interrupt_Enable(void)
{
	//前面配置过GPIO模式,因此需要 AFIO 对当前引脚进行复用配置
	//使能 AFIO
	RCC->APB2ENR |= AFIO_RCC_APB2_CLOCK_ENABLE;

	/**
	 * AFIO 外部中断控制线 EXTI 对应 GPIO 引脚配置
	 * KEY_UP PA0
	 * KEY0 PE4  
	 * KEY1 PE3  
	 * KEY2 PE2
	 * 需要配置 EXTI0 EXTI2 EXTI3 EXTI4
	 * EXTI0 EXTI2 EXTI3 ==> EXTICR1 寄存器
	 *  EXTI4 ==> EXTICR2 寄存器
	 * EXTI0 ==> PA0 ==> 寄存器对应位存储内容为 0000
	 * EXTI2 ==> PE2 ==> 寄存器对应位存储内容为 0100 
	 * EXTI3 ==> PE3 ==> 寄存器对应位存储内容为 0100
	 * EXTI4 ==> PE4 ==> 寄存器对应位存储内容为 0100 
	 */
	AFIO->EXTICR[0] |= EXTI_GPIOEX << 12 | EXTI_GPIOEX << 8 | EXTI_GPIOAX;
	AFIO->EXTICR[1] |= EXTI_GPIOEX;

	/**
	 * 配置外部中断控制线 EXTI 对应 上升沿 或者 下降沿
	 * key_up PA0==> EXTI0 上升沿触发
	 * KEY0 PE4、KEY1 PE3 、KEY2 PE2 下降沿触发
	 */
	EXTI->RTSR |= 0x01;
	EXTI->FTSR |= (0x07 << 2);

	/**
	 * 中断屏蔽寄存器配置,开启 EXTI 0 2 3 4 
	 */
	EXTI->IMR |= 0x1D;

	/**
	 * 中断注册 
	 * 利用 NVIC 对当前EXTI对应的 EXTIx_IRQn 进行注册,告知 MCU
	 * 当前程序运行过程中,存在 EXTI 外部中断控制线中断内容
	 * NVIC_SetPriority(EXTIx_IRQn,优先级)
	 */

	NVIC_SetPriority(EXTI0_IRQn, 1);
	NVIC_SetPriority(EXTI2_IRQn, 1);
	NVIC_SetPriority(EXTI3_IRQn, 1);
	NVIC_SetPriority(EXTI4_IRQn, 1);


	/**
	 * 中断触发之后处理函数
	 */
	NVIC_EnableIRQ(EXTI0_IRQn);
	NVIC_EnableIRQ(EXTI2_IRQn);
	NVIC_EnableIRQ(EXTI3_IRQn);
	NVIC_EnableIRQ(EXTI4_IRQn);

	/**
	 * EXTI0_IRQHandler 是 EXTI0_IRQn 的中断处理函数。
	 */

}


void EXTI0_IRQHandler(void)
{
	/**
	 * 
	 */

}
void EXTI2_IRQHandler(void)
{
	/**
	 * 
	 */

}

void EXTI3_IRQHandler(void)
{
	/**
	 * 
	 */

}

void EXTI4_IRQHandler(void)
{
	/**
	 * 
	 */

}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐