STM32_3:EXTI和NVIC

1.外部中断EXTI

EXTI:External interrupt/event controller外部中断/事件控制器

1.外部中断基础知识

1.STM32外部中断框架

中断的概念:在主程序运行过程中,出现了特定的中断触发条件,使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行。

  1. 有20条外部中断线
  2. 有十六条是GPIO的中断线(0~15),同一个引脚号对应同一个line
  3. 第十九条line只在有ETH互联设备上才存在
  4. GPIO的中断线要启用必须先要配置AFIO的寄存器EXTICRx

2.STM32外部中断机制框架

  1. 边沿检测:上升沿,下降沿或者双边沿
  2. 软件配置中断或者事件寄存器
  3. 屏蔽中断寄存器或事件寄存器
  4. 请求挂起寄存器
  5. 中断则发送给NVIC中断控制器
  6. 事件则产生一个脉冲响应

2.复用功能

1.什么是复用功能?

处理器的引脚本身默认就是一个普通的GPIO,但是它还可以被复用成其他功能,我们称之为一个引脚的复用功能

3.重映射

1.什么是重映射?

重映射属于复用功能的另外一个功能,可以把具有特殊功能的引脚,分配到其他引脚上去

如果某个功能被重映射了,那么这个功能将不再遵循其默认的默认分配。

2.中断嵌套控制器NVIC

NVIC:Nested Vectored Interrupt Controller 嵌套向量中断控制器

1.中断向量表

Cortex-M3内核支持256个中断,其中包含了16个内核中断(异常)和240个外部中断,并且具有256级的可编程中断设置。但是,STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32有84个中断,包括16个内核中断(异常)和68个可屏蔽中断, 具有16级可编程的中断优先级。而STM32F103系列上面,16个内核中断(异常)不变, 而可屏蔽中断只有60个(在107系列才有68个)

优先级号越小,优先级越高。

2.中断优先级分组

这60个中断,怎么管理呢?这就涉及到STM32的中断分组。STM32可以将中断分成5个组,分别为组0-4;同时,对每个中断设置一个抢占优先级和响应优先级。分组配置是由 SCB->AIRCR寄存器的bit10-8来定义的。SCB->AIRCR是在哪里的呢?由于这是CM3内核定义的

具体的分配关系如下所示:

CM3中定义了8个Bit用于设置中断源的优先级,而STM32只选用其中的4个Bit。抢占优先级的级别高于响应优先级,而数值越小所代表的的优先级越高,介绍一下抢占优先级、响应优先级的区别:

  • 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的;
  • 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;
  • 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行;
  • 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;(中断号)

除此之外有两点需要注意:

  • 打断的情况只会与抢占优先级有关, 和响应优先级无关!(中断嵌套)
  • 一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。

3.中断优先级控制函数结构体

中断优先级控制函数

  • NVIC_SetPriorityGrouping()。

中断优先级控制结构体参数 NVIC_InitTypeDef

  • NVIC_IRQChannel:定义初始化的是哪一个中断,这个可以在stm32f10x.h文件 中查到每个中断对应的名字,如USART1_IRQn;
  • NVIC_IRQChannelPreemptionPriority:定义此中断的抢占优先级别; NVIC_IRQChannelSubPriority:定义此中断的响应优先级别;
  • NVIC_IRQChannelCmd:该中断是否使能。

NVIC_Init()函数初始化NVIC寄存器

IRQn : Interrupt Request Number 中断请求编号或中断号

4.中断优先级设置步骤

1、系统运行后先设置中断优先级分组。调用函数。

2、针对每个中断,设置对应的抢占优先级和响应优先级。

3、如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。

5.项目:外部中断按键控制LED灯

按键控制LED的开发流程:

第一步:使能功能复用时钟

第二布,配置复用寄存器

第三步,配置中断屏蔽寄存器

固件库按键控制LED灯

外部中断EXTI结构体:

typedef struct {

uint32_t EXTI_Line;

EXTIMode_TypeDef EXTI_Mode;

EXTITrigger_TypeDef EXTI_Trigger;

FunctionalState EXTI_LineCmd;

}EXTI_InitTypeDef;

外部中断EXTI相关库函数:

void EXTI_DeInit(void);

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);

void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);

FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);

void EXTI_ClearFlag(uint32_t EXTI_Line);

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);

void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

注:EXTI 函数在misc.h

Miscellaneous /ˌmɪs.əˈleɪ.ni.əs/混杂的,各种各样的

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);

检查指定的 EXTI 中断线路是否发生了中断请求,即查询该线路的中断标志位(Interrupt Flag)是否被置位。

工作原理

当配置好的外部中断触发条件(如上升沿)发生时,硬件会自动将对应的中断挂起位 置 1。这个函数就是去读取这个状态位的值。


// 方式1:最常用的判断方式(推荐)

if (EXTI_GetITStatus(EXTI_Line0) != RESET)

{

// 中断发生了,执行处理代码

EXTI_ClearITPendingBit(EXTI_Line0);

}

软件流程设计

  • 初始化GPIO、AFIO(EXTI)外设时钟
  • 初始化GPIO按键,初始化LED引脚
  • 连接GPIO引脚到EXTI
  • 初始化EXTI外部中断条件
  • NVIC
  • 初始化NVIC嵌套中断控制器

编写外部中断函数

  • 判断中断发生控制LED灯
cs 复制代码
#include "stm32f10x.h"
#include "Exti.h"


void Exti_Init(void)
{
		
	GPIO_InitTypeDef Gpio_Initstructure;  
	EXTI_InitTypeDef Exti_Initstructure;
	NVIC_InitTypeDef NVIC_Initstructure;
	
	//
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);
	
	Gpio_Initstructure.GPIO_Pin = GPIO_Pin_0;
	Gpio_Initstructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOA,&Gpio_Initstructure);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
	
	//
	Exti_Initstructure.EXTI_Line = EXTI_Line0;
	Exti_Initstructure.EXTI_Mode = EXTI_Mode_Interrupt;
	Exti_Initstructure.EXTI_Trigger = EXTI_Trigger_Falling;
	Exti_Initstructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&Exti_Initstructure);
	
	//
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_Initstructure.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_Initstructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_Initstructure);
}

过程详解:

1.结构体声明和时钟使能

GPIO_InitTypeDef Gpio_Initstructure;

EXTI_InitTypeDef Exti_Initstructure;

NVIC_InitTypeDef NVIC_Initstructure;

// 开启时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

注:如果不开启AFIO时钟,

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);将不会把PA0引脚连接到EXTI_Line0。实际上是在操作 AFIO 模块的寄存器。

引脚与EXTI的映射关系保持默认或随机,在STM32中,EXTI线默认没有连接到任何GPIO引脚,或者保持上一次的状态。

2.配置GPIO引脚

3.连接GPIO引脚到EXTI线路

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);

建立映射关系

将物理引脚PA0连接到EXTI_Line0中断线

注:多个引脚可共享同一条EXTI线(如PA0和PB0都连到EXTI_Line0)

4.配置EXTI模块(中断条件设置)

Exti_Initstructure.EXTI_Line = EXTI_Line0; // 选择EXTI线路0

Exti_Initstructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式

Exti_Initstructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发

Exti_Initstructure.EXTI_LineCmd = ENABLE; // 使能该线路

EXTI_Init(&Exti_Initstructure); // 应用配置

EXTI_Trigger_Rising:上升沿触发(低→高)

EXTI_Trigger_Falling:下降沿触发(高→低)

EXTI_Trigger_Rising_Falling:双边沿触发(变化就触发)

通道:

  • EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */
  • EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */
  • EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */
  • EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */
  • EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */
  • EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts
  • EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */

5.配置NVIC

// 1. 设置优先级分组

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

NVIC_PriorityGroup_2:2位抢占优先级,2位子优先级

抢占优先级:决定是否能打断其他中断(高优先级可抢占低优先级)

子优先级:相同抢占优先级时的响应顺序
// 2. 配置具体中断通道

NVIC_Initstructure.NVIC_IRQChannel = EXTI0_IRQn; // 中断号

NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级

NVIC_Initstructure.NVIC_IRQChannelSubPriority = 0; // 子优先级

NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断

NVIC_Init(&NVIC_Initstructure);

硬件流程:

  1. 按键按下 → PA0电平:高 → 低

  2. EXTI检测到下降沿 → 设置中断标志位

  3. NVIC检查优先级 → 中断当前程序(如果允许)

  4. CPU跳转到中断服务函数

物理世界

手指按下按键

芯片引脚

PA0与GND接通 → 电压3.3V→0V

芯片内部GPIO

检测到PA0=0(之前是1)

芯片内部EXTI

发现"1→0"下降沿 → 置位中断标志

芯片内部连线

通过金属线发送IRQ6信号

芯片内部NVIC

接收信号 → 检查优先级 → 通知CPU

CPU核心

暂停主程序 → 跳转到你的中断函数

执行代码

你的灯控逻辑:亮1秒 → 灭1秒

检查

EXTI_GetITStatus() : 确认是PA0中断

清理

EXTI_ClearITPendingBit() :清除中断标志

返回

CPU回到主程序继续执行

补充:weak:弱定义;如果用户没有自己定义的中断函数,则系统默认执行系统自定义的中断函数,如果用户有定义中断函数则优先执行用户的中断函数。

相关推荐
-Springer-10 分钟前
STM32 学习 —— 个人学习笔记5(EXTI 外部中断 & 对射式红外传感器及旋转编码器计数)
笔记·stm32·学习
LS_learner29 分钟前
树莓派(ARM64 架构)Ubuntu 24.04 (Noble) 系统 `apt update` 报错解决方案
嵌入式硬件
来自晴朗的明天1 小时前
16、电压跟随器(缓冲器)电路
单片机·嵌入式硬件·硬件工程
钰珠AIOT1 小时前
在同一块电路板上同时存在 0805 0603 不同的封装有什么利弊?
嵌入式硬件
代码游侠1 小时前
复习——Linux设备驱动开发笔记
linux·arm开发·驱动开发·笔记·嵌入式硬件·架构
代码游侠12 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
xuxg200514 小时前
4G 模组 AT 命令解析框架课程正式发布
stm32·嵌入式·at命令解析框架
CODECOLLECT16 小时前
京元 I62D Windows PDA 技术拆解:Windows 10 IoT 兼容 + 硬解码模块,如何降低工业软件迁移成本?
stm32·单片机·嵌入式硬件
BackCatK Chen17 小时前
STM32+FreeRTOS:嵌入式开发的黄金搭档,未来十年就靠它了!
stm32·单片机·嵌入式硬件·freertos·低功耗·rtdbs·工业控制