中断介绍



中断和轮询

中断的硬件连接


EXTI0

- 类似这种梯形就是多选一,例如PB1这个引脚的电平变化会映射到EXTI1硬联。
- STM32 通过SYSCFG(或 F1 系列的 AFIO)的 EXTICR 配置寄存器,将特定 GPIO 引脚(如 PA0、PB1 等)"绑定" 到对应的 EXTI 中断线(EXTIn);当该引脚发生电平变化(上升沿、下降沿等)时,就会触发对应 EXTI 线,进而产生中断请求或事件信号。
- 通过AFIO_EXTICRx配置GPIO线上的外部中断/事件,而如果想让GPIO线映射过来,首先要使能AFIO时钟。

- GPIO变化过程映射到EXTI线上
打开AFIO时钟- EXTI控制器内部
上升沿/下降沿
中断/事件
清除中断到来位
使能中断 - 不能屏蔽
NVIC介绍

NVIC中断优先级

速记:抢占式优先级是主优先级,响应优先级是子优先级。

- 中断A:主1子0
中断B:主0子2
-> 同时到 -> B- 中断A:主1子0
中断B:主0子2
-> A先到,执行, B到了 -> B会抢占A的资源- 中断A:主1子0
中断B:主1子2
-> 同时到 -> A先执行- 中断A:主1子0
中断B:主1子2
-> B先到, 执行, A到了 -> A不会抢占B的资源- 中断A:主1子2
中断B:主1子2
-> 同时到 -> 看表(谁在前谁先运行)

4bit:
主4子0
主:0-15子: ( < 2的n次方)
主3子1
主:0-7子:0-1
主2子2
主:0-3子:0-3
主1子3
主:0-1子:0-7主0子4
主: 子:0-15

外部EXTI配置

中断处理

思考:
PA0 - EXTI0 - EXTI - NVIC - CPU
谁触发的中断?
EXTI0触发的中断 ,并不是PA0,是PA0映射到了EXTI0,EXTI0一步步触发的中断。
stm32 - 中断处理函数 - 名字定好了 - 启动文件中
打开启动文件: XXX_IRQHandler
EXTI0_IRQHandler
1.中断处理函数
2.EXIT0触发的中断调用该函数
中断处理函数没有定义, 需要开发人员自定义

中断处理流程

中断处理函数

1.APP/EXTI目录
2.APP/EXTI
新建exti.c exti.h
3.添加工程组/ C/C++头文件路径
4.工程组添加 stm32f10x_exti.c - EXTI库函数
KEY0按键



相关库函数和结构体







代码示例:

KEY0按键

思路:
KEY0一旦按下,GPIOE4就映射到了EXTI4上,然后会有一个下降沿,EXTI4设置中断模式,又能检测下降沿,然后发送一个中断信号到NVIC,NVIC发现是EXTI4的中断,给你分配了一个主优先级和子优先级,然后NVIC把中断信号发送到CPU里。
相关库函数和结构体




代码示例:

注意:
EXTI和NVIC有时钟,但是它们自动使能。IRQn在stm32f10x.h头文件的第167行,下面就是各个中断源。
xxxx_IRQn - 中断源 - 谁触发的中断 -
NVIC_InitTypeDef - IRQChannel - 赋值
接下来调用中断处理函数


实战演练



代码示例:
exti.h:
cpp
// exti.h
#ifndef __EXTI_H__
#define __EXTI_H__
#include "stm32f10x.h"
// KEY0
extern void My_EXTI_Init(void);
#endif
exti.c:
cpp
// exti.c
#include "exti.h"
#include "systick.h"
#include "beep.h"
#include "key.h"
//
// 中断的初始化 - 发送中断的流程
// KEY0 - GPIOE4
// KEY0 - GPIOE4 - EXTI4 - EXTI控制器 - NVIC - CPU核
//
void My_EXTI_Init(void){
// 1.KEY0 - 配置为上拉输入
// 2.GPIOE4映射到EXTI4上
// 2.1打开AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// 2.2将GPIOE3映射到EXTI4上
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
// 3.EXTI控制器的配置
// EXTI4 - 中断模式 - 下降沿触发
EXTI_InitTypeDef EXTI_Config;
EXTI_Config.EXTI_Line = EXTI_Line4; // EXTI4
EXTI_Config.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_Config.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿
EXTI_Config.EXTI_LineCmd = ENABLE; // 使能
EXTI_Init(&EXTI_Config);
// 4.NVIC控制器的配置
// EXTI4触发的中断 - 主1 - 子2 - 使能
NVIC_InitTypeDef NVIC_Config;
NVIC_Config.NVIC_IRQChannel = EXTI4_IRQn; // EXTI4触发的中断
NVIC_Config.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_Config.NVIC_IRQChannelSubPriority = 2;
NVIC_Config.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_Config);
}
//
// 中断处理函数到启动文件(.s)中找
// 中断处理函数
// 触发该函数 - KEY0按下 - 蜂鸣器控制
//
void EXTI4_IRQHandler(void){
// 判断是否是EXTI4触发的中断
if(EXTI_GetITStatus(EXTI_Line4) == SET){
// 业务处理
delay_ms(10); // 去抖动
if(KEY0 == 0)
BEEP = !BEEP;
}
// 清除中断到来位
EXTI_ClearITPendingBit(EXTI_Line4);
}
main.c:
cpp
// main.c
#include "stm32f10x.h"
#include "led.h"
#include "beep.h"
#include "system.h"
#include "systick.h"
#include "key.h"
#include "exti.h"
int main(void)
{
// |--|--|
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// LED初始化
LED_Init();
// BEEP初始化
BEEP_Init();
// systick初始化
Systick_Init();
// KEY初始化
KEY_Init();
// EXTI初始化
// 作用:按下KEY0 - EXTI4 - EXTI -NVIC -> 中断信号 -> CPU核
My_EXTI_Init();
while(1)
{
// 业务逻辑 - 做其他事情
LED1 = !LED1;
delay_ms(1000);
}
// 启动文件 - main函数 - 初始化 - while(1)
// while(1) -------(1)-----------------> 执行
// ^CPU核
// /
// 按下KEY0->中断信号
// CPU核从(1)停止 -> 执行中断处理函数
// EXTI4_IRQHandler
// 执行完返回(1)继续
}
补充:
- 和中断相关的三类函数:
xxx_ITConfig - 打开/关闭控制器的某个中断
xxx_GetITStatus - 判断某个中断是否到来
xxx_ClearITPendingBit - 中断中断到来位- EXTI_GetITStatus(EXTI_Line4) == SET的作用
例如:
EXTI6触发了中断, 调用其中断处理函数
// EXTI5 6 7 8 9 触发的中断调用该函数
// 问 - 是哪个硬件线触发的中断调用的该函数
void EXTI9_5_IRQHandler(void){
if(EXTI_GetITStatus(EXTI_Line5) == SET){...}
else if(EXTI_GetITStatus(EXTI_Line6) == SET){...}
else if(EXTI_GetITStatus(EXTI_Line7) == SET){...}
else if(EXTI_GetITStatus(EXTI_Line8) == SET){...}
else if(EXTI_GetITStatus(EXTI_Line9) == SET){...}
}
拓展:
循环开关灯,按下key0触发中断,开启蜂鸣器;按下key_up触发中断,关闭蜂鸣器。
。
KEY_UP使用中断来触发:
PA0 - 下拉输入
低电平 - 高电平 - 低电平
检测 - 上升沿
谁触发的中断 - EXTI0
初始化函数:
1.PA0 映射到EXTI0
2.EXTI配置 - EXTI0, 中断模式, 上升沿, 使能
3.NVIC配置 - EXTI0, 主1, 子0, 使能
中断处理函数:
EXTI0_IRQHandler
exti.c:
cpp
// exti.c
#include "exti.h"
#include "systick.h"
#include "beep.h"
#include "key.h"
//
// 中断的初始化 - 发送中断的流程
// KEY0 - GPIOE4
// KEY0 - GPIOE4 - EXTI4 - EXTI控制器 - NVIC - CPU核
// KEY_UP - GPIOA0 - EXTI0 - EXTI控制器 - NVIC - CPU核
//
void My_EXTI_Init(void){
// 1.KEY0 - 配置为上拉输入
// 1.2KEY_UP配置为下拉输入
// 2.GPIOE4映射到EXTI4上
// 2.1打开AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// 2.2将GPIOE3映射到EXTI4上
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
// 2.2将GPIOA0映射到EXTI0上
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
// 3.EXTI控制器的配置
// EXTI4 - 中断模式 - 下降沿触发
EXTI_InitTypeDef EXTI_Config;
EXTI_Config.EXTI_Line = EXTI_Line4; // EXTI4
EXTI_Config.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_Config.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿
EXTI_Config.EXTI_LineCmd = ENABLE; // 使能
EXTI_Init(&EXTI_Config);
// 3.2EXTI0 - 中断模式 - 上升沿触发
EXTI_Config.EXTI_Line = EXTI_Line0;
EXTI_Config.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_Config.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_Config.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_Config);
// 4.NVIC控制器的配置
// EXTI4触发的中断 - 主1 - 子2 - 使能
NVIC_InitTypeDef NVIC_Config;
NVIC_Config.NVIC_IRQChannel = EXTI4_IRQn; // EXTI4触发的中断
NVIC_Config.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_Config.NVIC_IRQChannelSubPriority = 2;
NVIC_Config.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_Config);
// 4.2EXTI0触发的中断 - 主1 - 子0 - 使能
NVIC_Config.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_Config.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_Config.NVIC_IRQChannelSubPriority = 0;
NVIC_Config.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_Config);
}
//
// 中断处理函数到启动文件(.s)中找
// 中断处理函数
// 触发该函数 - KEY0按下 - 蜂鸣器控制
//
void EXTI4_IRQHandler(void){
// 判断是否是EXTI4触发的中断
if(EXTI_GetITStatus(EXTI_Line4) == SET){
// 业务处理
delay_ms(10); // 去抖动
if(KEY0 == 0)
BEEP = 1;
}
// 清除中断到来位
EXTI_ClearITPendingBit(EXTI_Line4);
}
void EXTI0_IRQHandler(void){
// 判断是否是EXTI0触发的中断
if(EXTI_GetITStatus(EXTI_Line0) == SET){
// 业务处理
delay_ms(10);
if(KEY_UP == 1)
BEEP = 0;
}
// 清除中断到来位
EXTI_ClearITPendingBit(EXTI_Line0);
}
main.c:
cpp
// main.c
#include "stm32f10x.h"
#include "led.h"
#include "beep.h"
#include "system.h"
#include "systick.h"
#include "key.h"
#include "exti.h"
int main(void)
{
// |--|--|
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// |--|--|
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
// LED初始化
LED_Init();
// BEEP初始化
BEEP_Init();
// systick初始化
Systick_Init();
// KEY初始化
KEY_Init();
// EXTI初始化
// 作用:按下KEY0 - EXTI4 - EXTI -NVIC -> 中断信号 -> CPU核
My_EXTI_Init();
while(1)
{
// 业务逻辑 - 做其他事情
LED1 = !LED1;
delay_ms(1000);
}
// 启动文件 - main函数 - 初始化 - while(1)
// while(1) -------(1)-----------------> 执行
// ^CPU核
// /
// 按下KEY0->中断信号
// CPU核从(1)停止 -> 执行中断处理函数
// EXTI4_IRQHandler
// 执行完返回(1)继续
}