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.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() 来实现优先级的分组
3、EXTI外部中断/事件控制器
在STM32中还提供了一个EXTI外部中断/事件控制器,用于产生事件/中断请求的边沿检测,数量有23个,每个输入线都可以进行单独配置,可以通过寄存器选择中断/事件类型,和相应的触发条件(上升沿触发、下降沿触发和边沿触发),并且也可以根据需求单独进行屏蔽。

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个外部中断/事件控制器上

其中将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 0void 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(); //初始化LEDEXTI_Key_Config(); //初始化GPIO、EXTI及NVIC,之后就是进入死循环等待中断触发并执行中断服务程序while(1){}
}
总结:
关于中断的使用,编程内容总结如下几个:
1、初始化用来产生中断的GPIO;
2、初始化EXTI外部中断/事件控制器;
3、配置NVIC中断向量表;
4、编写中断服务程序。
相关文章:
009 - STM32学习笔记 - 中断
009 - STM32学习笔记 - 中断 这节的内容,野火的官方视频我反复看了好几次,但是感觉火哥在这块讲解的特别绕,理解起来很吃力,后来在看了一下其他老师的视频,结合一些书本资料和官方手册,才搞清楚STM32中断该…...
分享几种js格式化金额的方法
一、使用 Intl.NumberFormat 构造函数 这是 JavaScript 中格式化金额的最常见方法。Intl.NumberFormat()构造函数接受两个参数:语言环境和选项。语言环境是为其格式化金额的语言和地区。选项是一组控制金额格式的属性。例如,可以使用样式属性来指定货币…...
圣墟传说H5手工端搭建架设教程
圣墟传说H5手工端搭建架设教程 大家好,我是艾西。今天给大家带来的游戏是由小说改编而来的大型玄幻MMORPG仙侠手游,也是比较老的游戏了虽然你可能没有怎么听过,但总会有一批喜欢的玩家热衷于它。 那么让我们直接进入正题开始操作࿱…...
编程(40)----------单例模式
在简单总结单例模式之前, 需要了解一下背景知识-----为何会有单例模式? 想象一个这样的场景, 打游戏的时候, 尝试很多次, 都未通关. 这种情况下是否会考虑查一下攻略? 一个好的攻略甚至可能连每一关的每一个场景由多少只怪物都说的清清楚楚. 再比如, 在以前上学的时候, 为了…...
Java开发 - 让你少走弯路的Redis主从实现单节点哨兵模式
前言 前一篇中,我们讲解了Redis主从的搭建方式,其实很简单呐有木有,都是配置,连句代码都没有,是不是感觉高估了Redis主从的搭建方式?哈哈,没关系,跟着博主,包你全会。今…...
Java的Atomic原子类
Java SDK 并发包里提供了丰富的原子类,我们可以将其分为五个类别,这五个类别提供的方法基本上是相似的,并且每个类别都有若干原子类。 对基本数据类型的变量值进行原子更新;对对象变量的指向进行原子更新;对数组里面的…...
离线语音控制新方案,NRK3303语音识别芯片在智能风扇的应用
随着科技的不断发展,智能家居已经成为人们日常生活中不可或缺的一部分,涌现出越来越多的智能设备,如智能门锁、智能灯泡、智能冰箱等,这些设备为人们的生活带来了更多的便利和创新。其中作为常见的风扇通过添加智能语音控制功能&a…...
在树莓派3B+上安装Pytorch1.7
在树莓派3B上安装Pytorch1.7(应该是最简单的方法了)_package libopenblas-dev has no installation cand_Chauncey_Wang的博客-CSDN博客由于项目要求,我需要在树莓派上安装pytorch这就有几个问题,首先吧,咱们和外面之间有一道长城,…...
Java性能权威指南-总结4
Java性能权威指南-总结4 Java性能调优工具箱操作系统的工具和分析CPU运行队列磁盘使用率网络使用率 Java监控工具基本的VM信息 Java性能调优工具箱 操作系统的工具和分析 CPU运行队列 快速小结 检查应用性能时,首先应该审查CPU时间。优化代码的目的是提升而不是…...
c语言全局变量和局部变量问题汇总
✅作者简介:嵌入式领域优质创作者,博客专家 ✨个人主页:咸鱼弟 🔥系列专栏:单片机设计专栏 📃推荐一款求职面试、刷题神器👉注册免费刷题 1、关键字static的作用是什么? 定义静态变…...
14.3:给定一个由字符串组成的数组strs,必须把所有的字符串拼接起来,返回所有可能的拼接结果中字典序最小的结果
给定一个由字符串组成的数组strs,必须把所有的字符串拼接起来,返回所有可能的拼接结果中字典序最小的结果 贪心写法 首先注意的一点是:如果两个字符串的长度相同,“abc”,“abd”,肯定是“abc”的字典序最…...
C++ 项目实战:跨平台的文件与视频压缩解压工具的设计与实现
C实战:跨平台文件与视频压缩解压工具的设计与实现 一、引言(Introduction)1.1 项目背景与目标1.2 技术选型:C、FFmpeg、libarchive、libzip、QtCFFmpeglibarchivelibzipQt 二、设计思路与框架(Design Philosophy and F…...
C和指针(二)数据
数据类型 1,C语言中仅有四种基本数据类型——整型、浮点型、指针、聚合类型(数组、结构等)。 2,整型包括字符、短整型、整型、长整型,且可以分为有符号和无符号两种版本。 1)长整型至少和整型一样长&#…...
PyTorch基础学习(一)
一.简介 PyTorch是一个基于Python的开源机器学习框架,它提供了丰富的工具和接口,用于构建和训练深度学习模型。PyTorch的主要特点包括: 动态计算图: PyTorch使用动态计算图,这意味着在模型构建过程中可以实时地进行计…...
chatgpt赋能python:Python代做:让您的网站更友好的SEO利器
Python代做:让您的网站更友好的SEO利器 如果您是一位网站管理员或者SEO工程师,您一定知道SEO对于网站的重要性。那么在SEO中,Python代做可以为您提供什么?在本文中,我们将通过介绍Python代做的技术和方法,…...
2022年都快结束了,还有人不会安卓录屏?在安卓上录制屏幕的的实现方式
前言 在我之前的文章 《以不同的形式在安卓中创建GIF动图》 中,我挖了一个坑,可以通过录制屏幕后转为 GIF 的方式来创建 GIF。只是当时我只是提了这么一个思路,并没有给出录屏的方式,所以本文的内容就是教大家如何通过调用系统 A…...
px rem em rpx 区别 用法
任意浏览器的默认字体高都是16px。所有未经调整的浏览器都符合: 1em16px。那么12px0.75em,10px0.625em。为了简化font-size的换算,需要在css中的body选择器中声明Font-size62.5%,这就使em值变为 16px*62.5%10px, 这样12px1.2em, 10px1em, 也就是说只需要…...
忆享聚焦|ChatGPT、AI、网络数字、游戏……近期热点资讯一览
“忆享聚焦”栏目第十四期来啦!本栏目汇集近期互联网最新资讯,聚焦前沿科技,关注行业发展动态,筛选高质量讯息,拓宽用户视野,让您以最低的时间成本获取最有价值的行业资讯。 目录 行业资讯 1.科技部部长王志…...
[Daimayuan] 树(C++,动态规划,01背包方案数)
有一棵 n n n 个节点的以 1 1 1 号点为根的有根树。现在可以对这棵树进行若干次操作,每一次操作可以选择树上的一个点然后删掉连接这个点和它的儿子的所有边。 现在我们想知道对于每一个 k k k ( 1 ≤ k ≤ n 1≤k≤n 1≤k≤n),最少需要多少次操作能…...
如何选择源代码加密软件
(SDC沙盒)和DLP、文档加密、云桌面等,其优缺点做客观比较如下: 比较内容安全容器(SDC沙盒)DLP文档加密云桌面代表厂家*信达卖咖啡、赛门贴科亿*通、IP噶德、*盾、*途四杰、深*服设计理念以隔离容器加准入技术为基础,构…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...
