按键双击,长按和防抖的一种实现方式

最近进行N32WB031 mcu的项目开发,实现按键处理这块进行了测试验证:

1. 程序使用了systick来计数

2. 使用ns_timer 作为软件定时器。

3. 使用按键中断来进行sleep唤醒。

key.c 代码:

#include "stdio.h"
#include "n32wb03x.h"
#include "app_key.h"
#include "ns_delay.h"
#include "ns_timer.h"
#include "hx3918_software_i2c.h"
#include "app_lcd_ui.h"
#include "app_printf.h"
#include "rwip.h"
#include "ns_ble.h"
#include "ns_sleep.h"
#include "ns_log.h"
#include "hx3918.h"

//#include "key.h"
extern void delay_n_ms(uint32_t count);
timer_hnd_t timer1s_id = NS_TIMER_INVALID_HANDLER;
timer_hnd_t timer3s_id = NS_TIMER_INVALID_HANDLER;
timer_hnd_t timer10ms_id = NS_TIMER_INVALID_HANDLER;
timer_hnd_t timer5s_id = NS_TIMER_INVALID_HANDLER;
static uint8_t button_just_released = 0;  
// 按键状态变量  
 static ButtonState_t button_state = BUTTON_RELEASED;
 uint8_t key_flag = false;
// 读取按键状态  
uint8_t Read_Button(void) {  
    return GPIO_ReadInputDataBit(KEY_INPUT_PORT, KEY_INPUT_PIN) == RESET; // 假设按键按下时引脚为低电平  
} 


//按键事件回调函数
void KeyEventHandler(ButtonState_t state) 
{  
    switch (state) {  
        case BUTTON_SHORT_PRESSED:  
            // 单击
					//	printf("One_click,\n");
				lpuart_release_debug("One_click,\n",12);
			app_lcd_shortkey_handle();
            break;  
        case BUTTON_DOUBLE_CLICKED:  
					
					//	printf("double_click,\n");
						lpuart_release_debug("double_click,\n",12);
						app_lcd_doublekey_handle();
            break;  
        case BUTTON_LONG_PRESSED:  
            lpuart_release_debug("long_click,\n",12);
					//	printf("long_click,\n");
						app_longkey_handle();
            break;  
        default:  
            break;  
    } 
			
}  


void key_timeout_handle()
{
	 // 如果timer未取消则认为是短按
		ns_timer_cancel(timer1s_id);  
		timer1s_id = NS_TIMER_INVALID_HANDLER;
		button_state = BUTTON_SHORT_PRESSED;  
	
		KeyEventHandler(button_state);
		 
		 
}


void key5s_timeout_handle()
{
	 
		ns_timer_cancel(timer5s_id);  
		timer5s_id = NS_TIMER_INVALID_HANDLER;
		key_flag = false;
		
		 
}

void longkey_timeout_handle()
{
	ns_timer_cancel(timer3s_id);
	timer3s_id = NS_TIMER_INVALID_HANDLER;
	if(Read_Button())     //长按
	{

			KeyEventHandler(BUTTON_LONG_PRESSED); 
		 button_just_released = 0;  //清除双击flag
	}
	
}


uint8_t KeyCallHandle()
{
		return key_flag;
	
}

// 按键处理函数  
void ProcessKey(void) {  
     
		 static uint32_t pressCount = 0;  
    uint32_t current_time = GetTick(); // 获取当前时间(需要配置HAL_Tick)  

    if (Read_Button()) 
		{ // 按键被按下  
        if (button_state == BUTTON_RELEASED) 
				{ // 如果之前是释放状态  
            button_state = BUTTON_PRESSED; // 更新按键状态为按下  
            last_press_time = current_time; // 记录按键按下的时间  
						ns_timer_cancel(timer1s_id);
						ns_timer_cancel(timer3s_id);
						ns_timer_cancel(timer5s_id);
						timer1s_id = NS_TIMER_INVALID_HANDLER;
						
					timer5s_id = ns_timer_create(5000, (timer_callback_t)key5s_timeout_handle);
					timer3s_id = ns_timer_create(long_press_time, (timer_callback_t)longkey_timeout_handle);
						
        } 				
    } 
		else 
		{ // 按键被释放 
		
        if (button_state == BUTTON_PRESSED) 
				{ // 如果之前是按下状态  
            button_state = BUTTON_RELEASED; // 更新按键状态为释放  
						ns_timer_cancel(timer3s_id);    //清除long_key timer
						timer3s_id = NS_TIMER_INVALID_HANDLER;
						
            uint32_t press_duration = current_time - last_press_time; // 计算按键按下的持续时间  
						printf("pre:%d, cur:%d, last:%d",press_duration,current_time ,last_press_time);
						timer1s_id = ns_timer_create(short_press_time+10, (timer_callback_t)key_timeout_handle);
            
						if (press_duration < short_press_time && button_just_released) 
						{  
                // 如果在双击间隔内再次释放,并且之前已经释放过,则认为是双击  
								ns_timer_cancel(timer1s_id);
								timer1s_id = NS_TIMER_INVALID_HANDLER;
								
								button_state = BUTTON_DOUBLE_CLICKED;  
								KeyEventHandler(button_state);
								button_just_released = 0; // 
            } 
		
						else if (press_duration >= long_press_time) 
						{  
                // 如果按下时间超过长按时间,则认为是长按 
								ns_timer_cancel(timer1s_id);		
								timer1s_id = NS_TIMER_INVALID_HANDLER;
              //  button_state = BUTTON_LONG_PRESSED;  
							button_just_released = 0; // 
            }
						else
						{
							button_just_released = 1; // 标记按键刚刚被释放  
						}
						
						
						
            
  
            // 等待一段时间以确保不是误触或抖动  
            delay_n_ms(10); // 延迟10ms  
  
            if (Read_Button()) 
						{  
                // 如果在延迟后按键仍然被按下,则认为是长按的一部分,不处理双击和短按  
                button_just_released = 0;  
            }  
        } 
				
    }  
  
    // 根据需要重置状态或执行其他逻辑  
    if (button_state == BUTTON_SHORT_PRESSED ||  
        button_state == BUTTON_LONG_PRESSED ||  
        button_state == BUTTON_DOUBLE_CLICKED) {  
        // 处理完短按、长按或双击后,重置状态  
        button_state = BUTTON_RELEASED;  
        button_just_released = 0;  
    } 				
}

void EXTI0_1_IRQ_Handler(void);
/**
 * @brief  Configures key port.
 * @param GPIOx x can be A to G to select the GPIO port.
 * @param Pin This parameter can be GPIO_PIN_0~GPIO_PIN_15.
 */
void KeyInputExtiInit(GPIO_Module* GPIOx, uint16_t Pin)
{
    GPIO_InitType GPIO_InitStructure;
    EXTI_InitType EXTI_InitStructure;
    NVIC_InitType NVIC_InitStructure;

    /* Check the parameters */
    assert_param(IS_GPIO_ALL_PERIPH(GPIOx));

    /* Enable the GPIO Clock */
    if (GPIOx == GPIOA)
    {
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA | RCC_APB2_PERIPH_AFIO, ENABLE);
    }
    else if (GPIOx == GPIOB)
    {
        RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB | RCC_APB2_PERIPH_AFIO, ENABLE);
    }
    else
    {
        return;
    }

    /*Configure the GPIO pin as input floating*/
    if (Pin <= GPIO_PIN_ALL)
    {
        GPIO_InitStruct(&GPIO_InitStructure);
        GPIO_InitStructure.Pin          = Pin;
				GPIO_InitStructure.GPIO_Mode    = GPIO_MODE_INPUT;
        GPIO_InitStructure.GPIO_Pull    = GPIO_PULL_UP;
        GPIO_InitPeripheral(GPIOx, &GPIO_InitStructure);
    }
		
		/*Configure key EXTI Line to key input Pin*/
    GPIO_ConfigEXTILine(KEY_INPUT_PORT_SOURCE, KEY_INPUT_PIN_SOURCE);

	ModuleIrqRemoval(EXTI0_1_IRQn);
	ModuleIrqRegister(EXTI0_1_IRQn,EXTI0_1_IRQ_Handler);


    /*Configure key EXTI line*/
    EXTI_InitStructure.EXTI_Line    = KEY_INPUT_EXTI_LINE;
    EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_InitPeripheral(&EXTI_InitStructure);

    /*Set key input interrupt priority*/
    NVIC_InitStructure.NVIC_IRQChannel                   = KEY_INPUT_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority           = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
		
}

uint32_t	key10ms_timeout_handle()
	{
	   ns_timer_cancel(timer10ms_id);
		 if (Read_Button())
		 {
				key_flag = true;
				lpuart_release_debug("\nIRQ1,",6);
		 }
	
	}

// 中断服务程序(ISR) 

void EXTI0_1_IRQ_Handler(void) // EXTIx 是与您的按键GPIO引脚相关的中断处理函数  
{  

    if (EXTI_GetITStatus(KEY_INPUT_EXTI_LINE) != RESET) // KEY_EXTI_LINE 是您的按键GPIO引脚的中断线  
    {  
				ns_timer_cancel(timer10ms_id);
				timer10ms_id = ns_timer_create(10, (timer_callback_t)key10ms_timeout_handle);
        EXTI_ClrITPendBit(KEY_INPUT_EXTI_LINE); // 清除中断标志  
				lpuart_release_debug("\nIRQ,",5);
        // 检查按键状态  
 
    }  
}  





uint32_t tick;
//void HAL_InitTick(uint32_t TickPriority)
//uint32_t	key1ms_timeout_handle();
void HAL_InitTick()
{

//	timer1ms_id = ns_timer_create(1, (timer_callback_t)key1ms_timeout_handle);
	//启动systick_config定时器,并将其定时中断时间设置为1ms   
	if(SysTick_Config(SystemCoreClock/1000) == 0)
	{
		//设置systick中断优先级
	//	NVIC_SetPriority(SysTick_IRQn, 0);
		lpuart_release_debug("systick is On\n", 14);
	}
	else
	{
		lpuart_release_debug("systick is Fail\n", 16);
	}
	
}

uint32_t GetTick(void)
{
	return tick;
}


	/*    系统中断处理函数     */
	void SysTick_Handler(void)
	{
		/* 定义时钟中断处理函数 */
		tick++;
	}
	

	
	
void version_print()
{
		char ver_buf[26] ;
	snprintf(ver_buf, sizeof(ver_buf), "hx3918_test_ver: %s\r\n",HX3918_TEST_VER);  
		
		lpuart_release_debug(ver_buf,strlen(ver_buf));
}
	
	void key_init()
	{
		HAL_InitTick();
		app_hx3918_io_init();
		KeyInputExtiInit(KEY_INPUT_PORT,KEY_INPUT_PIN);
	//	version_print();
	}
	







.h代码:

#ifndef __APP_KEY_H
#define __APP_KEY_H

#include "n32wb03x.h"
#define KEY_N32_STB


#ifdef KEY_N32_STB
#define KEY_INPUT_PORT        GPIOA
#define KEY_INPUT_PIN         GPIO_PIN_0
#define KEY_INPUT_EXTI_LINE   EXTI_LINE0
#define KEY_INPUT_PORT_SOURCE GPIOA_PORT_SOURCE
#define KEY_INPUT_PIN_SOURCE  GPIO_PIN_SOURCE0
#define KEY_INPUT_IRQn        EXTI0_1_IRQn
#else
#define KEY_INPUT_PORT        GPIOB
#define KEY_INPUT_PIN         GPIO_PIN_1
#define KEY_INPUT_EXTI_LINE   EXTI_LINE1
#define KEY_INPUT_PORT_SOURCE GPIOB_PORT_SOURCE
#define KEY_INPUT_PIN_SOURCE  GPIO_PIN_SOURCE1
#define KEY_INPUT_IRQn        EXTI0_1_IRQn
#endif


// 按键状态变量  
typedef enum {  
    BUTTON_RELEASED = 0,  
    BUTTON_PRESSED,  
		BUTTON_SHORT_PRESSED,
    BUTTON_LONG_PRESSED,  
    BUTTON_DOUBLE_CLICKED  
} ButtonState_t;  


static uint32_t last_press_time = 0;  
static uint32_t short_press_time = 400; // 短按时间阈值,单位ms 
static uint32_t double_click_interval = 1000; // 双击间隔,单位ms  
static uint32_t long_press_time = 3000; // 长按时间,单位ms  


#if 1

void KeyInputExtiInit(GPIO_Module* GPIOx, uint16_t Pin);
void KeyEventHandler(ButtonState_t state) ;
//void HAL_InitTick(void);
uint32_t GetTick(void);
void key_init(void);
void ProcessKey(void);
uint8_t KeyCallHandle(void);
#endif
#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值