009 - STM32学习笔记 - 中断

009 - STM32学习笔记 - 中断

这节的内容,野火的官方视频我反复看了好几次,但是感觉火哥在这块讲解的特别绕,理解起来很吃力,后来在看了一下其他老师的视频,结合一些书本资料和官方手册,才搞清楚STM32中断该怎么使用。

1、中断概念

什么是中断?主线程序正常运行过程中,当中断信号产生时,系统先暂停主线程序,转去执行中断程序,当中断程序执行完后,在转回主线程序继续运行。

举个比较通俗的例子来说,加入一个人正在田里干活,家里人过来喊吃中午饭,此人就将手里的活放下回家吃饭,等吃完饭后在回来继续劳作。这里面田里干活就是主线程序,回家吃饭就是中断程序,而对应的家里人过来喊的这个动作,就是中断信号。

所以主线程序我们可以认为是正常运行的程序,中间插入的一些事情我们认为是突发的异常,只不过异常产生的时间不确定而已,那么在正常运行的程序过程中,可能会产生很多突发异常,可能有些异常是同时产生的,那么当出现多个同时产生的异常时,系统该如何处理呢?

用上一个例子来说明,正在田里干活,突然觉得肚子不舒服急需解手(例子而已,请勿介意),而此时家里人过来喊吃饭,那么该优先做那件事呢?当然是优先解手,等完事后再去吃饭(毕竟吃饭可以晚一会再去,肚子不舒服当然先解决咯)。那么事情就可以理顺了,先解手,在吃饭,吃完饭后再下地干活(解手后记得洗手哈!)。

所以这里就引出来一个概念,优先,当多个中断同时出现时,优先处理哪件事,就需要确定这些事的优先级

看一下STM32中提供出来的中断,这里将stm32提供的中断打断分为两部分说明,如下表:

系统异常(内核)

位置 优先级 优先级 类型 名称 说明 地址
- - - 保留 0x0000 0000
-3 固定 Reset 复位 0x0000 0004
-2 固定 NMI 不可屏蔽中断,时钟安全系统 0x0000 0008
-1 固定 HardFault 所有类型的错误 0x0000 000C
0 固定 MemManage MPU 不匹配 0x0000 0010
1 可设置 BusFault 预取指失败,存储器访问失败 0x0000 0014
2 可设置 UsageFault 未定义的指令或非法状态 0x0000 0018
- - - 保留 0x0000 001C - 0x0000 002B
3 可设置 SVCall 通过 SWI 指令调用的系统服务 0x0000 002C
4 可设置 Debug Monitor 调试监控器 0x0000 0030
- - 保留 0x0000 0034
5 可设置 PendSV 可挂起的系统服务 0x0000 0038
6 可设置 Systick 系统嘀嗒定时器 0x0000 003C

外部中断(外设)

位置 优先级 优先级 类型 名称 说明 地址
0 7 可设置 WWDG 窗口看门狗中断 0x0000 0040
1 8 可设置 PVD 连接到 EXTI 线的可编程电压检测 (PVD) 中断 0x0000 0044
2 9 可设置 TAMP_STAMP 连接到 EXTI 线的入侵和时间戳中断 0x0000 0048
3 10 可设置 RTC_WKUP 连接到 EXTI 线的 RTC 唤醒中断 0x0000 004C
4 11 可设置 FLASH Flash 全局中断 0x0000 0050
5 12 可设置 RCC RCC 全局中断 0x0000 0054
6 13 可设置 EXTI0 EXTI 线 0 中断 0x0000 0058
7 14 可设置 EXTI1 EXTI 线 1 中断 0x0000 005C
8 15 可设置 EXTI2 EXTI 线 2 中断 0x0000 0060
9 16 可设置 EXTI3 EXTI 线 3 中断 0x0000 0064
10 17 可设置 EXTI4 EXTI 线 4 中断 0x0000 0068
11 18 可设置 DMA1_Stream0 DMA1 流 0 全局中断 0x0000 006C
12 19 可设置 DMA1_Stream1 DMA1 流 1 全局中断 0x0000 0070
13 20 可设置 DMA1_Stream2 DMA1 流 2 全局中断 0x0000 0074
14 21 可设置 DMA1_Stream3 DMA1 流 3 全局中断 0x0000 0078
15 22 可设置 DMA1_Stream4 DMA1 流 4 全局中断 0x0000 007C
16 23 可设置 DMA1_Stream5 DMA1 流 5 全局中断 0x0000 0080
17 24 可设置 DMA1_Stream6 DMA1 流 6 全局中断 0x0000 0084
18 25 可设置 ADC ADC1、 ADC2 和 ADC3 全局中断 0x0000 0088
19 26 可设置 CAN1_TX CAN1 TX 中断 0x0000 008C
20 27 可设置 CAN1_RX0 CAN1 RX0 中断 0x0000 0090
21 28 可设置 CAN1_RX1 CAN1 RX1 中断 0x0000 0094
22 29 可设置 CAN1_SCE CAN1 SCE 中断 0x0000 0098
23 30 可设置 EXTI9_5 EXTI 线 [9:5] 中断 0x0000 009C
24 31 可设置 TIM1_BRK_TIM9 TIM1 刹车中断和 TIM9 全局中断 0x0000 00A0
25 32 可设置 TIM1_UP_TIM10 TIM1 更新中断和 TIM10 全局中断 0x0000 00A4
26 33 可设置 TIM1_TRG_COM_TIM11 TIM1 触发和换相中断与 TIM11 全局 中断 0x0000 00A8
27 34 可设置 TIM1_CC TIM1 捕获比较中断 0x0000 00AC
28 35 可设置 TIM2 TIM2 全局中断 0x0000 00B0
29 36 可设置 TIM3 TIM3 全局中断 0x0000 00B4
30 37 可设置 TIM4 TIM4 全局中断 0x0000 00B8
31 38 可设置 I2C1_EV I2C1 事件中断 0x0000 00BC
32 39 可设置 I2C1_ER I2C1 错误中断 0x0000 00C0
33 40 可设置 I2C2_EV I2C2 事件中断 0x0000 00C4
34 41 可设置 I2C2_ER I2C2 错误中断 0x0000 00C8
35 42 可设置 SPI1 SPI1 全局中断 0x0000 00CC
36 43 可设置 SPI2 SPI2 全局中断 0x0000 00D0
37 44 可设置 USART1 USART1 全局中断 0x0000 00D4
38 45 可设置 USART2 USART2 全局中断 0x0000 00D8
39 46 可设置 USART3 USART3 全局中断 0x0000 00DC
40 47 可设置 EXTI15_10 EXTI 线 [15:10] 中断 0x0000 00E0
41 48 可设置 RTC_Alarm 连接到 EXTI 线的 RTC 闹钟( A 和 B) 中断 0x0000 00E4
42 49 可设置 OTG_FS WKUP 连接到 EXTI 线的 USB On-The-Go FS 唤醒中断 0x0000 00E8
43 50 可设置 TIM8_BRK_TIM12 TIM8 刹车中断和 TIM12 全局中断 0x0000 00EC
44 51 可设置 TIM8_UP_TIM13 TIM8 更新中断和 TIM13 全局中断 0x0000 00F0
45 52 可设置 TIM8_TRG_COM_TIM14 TIM8 触发和换相中断与 TIM14 全局 中断 0x0000 00F4
46 53 可设置 TIM8_CC TIM8 捕获比较中断 0x0000 00F8
47 54 可设置 DMA1_Stream7 DMA1 流 7 全局中断 0x0000 00FC
48 55 可设置 FSMC FSMC 全局中断 0x0000 0100
49 56 可设置 SDIO SDIO 全局中断 0x0000 0104
50 57 可设置 TIM5 TIM5 全局中断 0x0000 0108
51 58 可设置 SPI3 SPI3 全局中断 0x0000 010C
52 59 可设置 UART4 UART4 全局中断 0x0000 0110
53 60 可设置 UART5 UART5 全局中断 0x0000 0114
54 61 可设置 TIM6_DAC TIM6 全局中断, DAC1 和 DAC2 下溢错误中断 0x0000 0118
55 62 可设置 TIM7 TIM7 全局中断 0x0000 011C
56 63 可设置 DMA2_Stream0 DMA2 流 0 全局中断 0x0000 0120
57 64 可设置 DMA2_Stream1 DMA2 流 1 全局中断 0x0000 0124
58 65 可设置 DMA2_Stream2 DMA2 流 2 全局中断 0x0000 0128
59 66 可设置 DMA2_Stream3 DMA2 流 3 全局中断 0x0000 012C
60 67 可设置 DMA2_Stream4 DMA2 流 4 全局中断 0x0000 0130
61 68 可设置 ETH 以太网全局中断 0x0000 0134
62 69 可设置 ETH_WKUP 连接到 EXTI 线的以太网唤醒中断 0x0000 0138
63 70 可设置 CAN2_TX CAN2 TX 中断 0x0000 013C
64 71 可设置 CAN2_RX0 CAN2 RX0 中断 0x0000 0140
65 72 可设置 CAN2_RX1 CAN2 RX1 中断 0x0000 0144
66 73 可设置 CAN2_SCE CAN2 SCE 中断 0x0000 0148
67 74 可设置 OTG_FS USB On The Go FS 全局中断 0x0000 014C
68 75 可设置 DMA2_Stream5 DMA2 流 5 全局中断 0x0000 0150
69 76 可设置 DMA2_Stream6 DMA2 流 6 全局中断 0x0000 0154
70 77 可设置 DMA2_Stream7 DMA2 流 7 全局中断 0x0000 0158
71 78 可设置 USART6 USART6 全局中断 0x0000 015C
72 79 可设置 I2C3_EV I2C3 事件中断 0x0000 0160
73 80 可设置 I2C3_ER I2C3 错误中断 0x0000 0164
74 81 可设置 OTG_HS_EP1_OUT USB On The Go HS 端点 1 输出全局 中断 0x0000 0168
75 82 可设置 OTG_HS_EP1_IN USB On The Go HS 端点 1 输入全局 中断 0x0000 016C
76 83 可设置 OTG_HS_WKUP 连接到 EXTI 的 USB On The Go HS  唤醒中断 0x0000 0170
77 84 可设置 OTG_HS USB On The Go HS 全局中断 0x0000 0174
78 85 可设置 DCMI DCMI 全局中断 0x0000 0178
79 86 可设置 CRYP CRYP 加密全局中断 0x0000 017C
80 87 可设置 HASH_RNG 哈希和随机数发生器全局中断 0x0000 0180
81 88 可设置 FPU FPU 全局中断 0x0000 0184
82 89 可设置 UART7 UART 7 全局中断 0x0000 0188
83 90 可设置 UART8 UART 8 全局中断 0x0000 018C
84 91 可设置 SPI4 SPI 4 全局中断 0x0000 0190
85 92 可设置 SPI5 SPI 5 全局中断 0x0000 0194
86 93 可设置 SPI6 SPI 6 全局中断 0x0000 0198
a、优先级

在上面的表中,需要注意的是,系统已经给各种中断(异常)给出了默认优先级,数字越小,优先级越高。而且再优先级类型中明确说明了哪些优先级可以设置,哪些优先级不可设置。也就是说,凡是标明了中断优先级类型为可设置的,则表明用户可以通过修改优先级级别,从而决定中断执行的先后顺序。

b、中断地址

当单片机产生中断信号的时候,系统需要响应这个中断,对应的在表中最后一栏,看到每个对应的中断均有一个地址,需要注意的是,这个地址不是绝对地址,而是偏移地址,这里牵扯到地址重映射,后面学到了在分享。

c、中断服务子程序

在启动代码那一节中提到中断服务程序,当启动代码执行后,会映射所有中断服务程序,即我们说的中断向量表,在向量表中包含了所有内核和外设的中断服务函数名,这里需要注意,后续在用到中断服务程序时,一定要确保编写的中断服务程序名和向量表中的程序名一致,否则程序不会报错,但是当中断产生时,并不会去执行写好的中断服务程序,而是执行系统默认的程序,而系统默认的为无限空循环,那程序就会死到那里,不会出现响应。

2、中断向量控制器(NVIC)

中断向量控制器(Nested vectored interrupt controller)属于内核外设,主要作用是管理包括内核和片上所有外设的中断相关功能。

使用到的两个库文件为:core_cm4.hmisc.h,NVIC寄存器位于core_cm4.h中,固件库中NVIC预留了很多位,我猜大概率时为了后期扩展使用,目前我用到的固件库版本还比较老,一共才90多个中断,有兴趣的话可以去找一找最新的固件库看一下。

typedef struct
{
  __IO uint32_t ISER[8];                 /*!< 偏移量: 0x000 (读/写)  中断使能寄存器 			*/
       uint32_t RESERVED0[24];
  __IO uint32_t ICER[8];                 /*!< 偏移量: 0x080 (读/写)  中断清除寄存器         	*/
       uint32_t RSERVED1[24];
  __IO uint32_t ISPR[8];                 /*!< 偏移量: 0x100 (读/写)  中断使能悬起寄存器		  */
       uint32_t RESERVED2[24];
  __IO uint32_t ICPR[8];                 /*!< 偏移量: 0x180 (读/写)  中断清除悬起寄存器         */
       uint32_t RESERVED3[24];
  __IO uint32_t IABR[8];                 /*!< 偏移量: 0x200 (读/写)  终端有效位寄存器           */
       uint32_t RESERVED4[56];
  __IO uint8_t  IP[240];                 /*!< 偏移量: 0x300 (读/写)  中断优先级寄存器           */
       uint32_t RESERVED5[644];
  __O  uint32_t STIR;                    /*!< 偏移量: 0xE00 (只 读)  软件触发中断寄存器         */
}  NVIC_Type;

这里学习一下几个常用的寄存器

中断优先级寄存器(NVIC_IPRx)

009 - STM32学习笔记 - 中断_第1张图片

这个图中,按照中断优先级分为IP0-IP80(实际目前我使用的内核中断已经增加到93,看过野火官方出的教材上,中断源已经增加到97了),每个中断的IPx寄存器都有8位位宽,原则来说,每个中断的优先级可配置范围位0~255,其值越小,优先级越高,但是实际起到作用的只有4位。

在这里插入图片描述
此表中,可以看到实际起到作用的只有高4位,低4位并没有使用,STM32官方为了表示这4位,将优先级分为主(抢占)优先级和子优先级,两个中断同时触发时,主优先级高的先抢占执行权,主优先级相同时,则子优先级高的限制性,当然也可能存在主优先级和子优先级相同的情况,这个STM32官方也给出了解决办法,就是硬件编号越小的,优先级越高。

因此根据主优先级和子优先级的分类,STM32将优先级分为5组分别如下
009 - STM32学习笔记 - 中断_第2张图片
可以用中标准库函数NVIC_PriorityGroupConfig() 来实现优先级的分组

3、EXTI外部中断/事件控制器

在STM32中还提供了一个EXTI外部中断/事件控制器,用于产生事件/中断请求的边沿检测,数量有23个,每个输入线都可以进行单独配置,可以通过寄存器选择中断/事件类型,和相应的触发条件(上升沿触发、下降沿触发和边沿触发),并且也可以根据需求单独进行屏蔽。
009 - STM32学习笔记 - 中断_第3张图片

EXTI控制器框图
typedef struct
{
      uint32_t EXTI_Line;               /* 选择输入线 */
      EXTIMode_TypeDef EXTI_Mode;       /*!< 触发模式:可选为外部中断或者事件模式 */
      EXTITrigger_TypeDef EXTI_Trigger; /*!< 触发方式:上升沿、下降沿、边沿检测 */
      FunctionalState EXTI_LineCmd;     /*!< 使能位 */ 
}EXTI_InitTypeDef;

以中断触发按键检测为例,对EXTI进行分析:

a、EXTI_Line外部中断/事件线

F429中168个GPIO通过下面的方式连接到16个外部中断/事件控制器上

009 - STM32学习笔记 - 中断_第4张图片
其中将GPIOA-GPIO中以引脚号从0-15分别挂在在EXTI0-EXIT15

我使用的开发板上,可以操作的按键电路GPIO有两个,分别为PA0和PC13,PA0是连接到EXTI0,PC13连接到EXTI13上,因此PA0的外部中断/事件线应该选择EXTI_Line0,而PC13则选择EXTI_Line13。

b、EXTI_Mode,外部中断/事件模式控制器
typedef enum
{
      EXTI_Mode_Interrupt = 0x00,	//中断模式
      EXTI_Mode_Event = 0x04		//事件模式
}EXTIMode_TypeDef;

这个直接选择中断模式EXTI_Mode_Interrupt就行。

c、EXTI_Trigger外部中断/事件触发方式
typedef enum
{
      EXTI_Trigger_Rising = 0x08,			//上升沿触发
      EXTI_Trigger_Falling = 0x0C,  		//下降沿触发
      EXTI_Trigger_Rising_Falling = 0x10	//边沿触发
}EXTITrigger_TypeDef;

这里我选择的是上升沿触发EXTI_Trigger_Rising

在将上面几个寄存器设置好后,就可以使用中断使能控制器``EXTI_LineCmd`对外部中断/事件进行使能。

下面将程序关键部分代码贴上来,里面详细的可以看一下注释,程序内容是:按键按下,触发中断,控制LED灯亮灭。

关于LED灯的配置和使用,可以看一下之前关于LED灯的文章,这里着重说一下按键中断的相关配置和程序。

创建bsp_exti_key.c和bsp_exti_key.h

bsp_exti_key.h

#ifndef __BSP_KEY_H__
#define __BSP_KEY_H__

#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"

#define KEY1_PORT               GPIOA					//宏定义KEY1按键端口
#define KEY1_PIN                GPIO_Pin_0				//宏定义KEY1按键引脚号
#define KEY1_CLK                RCC_AHB1Periph_GPIOA	 //宏定义KEY1时钟

#define KEY1_EXTI_IRQ           EXTI0_IRQn				//配置KEY1中断源

#define KEY2_PORT               GPIOC					//宏定义KEY1按键端口
#define KEY2_PIN                GPIO_Pin_13				//宏定义KEY1按键引脚号
#define KEY2_CLK                RCC_AHB1Periph_GPIOC	 //宏定义KEY1时钟

#define KEY2_EXTI_IRQ           EXTI15_10_IRQn			//配置KEY2中断源

#define KEY1_EXTI_PROTSOURCE    EXTI_PortSourceGPIOA	 //宏定义KEY1端口源
#define KEY1_EXTI_PINSOURCE     EXTI_PinSource0			//宏定义KEY1引脚源	
#define KEY1_EXTI_LINE          EXTI_Line0				//宏定义KEY1输入线

#define KEY2_EXTI_PROTSOURCE    EXTI_PortSourceGPIOC	//宏定义KEY2端口源
#define KEY2_EXTI_PINSOURCE     EXTI_PinSource13		//宏定义KEY2引脚源	
#define KEY2_EXTI_LINE          EXTI_Line13				//宏定义KEY2输入线

#define KEY1_Handler    EXTI0_IRQHandler				//宏定义KEY1中断服务函数

#define KEY2_Handler    EXTI15_10_IRQHandler			//宏定义KEY2中断服务函数

#define KEY_ON  1
#define KEY_OFF 0

void EXTI_Key_Config(void);
uint8_t Key_Scan(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin);

#endif  /*__BSP_KEY_H__*/

这里需要注意的是在中断源的配置上,看到PA0的中断源是EXTI0_IRQn,而PC13的中断源配置为EXTI15_10_IRQn,这里可以看一下stm32f4xx.h中对于IRQn_Type结构体定义,这里就不大篇幅贴源码了。

bsp_exti_key.h

#include "bsp_exti_key.h"

static void NVIC_Config(void)				//配置中断向量表
{
    /*创建NVIC_InitTypeDef结构体*/
    NVIC_InitTypeDef NVIC_InitStructure;
    
    /*设置NVIC为优先级组1*/
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    
    /*设置主优先级为1*/
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    
    /*设置子优先级为1*/
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    
    /*使能中断通道*/
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    
    /*配置中断源为按键1*/
    NVIC_InitStructure.NVIC_IRQChannel = KEY1_EXTI_IRQ;
    
    /*初始化中断*/
    NVIC_Init(&NVIC_InitStructure);
    
    /*配置中断源为按键2,其余沿用上面的配置*/
    NVIC_InitStructure.NVIC_IRQChannel = KEY2_EXTI_IRQ;
    
    /*初始化中断*/
    NVIC_Init(&NVIC_InitStructure);
}

void EXTI_Key_Config(void)
{
    /*--------------------------------GPIO引脚功能配置--------------------------------------*/
    GPIO_InitTypeDef KEY_InitStruct;
    /* 定义EXTI结构体 */
    EXTI_InitTypeDef EXTI_InitStructure;
    
    RCC_AHB1PeriphClockCmd(KEY1_CLK|KEY2_CLK,ENABLE);
    
    KEY_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    
    KEY_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    
    KEY_InitStruct.GPIO_Pin = KEY1_PIN;
    
    GPIO_Init(KEY1_PORT,&KEY_InitStruct);

    KEY_InitStruct.GPIO_Pin = KEY2_PIN;
    
    GPIO_Init(KEY2_PORT,&KEY_InitStruct);
    /************************************配置NVIC向量控制器*********************************/
    NVIC_Config();
    
    /******************************配置EXTI外部中断/事件控制器*******************************/
    /*使能SYSCFG时钟,使用GPIO外部中断时,必须使能SYSCFG时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
    
    /* 将EXTI连接到按键1引脚 */
    SYSCFG_EXTILineConfig(KEY1_EXTI_PROTSOURCE,KEY1_EXTI_PINSOURCE);
    
    /* 将EXTI连接到按键1引脚 */
    SYSCFG_EXTILineConfig(KEY2_EXTI_PROTSOURCE,KEY2_EXTI_PINSOURCE);
    
    /* 设置EXTI模式为外部中断模式 */
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    
    /* 设置EXTI触发模式为上升沿 */
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    
    /* 使能EXTI */
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    
    /* 设置引脚为KEY1 */
    EXTI_InitStructure.EXTI_Line = KEY1_EXTI_LINE;
    
    /* 初始化KEY1 */
    EXTI_Init(&EXTI_InitStructure);
    /* 设置引脚为KEY1,其余沿用上面的配置*/
    EXTI_InitStructure.EXTI_Line = KEY2_EXTI_LINE;
    /* 初始化KEY1 */
    EXTI_Init(&EXTI_InitStructure);
}

在完成以上设置后,就可以写我们的中断服务函数了,中断服务函数是放在stm32f4xx_it.c中,具体实现如下:

/* 中断服务程序名需要和启动文件startup_stm32f429_439xx.s中中断向量表的中断服务程序名一致,否则系统会跑去执行默认的程序,即启动代码中的B .程序进入死循环,这里用宏定义将中断服务程序改为KEY1/KEY2_Handler,目的是为了方便看出来这个中断服务程序是用来做什么的 */
void KEY1_Handler(void)
{
    /* 当中断触发后,会将中断标志位状态置位,只需要读取中断标志位就可以知道终端是否触发 */
    if(EXTI_GetITStatus(KEY1_EXTI_LINE) != RESET)
    {
        LED_G_TOGGLE;					//中断触发后,用户程序
    }
    /* 中断触发后,需要将中断标志位清除,后续会等待中断再次触发 */
    EXTI_ClearITPendingBit(KEY1_EXTI_LINE);
}
void KEY2_Handler(void)
{
    if(EXTI_GetITStatus(KEY2_EXTI_LINE) != RESET)
    {
        LED_R_TOGGLE;
    }
    EXTI_ClearITPendingBit(KEY2_EXTI_LINE);
}

main.c

#include "stm32f4xx.h"
#include "bsp_led.h"
#include "bsp_exti_key.h"

int main(void)
{
    LED_Config();				//初始化LED
    EXTI_Key_Config();			//初始化GPIO、EXTI及NVIC,之后就是进入死循环等待中断触发并执行中断服务程序
    while(1)
    {
    }
}
总结:

关于中断的使用,编程内容总结如下几个:

1、初始化用来产生中断的GPIO;

2、初始化EXTI外部中断/事件控制器;

3、配置NVIC中断向量表;

4、编写中断服务程序。

你可能感兴趣的:(stm32,stm32,单片机,学习)