中断:中断是指计算机运行过程中,出现某些意外情况
需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原
被暂停的程序继续运行
在STM32中(这里以STM32f407为例子),中断共有23条中断线
●EXTI0-EXTI 15连接到外部中断及GPIO引脚
● EXTI 线 16 连接到 PVD 输出
● EXTI 线 17 连接到 RTC 闹钟事件
● EXTI 线 18 连接到 USB OTG FS 唤醒事件
● EXTI 线 19 连接到以太网唤醒事件
● EXTI 线 20 连接到 USB OTG HS(在 FS 中配置)唤醒事件
● EXTI 线 21 连接到 RTC 入侵和时间戳事件
● EXTI 线 22 连接到 RTC 唤醒事件
其中外部中断与GPIO口中断相关
的16个一一对应相对应每个GPIO组引脚的每一个引脚;即如下图所示,PA组~PI组
的0号引脚
都连接到同一个EXTI0
中断线上,PA组~PI组
的所有1号引脚
都被连接到同一个EXTI1
中断线上,其他中断线以此类推。。。
注意:由于一个中断线上同时连接着多个GPIO的引脚,因此导致一个中断线一个时间内只能设置其中断线上的某一引脚作为它的
中断引脚
;
①中断触发
②CPU检测到指定寄存器的标志位为1
(下面中断处理函数会讲到)
③CPU跳转预先写好的中断处理函数运行代码
④运行结束返回原来CPU正在处理的事务进行处理
创建工程,需要勾选以下模块
如图所示由于中断是在APB2总线上的外设,因此在配置外部中断前
需要调用RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
函数用于启动时钟
使用中断,需要调用GPIO_Init()
函数先初始指定的GPIO引脚为输入模式,这里设置的中断源为GPIO组的PA0引脚作为中断引脚
void _IOInit(void)
{
//a.使能定时器所需时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//b.定义用于初始化GPIO引脚的结构体
GPIO_InitTypeDef GPIOa;
//c.将用到的外部中断引脚PA0 初始化为输入模式
GPIOa.GPIO_Pin = GPIO_Pin_0; //设置引脚
GPIOa.GPIO_Mode = GPIO_Mode_IN; //设置模式为输入模式
GPIOa.GPIO_Speed = GPIO_Medium_Speed; //这里速度指电平翻转速度中速
GPIOa.GPIO_PuPd = GPIO_PuPd_NOPULL; //无上拉下拉--浮空输入
GPIO_Init(GPIOA,&GPIOa); //初始化
}
这里主要是配置中断线的中断源、模式以及触发的方式等
void _ExtiInit()
{
//a.初始化中断所使用结构体
EXTI_InitTypeDef extiStruct;
//b.将指定的引脚连接到外部中断线上,EXTI0
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
//d.初始化外部中断线
extiStruct.EXTI_Line = EXTI_Line0;//启动第0个中断线 EXTI0-EXTI22
extiStruct.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式:中断模式
extiStruct.EXTI_Trigger = EXTI_Trigger_Rising;//触发方式:上升沿触发
extiStruct.EXTI_LineCmd = ENABLE;//使能开启
EXTI_Init(&extiStruct);//初始化中断
}
void NvicInit()
{
NVIC_InitTypeDef NvicStruct;
NvicStruct.NVIC_IRQChannel = EXTI0_IRQn;//指定中断管理的通道(通过stm32f4xx.h头文件查找IRQn可查找到该表)
/*
注意:抢占优先级从0开始(负数的为内核所使用的),0为除内核使用的优先级外最高的抢占优先级,
当同时有两个中断发送至CPU时,会先处理抢占优先级高的中断;
且只有抢占优先级相同的清况下才会去根据响应优先级来判断cpu先处理哪一个中断
*/
NvicStruct.NVIC_IRQChannelPreemptionPriority = 2; //设置该中断的抢占优先级(范围0-15)
NvicStruct.NVIC_IRQChannelSubPriority = 2; //设置该中断的响应优先级(范围0-15)
NvicStruct.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Init(&NvicStruct); //初始化
}
值得注意的是,抢占优先级与响应优先级共同所使用的空间只有4bit,可以在main函数开始处调用
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
分组函数(这个函数只能在main函数的开头地方调用,且整个工程只需进行一次分组)NVIC_PriorityGroup:参数范围 0-4 设置0:抢占优先级占0bit,响应优先级占4bit 1:抢占优先级占1bit,响应优先级占3bit 2:抢占优先级占2bit,响应优先级占2bit 3:抢占优先级占3bit,响应优先级占1bit 4:抢占优先级占4bit,响应优先级占0bit
在STM32中中断处理函数是需要使用其官方标准库所提供的中断处理函数名的,且返回值为void
空,我们需要去到startup_stm32f40_41xxx.s
的一个启动文件中去查找(可能有的放置的位置会不一样);
中断处理函数是当我们配置的中断引脚产生中断后会将中断信号线的中断线标志寄存器的标志位置为1
,如中断信号EXTI5产生中断后我们可以使用函数ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)
获取指定中断线的标志位是否为1,因此在使用部分中断线是共用一个中断处理函数的,可以在函数中判断中断信号线的标志位是否被置为1了;注意:每次中断处理完以后需要使用函数void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
把中断的标志位清除,否则会一直重复地进入中断处理函数
使用单个中断线的中断函数:
void EXTI3_IRQHandler()
{
/*...想要中断处理的代码...*/
//将中断标志位清为0
EXTI_ClearITPendingBit(EXTI_Line3);
}
在多个中断线一起使用的中断处理函数中,可如代码所示判断其中断线寄存器标志位进行判断当前触发的中断来自哪一条中断线
void EXTI9_5_IRQHandler(void)
{
//先判断 EXTi5 EXTI6的哪个标志位置为1
if(EXTI_GetITStatus(EXTI_Line5) == 1)
{
//能进来这里表示是EXTI5触发的中断
EXTI_ClearITPendingBit(EXTI_Line5);
}
if(EXTI_GetITStatus(EXTI_Line6) == 1)
{
//能进来这里表示是EXTI6触发的中断
EXTI_ClearITPendingBit(EXTI_Line6);
}
}
这是个人在对STM32学习中断时对于中断的理解,若有错误的地方敬请见谅并麻烦大佬们纠正一下。非常感谢