这节的内容,野火的官方视频我反复看了好几次,但是感觉火哥在这块讲解的特别绕,理解起来很吃力,后来在看了一下其他老师的视频,结合一些书本资料和官方手册,才搞清楚STM32中断该怎么使用。
什么是中断?主线程序正常运行过程中,当中断信号产生时,系统先暂停主线程序,转去执行中断程序,当中断程序执行完后,在转回主线程序继续运行。
举个比较通俗的例子来说,加入一个人正在田里干活,家里人过来喊吃中午饭,此人就将手里的活放下回家吃饭,等吃完饭后在回来继续劳作。这里面田里干活就是主线程序,回家吃饭就是中断程序,而对应的家里人过来喊的这个动作,就是中断信号。
所以主线程序我们可以认为是正常运行的程序,中间插入的一些事情我们认为是突发的异常,只不过异常产生的时间不确定而已,那么在正常运行的程序过程中,可能会产生很多突发异常,可能有些异常是同时产生的,那么当出现多个同时产生的异常时,系统该如何处理呢?
用上一个例子来说明,正在田里干活,突然觉得肚子不舒服急需解手(例子而已,请勿介意),而此时家里人过来喊吃饭,那么该优先做那件事呢?当然是优先解手,等完事后再去吃饭(毕竟吃饭可以晚一会再去,肚子不舒服当然先解决咯)。那么事情就可以理顺了,先解手,在吃饭,吃完饭后再下地干活(解手后记得洗手哈!)。
所以这里就引出来一个概念,优先,当多个中断同时出现时,优先处理哪件事,就需要确定这些事的优先级。
看一下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 |
在上面的表中,需要注意的是,系统已经给各种中断(异常)给出了默认优先级,数字越小,优先级越高。而且再优先级类型中明确说明了哪些优先级可以设置,哪些优先级不可设置。也就是说,凡是标明了中断优先级类型为可设置的,则表明用户可以通过修改优先级级别,从而决定中断执行的先后顺序。
当单片机产生中断信号的时候,系统需要响应这个中断,对应的在表中最后一栏,看到每个对应的中断均有一个地址,需要注意的是,这个地址不是绝对地址,而是偏移地址,这里牵扯到地址重映射,后面学到了在分享。
在启动代码那一节中提到中断服务程序,当启动代码执行后,会映射所有中断服务程序,即我们说的中断向量表,在向量表中包含了所有内核和外设的中断服务函数名,这里需要注意,后续在用到中断服务程序时,一定要确保编写的中断服务程序名和向量表中的程序名一致,否则程序不会报错,但是当中断产生时,并不会去执行写好的中断服务程序,而是执行系统默认的程序,而系统默认的为无限空循环,那程序就会死到那里,不会出现响应。
中断向量控制器(Nested vectored interrupt controller)属于内核外设,主要作用是管理包括内核和片上所有外设的中断相关功能。
使用到的两个库文件为:core_cm4.h
和misc.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)
这个图中,按照中断优先级分为IP0-IP80(实际目前我使用的内核中断已经增加到93,看过野火官方出的教材上,中断源已经增加到97了),每个中断的IPx寄存器都有8位位宽,原则来说,每个中断的优先级可配置范围位0~255,其值越小,优先级越高,但是实际起到作用的只有4位。
此表中,可以看到实际起到作用的只有高4位,低4位并没有使用,STM32官方为了表示这4位,将优先级分为主(抢占)优先级和子优先级,两个中断同时触发时,主优先级高的先抢占执行权,主优先级相同时,则子优先级高的限制性,当然也可能存在主优先级和子优先级相同的情况,这个STM32官方也给出了解决办法,就是硬件编号越小的,优先级越高。
因此根据主优先级和子优先级的分类,STM32将优先级分为5组分别如下
可以用中标准库函数NVIC_PriorityGroupConfig()
来实现优先级的分组
在STM32中还提供了一个EXTI外部中断/事件控制器,用于产生事件/中断请求的边沿检测,数量有23个,每个输入线都可以进行单独配置,可以通过寄存器选择中断/事件类型,和相应的触发条件(上升沿触发、下降沿触发和边沿触发),并且也可以根据需求单独进行屏蔽。
typedef struct
{
uint32_t EXTI_Line; /* 选择输入线 */
EXTIMode_TypeDef EXTI_Mode; /*!< 触发模式:可选为外部中断或者事件模式 */
EXTITrigger_TypeDef EXTI_Trigger; /*!< 触发方式:上升沿、下降沿、边沿检测 */
FunctionalState EXTI_LineCmd; /*!< 使能位 */
}EXTI_InitTypeDef;
以中断触发按键检测为例,对EXTI进行分析:
F429中168个GPIO通过下面的方式连接到16个外部中断/事件控制器上
其中将GPIOA-GPIO中以引脚号从0-15分别挂在在EXTI0-EXIT15
我使用的开发板上,可以操作的按键电路GPIO有两个,分别为PA0和PC13,PA0是连接到EXTI0,PC13连接到EXTI13上,因此PA0的外部中断/事件线应该选择EXTI_Line0,而PC13则选择EXTI_Line13。
typedef enum
{
EXTI_Mode_Interrupt = 0x00, //中断模式
EXTI_Mode_Event = 0x04 //事件模式
}EXTIMode_TypeDef;
这个直接选择中断模式EXTI_Mode_Interrupt
就行。
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、编写中断服务程序。