在物联网产品开发中,经常需要按键检测,特别在同时处理长按和短按的需求下,
尽量减少单片机的工作负担,可以在周期循环中,使用延时消抖,
超时分段处理的方法来检测长短按事件,再处理不同的逻辑。
以stm32f2为例gpio_interrupt.c主要是检测一个被设定为配对wps功能的按键输入事件,
下降沿中断触发,延时消抖,超时统计长按和短按。
#ifndef __GPIO_INTERRUPT__
#define __GPIO_INTERRUPT__
#include "stm32f2xx_hal.h"
#include "it_config.h"
#include "comtypes.h"
#define WPS_PRESSKEY_PIN GPIO_PIN_15
#define WPS_PRESSKEY_TYPE GPIOA
#define WPS_PRESSKEY_ITMODE GPIO_MODE_IT_FALLING
#define WPS_PRESSKEY_IRQ EXTI15_10_IRQn
//20ms消抖
#define WPS_DEBOUNCE_TIME (20)
//3s长按
#define WPS_LONG_PRESS_TIME (WPS_DEBOUNCE_TIME * 150)
#define WPS_PressKey_Read() HAL_GPIO_ReadPin(WPS_PRESSKEY_TYPE,WPS_PRESSKEY_PIN)
void WPS_PressKey_Init(void);
int WPS_PressKey_Check(uint16_t looptime);
#endif
#include "gpio_interrupt.h"
/**
以一个检测WPS按键的引脚输入中断为例包括,
按键中断触发标志检测,
按键消抖延时计时,
返回检测按键的状态(无效,短按,长按)。
*/
static __IO BOOLEAN is_wps_press_key_pressed = FALSE;
static __IO int wps_press_key_debounce_cnt = 0;
static __IO int wps_timeout = 0;
static __IO int wps_status = 0;
/**
gpio初始化,包括gpio分组,引脚,中断模式,中断号,中断主优先级,中断次优先级。
*/
static void GPIO_Int_Init(GPIO_TypeDef*gpiox,uint32_t pinnum,uint32_t intmode,IRQn_Type irq,uint32_t PreemptPriority, uint32_t SubPriority)
{
GPIO_InitTypeDef GPIO_InitStructure;
if(gpiox == GPIOA){
__HAL_RCC_GPIOA_CLK_ENABLE();
}else if(gpiox == GPIOB){
__HAL_RCC_GPIOB_CLK_ENABLE();
}else if(gpiox == GPIOC){
__HAL_RCC_GPIOC_CLK_ENABLE();
}else if(gpiox == GPIOD){
__HAL_RCC_GPIOD_CLK_ENABLE();
}
/*Configure GPIO pin as input floating */
GPIO_InitStructure.Mode = intmode;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Pin = pinnum;
HAL_GPIO_Init(gpiox, &GPIO_InitStructure);
/* Enable and set EXTI Line0 Interrupt to the lowest priority */
HAL_NVIC_SetPriority(irq, PreemptPriority, SubPriority);
HAL_NVIC_EnableIRQ(irq);
}
//配对wps按键初始化
void WPS_PressKey_Init(void)
{
GPIO_Int_Init(WPS_PRESSKEY_TYPE,
WPS_PRESSKEY_PIN,
WPS_PRESSKEY_ITMODE,
WPS_PRESSKEY_IRQ,
WPS_INT_NVIC_PREEMPTIONPRIORITY,
WPS_INT_NVIC_SUBPRIORITY);
}
/**
外层调用轮询检测,looptime为外部循环时基,
返回值
0:无效,
1:短按,
2:长按。
*/
int WPS_PressKey_Check(uint16_t looptime)
{
//配对wps按键触发标志
if(is_wps_press_key_pressed)
{
/*
由于是下降沿触发,那么当一直按下就是保持低电平,抬起就保持高电平,
所以当用户抬起按键,则被认为是一次检测的结束信号,
无效时间段:0~WPS_DEBOUNCE_TIME,WPS_LONG_PRESS_TIME/2~WPS_LONG_PRESS_TIME,
短按时间段:WPS_DEBOUNCE_TIME~WPS_LONG_PRESS_TIME/2,
长按时间段: >= WPS_LONG_PRESS_TIME。
*/
if(WPS_PressKey_Read())
{
is_wps_press_key_pressed = FALSE;
if(wps_press_key_debounce_cnt >= (WPS_LONG_PRESS_TIME / looptime / 2)){
return 0;
}
return wps_status;
}
/**
第一阶段延时消抖,是为了过滤误触发,
第二阶段延时计时,是为了计算短按触发,抬起动作为结束信号,
第三阶段延时计时,是为了计算长按触发,即使没有抬起动作。
*/
if(wps_press_key_debounce_cnt++ >= (wps_timeout / looptime))
{
wps_press_key_debounce_cnt = 0;
if(!WPS_PressKey_Read())
{
if(wps_status++ >= 1)
{
is_wps_press_key_pressed = FALSE;
return wps_status;
}
wps_timeout = WPS_LONG_PRESS_TIME - wps_timeout;
}
}
}
return 0;
}
/**
* @brief EXTI line detection callbacks
* @param GPIO_Pin: Specifies the pins connected EXTI line
* @retval None
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
/**
配对wps按键中断触发回调函数,匹配触发引脚。
*/
if(GPIO_Pin == WPS_PRESSKEY_PIN)
{
wps_timeout = WPS_DEBOUNCE_TIME;
wps_status = 0;
wps_press_key_debounce_cnt = 0;
is_wps_press_key_pressed = TRUE;
}
}