一、中断的基本概念
-
中断(Interrupt)
- 中断是CPU在执行当前程序时,被外部或内部事件打断的过程。
- 中断发生后,CPU会暂停当前任务,转而执行中断服务例程(ISR),处理中断事件。
-
中断源(Interrupt Source)
- 中断源是触发中断的事件来源,可以是外部信号(如GPIO引脚状态变化)、内部事件(如定时器溢出)或系统事件(如错误)。
-
中断向量(Interrupt Vector)
- 中断向量是中断服务例程的入口地址。
- STM32的中断向量表存储在Flash的起始地址(通常是0x08000000),包含所有中断的入口地址。
-
中断地址
- 中断地址是中断向量表中存储的地址,指向具体的中断服务例程。
- 例如,外部中断0的中断地址是中断向量表中对应EXTI0的地址。
二、中断优先级
-
中断优先级的作用
- 当多个中断同时发生时,CPU需要根据优先级决定先处理哪个中断。
- STM32的中断优先级由NVIC(Nested Vectored Interrupt Controller)管理。
-
优先级分组
- STM32支持中断优先级分组,将优先级分为抢占优先级和子优先级。
- 抢占优先级高者可以打断优先级低的中断。
- 子优先级用于同一抢占优先级下的中断排序。
-
优先级配置
- NVIC的优先级寄存器(IPR)用于设置中断的优先级。
- 优先级数值越小,优先级越高。
三、外部中断模块(EXTI)的使用场景
-
按键输入
- 当按键按下或释放时,GPIO引脚状态变化触发外部中断,进入中断服务例程处理按键事件。
-
传感器信号检测
- 例如,使用外部中断检测传感器的脉冲信号或状态变化。
-
通信信号同步
- 在串行通信中,外部中断可用于同步接收信号的起始位或停止位。
-
紧急事件处理
- 当检测到异常信号(如过流、过压)时,通过外部中断快速响应。
三、模块知识点
一、NVIC中断优先级分组概述
NVIC是STM32的中断控制器,用于管理中断的优先级和响应顺序。STM32支持中断优先级分组,允许开发者根据实际需求分配抢占优先级和响应优先级的位数。这种分组机制可以优化中断的响应效率和系统的实时性能。
1、中断优先级分组的配置
-
中断优先级分组的作用
- 资源分配:通过分组,可以为不同的中断源分配不同的优先级,从而区分哪些中断更为重要。
- 响应时间管理:合理的分组可以优化中断的响应时间,提高系统的实时性能。
- 系统稳定性:避免优先级倒置的问题,保证系统运行的稳定性。
-
优先级分组的配置方式
-
STM32的中断优先级分组通过NVIC的应用中断与复位控制寄存器(AIRCR)的高4位进行配置。
-
根据配置的不同,优先级分组可以分为以下几种:
分组 AIRCR[10:8] 抢占优先级位数 响应优先级位数 抢占优先级级别 响应优先级级别 0 111 0位 4位 1级 16级 1 110 1位 3位 2级 8级 2 101 2位 2位 4级 4级 3 100 3位 1位 8级 2级 4 011 4位 0位 16级 1级 -
说明:
- 抢占优先级越高(数值越小),中断可以打断其他低优先级中断的执行。
- 响应优先级在抢占优先级相同时起作用,决定中断的执行顺序。
-
-
配置函数
- NVIC的优先级分组可以通过
NVIC_PriorityGroupConfig()函数设置。例如:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置为分组2
- NVIC的优先级分组可以通过
2、中断优先级的设置
-
中断优先级寄存器(IPR)
- 每个中断源都有一个对应的IPR寄存器,用于设置中断的优先级。
- 优先级数值越小,优先级越高。
-
设置函数
- 使用
HAL_NVIC_SetPriority()函数设置中断优先级。例如:HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 1); // 抢占优先级为2,响应优先级为1
- 使用
-
使能中断
- 使用
HAL_NVIC_EnableIRQ()函数使能中断。例如:HAL_NVIC_EnableIRQ(EXTI0_IRQn); // 使能EXTI0中断
- 使用
3、中断优先级的使用场景
- 实时性要求高的中断
- 高抢占优先级的中断可以打断低优先级中断的执行,适合处理实时性要求高的任务。
- 多中断系统
- 在多中断场景下,合理的优先级分配可以避免优先级倒置,确保系统稳定运行。
4、完整示例
以下是一个完整的代码示例,展示如何配置NVIC的优先级分组和中断优先级:
#include "stm32f10x.h"
void NVIC_Config(void)
{
// 设置优先级分组为分组2:2位抢占优先级,2位响应优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 配置EXTI0中断优先级:抢占优先级为1,响应优先级为1
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
int main(void)
{
// 初始化NVIC配置
NVIC_Config();
while (1)
{
// 主循环
}
}
总结
- NVIC的优先级分组:通过AIRCR寄存器的高4位配置,支持5种分组方式。
- 中断优先级设置:通过IPR寄存器设置抢占优先级和响应优先级。
- 配置函数:使用
NVIC_PriorityGroupConfig()设置分组,使用HAL_NVIC_SetPriority()设置优先级。
二. EXTI功能概述
EXTI(外部中断/事件控制器)是STM32中用于检测GPIO引脚电平变化并触发中断或事件的模块。它支持以下功能:
- 支持所有GPIO口:任意GPIO引脚都可以配置为外部中断。
- 触发方式:支持上升沿、下降沿、双边沿或软件触发。
- 通道数:支持16个GPIO引脚(EXTI0~EXTI15),外加PVD输出、RTC闹钟、USB唤醒等。
- 响应方式:中断响应(执行中断服务函数)或事件响应(触发其他外设操作)。
1. 为什么相同的Pin不能同时触发中断?
在STM32中,EXTI模块通过AFIO(复用功能引脚选择器)将GPIO引脚连接到中断通道。由于EXTI只有16个通道(EXTI0~EXTI15),而STM32有多个GPIO端口(如GPIOA、GPIOB、GPIOC等),因此每个通道只能连接一个引脚。
例如:
- PA0、PB0、PC0等引脚都对应EXTI0通道,但只能选择其中一个引脚连接到EXTI0。
- 如果需要多个中断引脚,必须选择不同Pin号的引脚(如PA0和PB1),否则会产生冲突。
AFIO的作用是选择GPIO引脚并将其映射到EXTI通道,因此在配置中断时,需要通过AFIO指定具体引脚。
2. EXTI配置步骤
步骤1:使能时钟
使能GPIO和AFIO的时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
步骤2:配置GPIO
将GPIO引脚配置为输入模式(如浮空输入、上拉输入):
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
步骤3:连接GPIO到EXTI
通过AFIO将GPIO引脚映射到EXTI通道:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); // 将PA0映射到EXTI0
步骤4:配置EXTI
设置EXTI的触发方式(上升沿、下降沿等)并使能中断:
EXTI_InitTypeDef EXTI_InitStructure;
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);
步骤5:配置NVIC
设置中断优先级并使能中断:
NVIC_InitTypeDef NVIC_InitStructure;
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);
步骤6:编写中断服务函数
在中断服务函数中处理中断事件并清除中断标志:
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) != RESET)
{
// 处理中断事件
EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志
}
}
4. 注意事项
- 相同Pin冲突:相同的Pin(如PA0和PB0)不能同时配置为EXTI中断。
- 中断优先级:合理配置中断优先级,避免优先级倒置。
- 中断标志清除:在中断服务函数中必须清除中断标志。
- 共享中断线:对于EXTI9_5和EXTI15_10等共享中断线,需要在中断服务函数中判断具体引脚。
四、中断的配置流程
中断配置涉及多个模块:RCC(时钟)、GPIO(通用输入输出)、AFIO(复用功能)、EXTI(外部中断控制器)和NVIC(中断控制器)。以下是详细步骤:
1. 时钟配置(RCC)
- 使能外设时钟:通过RCC模块使能GPIO、AFIO和EXTI的时钟。
- 示例代码:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
2. GPIO配置
- 配置GPIO引脚为中断模式:
- 设置GPIO引脚为输入模式。
- 配置引脚的上拉/下拉电阻(根据需要)。
- 示例代码:
GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入 GPIO_Init(GPIOA, &GPIO_InitStructure);
3. AFIO配置
- 映射中断线:通过AFIO模块将GPIO引脚映射到EXTI的中断线。
- 示例代码:
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); // 将PA0映射到EXTI0
4. EXTI配置
- 配置中断触发条件:设置EXTI的中断触发方式(上升沿、下降沿或双边沿)。
- 示例代码:
EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line = EXTI_Line0; // 配置EXTI0 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发 EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure);
5. NVIC配置
- 使能中断:通过NVIC使能对应的外部中断。
- 设置中断优先级:根据需求配置中断优先级。
- 示例代码:
NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; // 配置EXTI0中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);
6. 编写中断服务例程(ISR)
- 在对应的中断服务例程中编写处理逻辑。
- 示例代码:
void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) != RESET) // 检查EXTI0是否触发 { // 处理中断事件 EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志 } }
五、总结
- 中断的基本概念:中断源、中断向量、中断地址。
- 中断优先级:抢占优先级和子优先级,通过NVIC配置。
- 外部中断模块(EXTI)的使用场景:按键检测、传感器信号、通信同步等。
- 中断配置流程:
- 使能时钟(RCC)。
- 配置GPIO为中断模式。
- 映射GPIO到EXTI(通过AFIO)。
- 配置EXTI的中断触发方式。
- 通过NVIC使能中断并设置优先级。
- 编写中断服务例程(ISR)。
案例:使用外部中断控制LED状态
硬件需求
- STM32开发板(如STM32F103C8T6)。
- 一个按键。
- 一个LED灯。
- 限流电阻(如330Ω)。
- 连接线。
电路连接
- 将按键的一端连接到STM32的一个GPIO引脚(如PA0)。
- 将按键的另一端连接到地(GND)。
- 将LED的正极连接到另一个GPIO引脚(如PB0)。
- 将LED的负极通过限流电阻连接到地(GND)。
软件目标
- 配置PA0为外部中断输入。
- 配置PB0为普通GPIO输出。
- 当按键按下时,通过外部中断触发ISR,切换LED的状态。
代码实现
1. 初始化GPIO和外部中断
#include "stm32f10x.h"
void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能GPIOA和GPIOB的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
// 配置PA0为浮空输入(按键连接到PA0)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置PB0为推挽输出(LED连接到PB0)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 确保LED初始状态为关闭
GPIO_SetBits(GPIOB, GPIO_Pin_0);
}
void EXTI_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 使能AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// 配置PA0为中断线
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
// 配置EXTI0为下降沿触发
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
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);
}
2. 中断服务例程(ISR)
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) != RESET) // 检查是否EXTI0触发
{
// 切换LED状态
if (GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_0) == Bit_SET)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_0); // 打开LED
}
else
{
GPIO_SetBits(GPIOB, GPIO_Pin_0); // 关闭LED
}
// 清除中断标志
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
3. 主函数
int main(void)
{
// 初始化GPIO和外部中断
GPIO_Config();
EXTI_Config();
while (1)
{
// 主循环为空,所有操作在中断中完成
}
}
代码说明
-
GPIO配置:
- PA0配置为浮空输入,用于按键检测。
- PB0配置为推挽输出,用于控制LED。
-
外部中断配置:
- 将PA0映射到EXTI0。
- 配置EXTI0为下降沿触发(按键按下时触发)。
- 通过NVIC使能EXTI0中断。
-
中断服务例程(ISR):
- 检测是否为EXTI0触发。
- 切换LED的状态(通过读取PB0的状态并取反)。
- 清除中断标志,避免重复触发。
运行结果
- 当按键按下时,LED状态会切换(从亮变灭,或从灭变亮)。
- 主循环为空,所有操作都在中断服务例程中完成。
拓展
- 防抖处理:按键可能会产生抖动,可以通过软件延时或硬件滤波解决。
- 多按键检测:可以扩展到多个按键,通过配置多个外部中断线(如EXTI1、EXTI2等)。
- 组合功能:结合定时器中断,实现按键长按和短按的区分。
1369

被折叠的 条评论
为什么被折叠?



