一、中断和事件
记录学习中断和事件的学习
1.1 NVIC(嵌套矢量中断控制器)
NVIC管理着所有中断,包括核心异常。中断分为内部中断(也称异常)和外部中断,根据core_cm4.h
文件,NVIC寄存器映射如下
typedef struct
{
__IOM uint32_t ISER[8U]; /* 中断使能寄存器 */
uint32_t RESERVED0[24U];
__IOM uint32_t ICER[8U]; /* 中断失能寄存器 */
uint32_t RESERVED1[24U];
__IOM uint32_t ISPR[8U]; /* 中断挂起使能寄存器 */
uint32_t RESERVED2[24U];
__IOM uint32_t ICPR[8U]; /* 中断挂起失能寄存器 */
uint32_t RESERVED3[24U];
__IOM uint32_t IABR[8U]; /* 中断激活寄存器 */
uint32_t RESERVED4[56U];
__IOM uint8_t IP[240U]; /* 中断优先级寄存器 */
uint32_t RESERVED5[644U];
__OM uint32_t STIR; /* 软件触发中断寄存器 */
} NVIC_Type;
根据编程手册中NVIC寄存器映射图如下
由图可以看出,NVIC_ISER
中断使能寄存器使用了8个,可完成240个中断的使能;NVIC_ICER
中断失能寄存器使用了8个,可完成240个中断的失能;NVIC_ISPR
中断挂起使能寄存器使用了8个,可完成240个中断挂起的使能;NVIC_ICPR
中断挂起失能寄存器使用了8个,可完成240个中断挂起的失能;NVIC_IABR
中断激活寄存器使用了8个,可完成240个中断的激活;NVIC_IPR
中断优先级寄存器使用了60个,可完成240个中断的优先级配置;NVIC_STIP
软件触发中断寄存器使用了1个,以触发软件生成中断,写入的值即为需要软件触发中断的ID,仅用了8位,可设置0-239范围。NVIC通过NVIC_IPRx
寄存器用来配置中断优先级
由图可以看出,NVIC_IPR
寄存器共使用了60个,每个NVIC_IPRx
分为了4个用于配置优先级的字段,每个字段8位用于配置一个中断优先级
该寄存器为每一个中断提供了一个8位字段来设置优先级,理论上可设置256个优先级,在F407中只使用了高4位,因此可设置16个优先级。
1.2 优先级分组
这4位又分为高n位的抢占优先级和低4-n位的子优先级,抢占优先级高的可以中断嵌套,子优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队,编号越小,优先级越高,在文件stm32f4xx_hal_cortex.c
中给出了优先级分组
/**
* @brief Sets the priority grouping field (preemption priority and subpriority)
* using the required unlock sequence.
* @param PriorityGroup The priority grouping bits length.
* This parameter can be one of the following values:
* @arg NVIC_PRIORITYGROUP_0: 0 bits for preemption priority
* 4 bits for subpriority
* @arg NVIC_PRIORITYGROUP_1: 1 bits for preemption priority
* 3 bits for subpriority
* @arg NVIC_PRIORITYGROUP_2: 2 bits for preemption priority
* 2 bits for subpriority
* @arg NVIC_PRIORITYGROUP_3: 3 bits for preemption priority
* 1 bits for subpriority
* @arg NVIC_PRIORITYGROUP_4: 4 bits for preemption priority
* 0 bits for subpriority
* @note When the NVIC_PriorityGroup_0 is selected, IRQ preemption is no more possible.
* The pending IRQ priority will be managed only by the subpriority.
* @retval None
*/
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
/* Check the parameters */
assert_param(IS_NVIC_PRIORITY_GROUP(PriorityGroup));
/* Set the PRIGROUP[10:8] bits according to the PriorityGroup parameter value */
NVIC_SetPriorityGrouping(PriorityGroup);
}
优先级分组 | 抢占优先级 | 子优先级 | 高4位使用情况 |
---|---|---|---|
NVIC_PriorityGroup_0 | 0 级抢占优先级 | 0-15 级子优先级 | 0bit 用于抢占优先级 ,4bit 全用于子优先级 |
NVIC_PriorityGroup_1 | 0-1 级抢占优先级 | 0-7 级子优先级 | 1bit 用于抢占优先级 ,3bit 全用于子优先级 |
NVIC_PriorityGroup_2 | 0-3 级抢占优先级 | 0-3 级抢占优先级 | 2bit 用于抢占优先级 ,2bit 全用于子优先级 |
NVIC_PriorityGroup_3 | 0-7 级抢占优先级 | 0-1 级抢占优先级 | 3bit 用于抢占优先级 ,1bit 全用于子优先级 |
NVIC_PriorityGroup_4 | 0-15 级抢占优先级 | 0 级抢占优先级 | 4bit 用于抢占优先级 ,0bit 全用于子优先级 |
系统上电复位后默认使用的是优先级分组0,在F407中总共有82个中断可设置优先级。
1.3 常用NVIC API
在stm32f4xx_hal_cortex.c
文件中,给出了配置NVIC中断的函数
HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
函数配置中断优先级分组HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
函数配置具体外设中断通道的抢占优先级和子优先级HAL_NVIC_EnableIRQ(IRQn_Type IRQn)
函数使能中断请求
在编程时,中断使能一般有两个门,外设使能相应的中断送入NVIC,NVIC使能后才能响应中断。
在stm32f407xx.h
文件中列举了中断通道IRQn
,具体如下
typedef enum
{
/****** Cortex-M4 Processor Exceptions Numbers ****************************************************************/
NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */
MemoryManagement_IRQn = -12, /*!< 4 Cortex-M4 Memory Management Interrupt */
BusFault_IRQn = -11, /*!< 5 Cortex-M4 Bus Fault Interrupt */
UsageFault_IRQn = -10, /*!< 6 Cortex-M4 Usage Fault Interrupt */
SVCall_IRQn = -5, /*!< 11 Cortex-M4 SV Call Interrupt */
DebugMonitor_IRQn = -4, /*!< 12 Cortex-M4 Debug Monitor Interrupt */
PendSV_IRQn = -2, /*!< 14 Cortex-M4 Pend SV Interrupt */
SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt */
/****** STM32 specific Interrupt Numbers **********************************************************************/
WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */
PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */
TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line */
RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line */
FLASH_IRQn = 4, /*!< FLASH global Interrupt */
RCC_IRQn = 5, /*!< RCC global Interrupt */
EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */
EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */
EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */
EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */
EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */
DMA1_Stream0_IRQn = 11, /*!< DMA1 Stream 0 global Interrupt */
DMA1_Stream1_IRQn = 12, /*!< DMA1 Stream 1 global Interrupt */
DMA1_Stream2_IRQn = 13, /*!< DMA1 Stream 2 global Interrupt */
DMA1_Stream3_IRQn = 14, /*!< DMA1 Stream 3 global Interrupt */
DMA1_Stream4_IRQn = 15, /*!< DMA1 Stream 4 global Interrupt */
DMA1_Stream5_IRQn = 16, /*!< DMA1 Stream 5 global Interrupt */
DMA1_Stream6_IRQn = 17, /*!< DMA1 Stream 6 global Interrupt */
ADC_IRQn = 18, /*!< ADC1, ADC2 and ADC3 global Interrupts */
CAN1_TX_IRQn = 19, /*!< CAN1 TX Interrupt */
CAN1_RX0_IRQn = 20, /*!< CAN1 RX0 Interrupt */
CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */
CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */
EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */
TIM1_BRK_TIM9_IRQn = 24, /*!< TIM1 Break interrupt and TIM9 global interrupt */
TIM1_UP_TIM10_IRQn = 25, /*!< TIM1 Update Interrupt and TIM10 global interrupt */
TIM1_TRG_COM_TIM11_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt and TIM11 global interrupt */
TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */
TIM2_IRQn = 28, /*!< TIM2 global Interrupt */
TIM3_IRQn = 29, /*!< TIM3 global Interrupt */
TIM4_IRQn = 30, /*!< TIM4 global Interrupt */
I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */
I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */
I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */
I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */
SPI1_IRQn = 35, /*!< SPI1 global Interrupt */
SPI2_IRQn = 36, /*!< SPI2 global Interrupt */
USART1_IRQn = 37, /*!< USART1 global Interrupt */
USART2_IRQn = 38, /*!< USART2 global Interrupt */
USART3_IRQn = 39, /*!< USART3 global Interrupt */
EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */
RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line Interrupt */
OTG_FS_WKUP_IRQn = 42, /*!< USB OTG FS Wakeup through EXTI line interrupt */
TIM8_BRK_TIM12_IRQn = 43, /*!< TIM8 Break Interrupt and TIM12 global interrupt */
TIM8_UP_TIM13_IRQn = 44, /*!< TIM8 Update Interrupt and TIM13 global interrupt */
TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */
TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare global interrupt */
DMA1_Stream7_IRQn = 47, /*!< DMA1 Stream7 Interrupt */
FSMC_IRQn = 48, /*!< FSMC global Interrupt */
SDIO_IRQn = 49, /*!< SDIO global Interrupt */
TIM5_IRQn = 50, /*!< TIM5 global Interrupt */
SPI3_IRQn = 51, /*!< SPI3 global Interrupt */
UART4_IRQn = 52, /*!< UART4 global Interrupt */
UART5_IRQn = 53, /*!< UART5 global Interrupt */
TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC1&2 underrun error interrupts */
TIM7_IRQn = 55, /*!< TIM7 global interrupt */
DMA2_Stream0_IRQn = 56, /*!< DMA2 Stream 0 global Interrupt */
DMA2_Stream1_IRQn = 57, /*!< DMA2 Stream 1 global Interrupt */
DMA2_Stream2_IRQn = 58, /*!< DMA2 Stream 2 global Interrupt */
DMA2_Stream3_IRQn = 59, /*!< DMA2 Stream 3 global Interrupt */
DMA2_Stream4_IRQn = 60, /*!< DMA2 Stream 4 global Interrupt */
ETH_IRQn = 61, /*!< Ethernet global Interrupt */
ETH_WKUP_IRQn = 62, /*!< Ethernet Wakeup through EXTI line Interrupt */
CAN2_TX_IRQn = 63, /*!< CAN2 TX Interrupt */
CAN2_RX0_IRQn = 64, /*!< CAN2 RX0 Interrupt */
CAN2_RX1_IRQn = 65, /*!< CAN2 RX1 Interrupt */
CAN2_SCE_IRQn = 66, /*!< CAN2 SCE Interrupt */
OTG_FS_IRQn = 67, /*!< USB OTG FS global Interrupt */
DMA2_Stream5_IRQn = 68, /*!< DMA2 Stream 5 global interrupt */
DMA2_Stream6_IRQn = 69, /*!< DMA2 Stream 6 global interrupt */
DMA2_Stream7_IRQn = 70, /*!< DMA2 Stream 7 global interrupt */
USART6_IRQn = 71, /*!< USART6 global interrupt */
I2C3_EV_IRQn = 72, /*!< I2C3 event interrupt */
I2C3_ER_IRQn = 73, /*!< I2C3 error interrupt */
OTG_HS_EP1_OUT_IRQn = 74, /*!< USB OTG HS End Point 1 Out global interrupt */
OTG_HS_EP1_IN_IRQn = 75, /*!< USB OTG HS End Point 1 In global interrupt */
OTG_HS_WKUP_IRQn = 76, /*!< USB OTG HS Wakeup through EXTI interrupt */
OTG_HS_IRQn = 77, /*!< USB OTG HS global interrupt */
DCMI_IRQn = 78, /*!< DCMI global interrupt */
RNG_IRQn = 80, /*!< RNG global Interrupt */
FPU_IRQn = 81 /*!< FPU global interrupt */
} IRQn_Type;
1.4 EXTI(外部中断/事件)
EXTI外部中断/事件控制器包含多达23个边缘检测器,用于产生事件/中断请求。每条输入线均可独立配置,以选择类型(中断或事件)以及相应的触发事件(上升沿、下降沿或两者)。每条线路也可单独屏蔽。挂起寄存器用于维护中断请求的状态线。EXTI可监测指定GPIO的电平信号,触发事件发生时会向NVIC发出中断请求,经NVIC裁决后即可MCU响应中断。EXTI框图如下
由图知EXTI可分为两部分,一个是产生中断,一个是产生事件,具体分析如下
-
中断
中断是表示有某个事件发送产生中断并跳转到对应的中断处理程序中。外部信号经GPIO进入边沿监测器,触发事件发生后输出到一个或门;软件中断事件寄存器允许软件启动中断,输出到一个与门;中断屏蔽寄存器可决定中断的产生,然后进入挂起请求寄存器;最后由挂起请求寄存器决定将中断输送到NVIC。中断需要CPU参与。 -
事件
事件是表示检测到某一动作(电平边沿)触发事件发生了。产生事件是在或门之后输出到另一个与门,该与门结果由事件屏蔽寄存器控制输出;产生有效输出后,脉冲发生器会输出一个脉冲信号。事件不CPU参与。
这里给出事件和中断之间的区别:中断可被更优先的中断屏蔽,事件不会;事件是一个触发信号(脉冲),中断则是一个固定电平信号;中断一定要有中断服务函数,必须有CPU参入,事件不需要。
1.5 外部中断/事件线映射
GPIO以以下方式连接16组外部中断/事件线
剩下的7组外部中断/事件以以下方式连接
EXTI基本结构如下
IO口外部中断分配了7个中断向量
在启动文件中,给出了中断函数入口
EXTI0_IRQHandler ; EXTI Line0
EXTI1_IRQHandler ; EXTI Line1
EXTI2_IRQHandler ; EXTI Line2
EXTI3_IRQHandler ; EXTI Line3
EXTI4_IRQHandler ; EXTI Line4
EXTI9_5_IRQHandler ; External Line[9:5]s
EXTI15_10_IRQHandler ; External Line[15:10]s
由图和启动代码可以看出外部中断线5-9公用一个中断向量,共用一个服务函数;外部中断线10-15共用一个中断向量,共用一个服务函数。
1.6 EXTI寄存器
在stm32f407xx.h
文件中,给出了EXTI寄存器的定义
/**
* @brief External Interrupt/Event Controller
*/
typedef struct
{
__IO uint32_t IMR; /*!< EXTI 中断屏蔽寄存器, Address offset: 0x00 */
__IO uint32_t EMR; /*!< EXTI 时间屏蔽寄存器, Address offset: 0x04 */
__IO uint32_t RTSR; /*!< EXTI 上升沿触发选择寄存器, Address offset: 0x08 */
__IO uint32_t FTSR; /*!< EXTI 下降沿触发选择寄存器, Address offset: 0x0C */
__IO uint32_t SWIER; /*!< EXTI 软件中断事件寄存器, Address offset: 0x10 */
__IO uint32_t PR; /*!< EXTI 挂起寄存器, Address offset: 0x14 */
} EXTI_TypeDef;
二、HAL编程
这里设计使用外接的按键作为触发源,上升沿触发MCU产生中断并在中断服务函数中实现点亮LED的任务,编程步骤如下
- 配置GPIO
- 配置NVIC
- 配置EXTI
- 编写中断服务函数
在文件stm32f4xx_hal_gpio.c
中存在两个有关EXTI中断的函数:void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
和__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
,前一个函数是在中断服务函数中对GPIO外部中断进行检测;然后调用后一个回调函数执行中断响应,需要进行重定义。
key.c
文件
/*
*********************************************************************************************************
* Module Description
*
* 独立按键驱动模块,板载三个按键K_UP、K0和K1
* 按键K_UP(WK_UP可作为待机唤醒选项)按下为高电平,设置为下拉输入模式
* 按键K0和按键K1按下为低电平,设置为上拉输入模式
* 配置EXTI外部中断,按键按下实现LED翻转
* 优先级:K_UP > K_0 > K_1
*********************************************************************************************************
*/
#include "bsp.h"
/*
**********************************************************************************
* @brief 按键硬件初始化,配置按键对应的GPIO
* @param None
* @return None
* @note
**********************************************************************************
*/
static void bsp_InitKeyHard()
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 打开GPIO时钟 */
GPIO_CLK_ENABLE_K_UP();
GPIO_CLK_ENABLE_K0();
GPIO_CLK_ENABLE_K1();
/* 配置K_UP为下拉输入,K0和K1为上拉输入 */
GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING; // 上升沿触发中断输入模式
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // GPIO速度
GPIO_InitStructure.Pin = GPIO_PIN_K_UP; // K_UP引脚
GPIO_InitStructure.Pull = GPIO_PULLDOWN; // 下拉模式
HAL_GPIO_Init(GPIO_PORT_K_UP, &GPIO_InitStructure);
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); // 第4优先级组
HAL_NVIC_SetPriority(GPIO_IRQ_K_UP, 1, 0); // 第1抢占优先级,第0子优先级
HAL_NVIC_EnableIRQ(GPIO_IRQ_K_UP); // 使能K_UP按键中断
GPIO_InitStructure.Pin = GPIO_PIN_K0; // K0引脚
GPIO_InitStructure.Pull = GPIO_PULLUP; // 上拉模式
HAL_GPIO_Init(GPIO_PORT_K0, &GPIO_InitStructure);
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); // 第4优先级组
HAL_NVIC_SetPriority(GPIO_IRQ_K0, 2, 0); // 第2抢占优先级,第0子优先级
HAL_NVIC_EnableIRQ(GPIO_IRQ_K0); // 使能K0按键中断
GPIO_InitStructure.Pin = GPIO_PIN_K1; // K1引脚
GPIO_InitStructure.Pull = GPIO_PULLUP; // 上拉模式
HAL_GPIO_Init(GPIO_PORT_K1, &GPIO_InitStructure);
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); // 第4优先级组
HAL_NVIC_SetPriority(GPIO_IRQ_K1, 3, 0); // 第3抢占优先级,第0子优先级
HAL_NVIC_EnableIRQ(GPIO_IRQ_K1); // 使能K1按键中断
}
/*
**********************************************************************************
* @brief 初始化按键驱动
* @param None
* @return None
* @note
**********************************************************************************
*/
void bsp_InitKey()
{
bsp_InitKeyHard();
}
/*
**********************************************************************************
* @brief 阻塞式按键扫描函数
* @param None
* @return 键值,如下
* K_UP按下返回3
* K0按下返回1
* K1按下返回2
* @note
**********************************************************************************
*/
uint8_t bsp_ScanKey()
{
uint8_t key_value = 0; // 键值
// K_UP按键检测
if(K_UP_NOW == 1)
{
bsp_DelayMs(20);
while(K_UP_NOW == 1);
bsp_DelayMs(20);
key_value = 3;
}
// K0按键检测
if(K0_NOW == 0)
{
bsp_DelayMs(20);
while(K0_NOW == 0);
bsp_DelayMs(20);
key_value = 1;
}
// K1按键检测
if(K1_NOW == 0)
{
bsp_DelayMs(20);
while(K1_NOW == 0);
bsp_DelayMs(20);
key_value = 2;
}
return key_value;
}
/* Interrupt function */
/*
**********************************************************************************
* @brief EXTI0中断服务函数,按键K_UP触发EXTI0_IRQHandler函数
* @param None
* @return None
* @note
**********************************************************************************
*/
void EXTI0_IRQHandler()
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_K_UP); // 处理K_UP引脚中断请求
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_K_UP); // HAL库默认先清除中断再处理回调,退出时再清除一次中断
}
/*
**********************************************************************************
* @brief EXTI4中断服务函数,按键K_0触发EXTI4_IRQHandler函数
* @param None
* @return None
* @note
**********************************************************************************
*/
void EXTI4_IRQHandler()
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_K0); // 处理K0引脚中断请求
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_K0); // HAL库默认先清除中断再处理回调,退出时再清除一次中断
}
/*
**********************************************************************************
* @brief EXTI3中断服务函数,按键K_1触发EXTI3_IRQHandler函数
* @param None
* @return None
* @note
**********************************************************************************
*/
void EXTI3_IRQHandler()
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_K1); // 处理K_1引脚中断请求
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_K1); // HAL库默认先清除中断再处理回调,退出时再清除一次中断
}
/*
**********************************************************************************
* @brief EXTI线检测回调函数,所有外部中断服务函数会调用此函数
* @param GPIO_Pin:外部GPIO中断引脚号
* @return None
* @note
**********************************************************************************
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
bsp_DelayMs(20); // 去抖
switch(GPIO_Pin)
{
case GPIO_PIN_K_UP:
{
bsp_LedToggle(1);
bsp_LedToggle(2);
printf("K_UP press!!");
break;
}
case GPIO_PIN_K0:
{
bsp_LedToggle(1);
printf("K_0 press!!");
break;
}
case GPIO_PIN_K1:
{
bsp_LedToggle(2);
printf("K_1 press!!");
break;
}
default:
break;
}
}
key.h
文件
#ifndef __BSP_KEYS_H
#define __BSP_KEYS_H
/* private define */
#define HARD_KEY_NUM 3 // 实体按键个数
// 按键引脚定义,板载三个按键K_UP、K0和K1,所接引脚分别是PA0、PE4和PE3
#define GPIO_CLK_ENABLE_K_UP() do{__HAL_RCC_GPIOA_CLK_ENABLE();}while(0)
#define GPIO_PORT_K_UP GPIOA
#define GPIO_PIN_K_UP GPIO_PIN_0
#define GPIO_IRQ_K_UP EXTI0_IRQn
#define GPIO_CLK_ENABLE_K0() do{__HAL_RCC_GPIOE_CLK_ENABLE();}while(0)
#define GPIO_PORT_K0 GPIOE
#define GPIO_PIN_K0 GPIO_PIN_4
#define GPIO_IRQ_K0 EXTI4_IRQn
#define GPIO_CLK_ENABLE_K1() do{__HAL_RCC_GPIOE_CLK_ENABLE();}while(0)
#define GPIO_PORT_K1 GPIOE
#define GPIO_PIN_K1 GPIO_PIN_3
#define GPIO_IRQ_K1 EXTI3_IRQn
// 按键引脚当前电平
#define K_UP_NOW HAL_GPIO_ReadPin(GPIO_PORT_K_UP, GPIO_PIN_K_UP)
#define K0_NOW HAL_GPIO_ReadPin(GPIO_PORT_K0, GPIO_PIN_K0)
#define K1_NOW HAL_GPIO_ReadPin(GPIO_PORT_K1, GPIO_PIN_K1)
// 键值定义
#define PRES_K_UP 3
#define PRES_K0 1
#define PRES_K1 2
/* public statement */
void bsp_InitKey();
uint8_t bsp_ScanKey();
#endif