基于定时器扫描的状态机按键,实现单击、双击、长按、短长按、超长按的按键识别

基于定时器扫描的状态机按键,实现单击、双击、长按、长按释放、短长按、短长按释放、超长按、超长按释放的按键识别

说明

本按键例程是基于10ms的定时器扫描,实现了单击、双击、长按、长按释放、短长按、短长按释放、超长按、超长按释放的按键识别。希望对你也有帮助。

使用说明:

  1. 传入一个读取按键管脚电平的函数指针
    该函数为返回bool类型,调用参数void
    指针赋值给read_key
    本例程按键管脚电平低为按下
    若电平高为按下,则将返回值取反
    注意自己需要提前做好GPIO初始化,设定管脚为输入等
    参考如下:
bool read_key1(void)  // 读按键1管脚电平
{
	return HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)? true : false;  // 此处添加自己的读管脚函数
}
bool read_key1(void)  // 读按键1管脚电平
{
	return nrf_gpio_pin_read(KEY1_PIN)? true : false;  // 此处添加自己的读管脚函数
}
  1. 传入一个按键回调事件的函数指针
    该函数为返回void类型,调用参数void
    指针赋值给single_click_callback等回调函数
    参考如下:
void key0_single_click_callback(void)  // 单机事件处理
{
	  printf("key0_single_click_callback!\r\n");  // 此处添加自己的按键中断回调事件
}
  1. 创建一个10ms的定时器,这里就不介绍了
  2. 在定时器初始化之前,进行按键初始化my_key_init()
  3. 在定时器中断里进行按键扫描my_key_scan()

代码

my_key.c文件

#include <string.h>

#include "my_key.h"

static my_key_param_t key_param[KEY_NUMBER] = {0};  // 按键参数结构体数组

void key0_single_click_callback(void)
{
	printf("key0_single_click_callback\r\n");
}
	
void key0_double_click_callback(void)
{
	printf("key0_double_click_callback\r\n");
}

void key0_long_press_callback(void)
{
	printf("key0_long_press_callback\r\n");
}

void key0_long_release_callback(void)
{
	printf("key0_long_release_callback\r\n");
}

void key0_short_long_press_callback(void)
{
	printf("key0_short_long_press_callback\r\n");
}

void key0_short_long_release_callback(void)
{
	printf("key0_short_long_release_callback\r\n");
}

void key0_long_long_press_callback(void)
{
	printf("key0_long_long_press_callback\r\n");
}

void key0_long_long_release_callback(void)
{
	printf("key0_long_long_release_callback\r\n");
}

void key1_single_click_callback(void)
{
	printf("key1_single_click_callback\r\n");
}

void key1_double_click_callback(void)
{
	printf( "key1_double_click_callback\r\n");
}

void key1_long_press_callback(void)
{
	printf("key1_long_press_callback\r\n");
}

void key1_long_release_callback(void)
{
	printf("key1_long_release_callback\r\n");
}

void key1_short_long_press_callback(void)
{
	printf("key1_short_long_press_callback\r\n");
}

void key1_short_long_release_callback(void)
{
	printf("key1_short_long_release_callback\r\n");
}

void key1_long_long_press_callback(void)
{
	printf("key1_long_long_press_callback\r\n");
}

void key1_long_long_release_callback(void)
{
	printf("key1_long_long_release_callback\r\n");
}

void my_key_init(void)  // 按键初始化
{
	key_param[0].read_key = read_key1;
	key_param[0].key_is_press = false;
	key_param[0].count = 0;
	key_param[0].state = KEY_STATE_IDLE;
	key_param[0].event = KEY_EVENT_IDLE;
	key_param[0].single_click_callback = key0_single_click_callback;
	key_param[0].double_click_callback = key0_double_click_callback;
	key_param[0].long_press_callback = key0_long_press_callback;
	key_param[0].long_release_callback = key0_long_release_callback;
	key_param[0].short_long_press_callback = key0_short_long_press_callback;
	key_param[0].short_long_release_callback = key0_short_long_release_callback;
	key_param[0].long_long_press_callback = key0_long_long_press_callback;
	key_param[0].long_long_release_callback = key0_long_long_release_callback;

	key_param[1].read_key = read_key2;
	key_param[1].key_is_press = false;
	key_param[1].count = 0;
	key_param[1].state = KEY_STATE_IDLE;
	key_param[1].event = KEY_EVENT_IDLE;
	key_param[1].single_click_callback = key1_single_click_callback;
	key_param[1].double_click_callback = key1_double_click_callback;
	key_param[1].long_press_callback = key1_long_press_callback;
	key_param[1].long_release_callback = key1_long_release_callback;
	key_param[1].short_long_press_callback = key1_short_long_press_callback;
	key_param[1].short_long_release_callback = key1_short_long_release_callback;
	key_param[1].long_long_press_callback = key1_long_long_press_callback;
	key_param[1].long_long_release_callback = key1_long_long_release_callback;
}
	
void my_key_scan(void)  // 按键扫描
{
	for (uint8_t i = 0; i < KEY_NUMBER; i++)  // 轮询按键
	{
		if (key_param[i].read_key == NULL) continue;

		key_param[i].key_is_press = !key_param[i].read_key();  // 获取按键状态
		
		// 扫描按键状态
		switch (key_param[i].state)  // 进入状态机流程
		{
			case KEY_STATE_IDLE:  // 按键空闲状态
				if (key_param[i].key_is_press)  // 若按键被按下
				{
					key_param[i].count = 0;  // 计数清零
					key_param[i].state = KEY_STATE_FIRST_PRESS;  // 跳转到按键第一次按下状态
				}
				else
				{
					key_param[i].count = 0;  // 计数清零
					key_param[i].state = KEY_STATE_IDLE;  // 跳转到按键空闲状态
					key_param[i].event = KEY_EVENT_IDLE;  // 按键空闲事件
				}
				break;

			case KEY_STATE_FIRST_PRESS:  // 按键第一次按下状态
				if (key_param[i].key_is_press)  // 若按键被按下
				{
					if (++key_param[i].count >= KEY_SHORT_PRESS_COUNT_NUMBER)  // 若第一次按下的时间计数超过KEY_SHORT_PRESS_COUNT_NUMBER
					{
						key_param[i].count = 0;  // 计数清零
						key_param[i].state = KEY_STATE_FIRST_PRESS_VALID;  // 跳转到按键第一次按下有效状态
					}
				}
				else 
				{
					// 按下时间不够,不处理,按键消抖
					key_param[i].count = 0;  // 计数清零
					key_param[i].state = KEY_STATE_IDLE;  // 跳转到按键空闲状态
					key_param[i].event = KEY_EVENT_IDLE;  // 按键空闲事件
				}
				break;
				
			case KEY_STATE_FIRST_PRESS_VALID:	// 按键第一次按下有效状态
				if (key_param[i].key_is_press)  // 若按键被按下
				{
					if (++key_param[i].count >= KEY_LONG_PRESS_COUNT_NUMBER - KEY_SHORT_PRESS_COUNT_NUMBER)  // 若第一次按下的时间计数超过KEY_LONG_PRESS_COUNT_NUMBER
					{
						key_param[i].count = 0;  // 计数清零
						key_param[i].state = KEY_STATE_LONG_PRESS;  // 跳转到按键长按状态
						key_param[i].event = KEY_EVENT_LONG_PRESS;  // 按键长按事件
					}
				}
				else 
				{
					key_param[i].count = 0;  // 计数清零
					key_param[i].state = KEY_STATE_FIRST_RELEASE;  // 跳转到按键第一次释放状态
				}
				break;
				
			case KEY_STATE_FIRST_RELEASE:  // 按键第一次释放状态
				if (!key_param[i].key_is_press)  // 若按键释放
				{
					if (++key_param[i].count >= KEY_DOUBLE_CLICK_INTERVAL_COUNT_NUMBER)  // 若超过KEY_DOUBLE_CLICK_INTERVAL_COUNT_NUMBER计数时间,按键没有第二次被按下
					{
						key_param[i].count = 0;  // 计数清零
						key_param[i].state = KEY_STATE_IDLE;  // 跳转到按键空闲状态
						key_param[i].event = KEY_EVENT_SINGLE_CLICK;  // 按键单击事件
					}
				}
				else if (key_param[i].count >= KEY_SHORT_PRESS_COUNT_NUMBER && key_param[i].count < KEY_DOUBLE_CLICK_INTERVAL_COUNT_NUMBER)  // 若在间隔时间内,获取了第二次按键按下
				{
					key_param[i].count = 0;  // 计数清零
					key_param[i].state = KEY_STATE_SECOND_PRESS;  // 跳转到按键第二次按下状态
				}
				else if (key_param[i].count < KEY_SHORT_PRESS_COUNT_NUMBER) // 若在KEY_SHORT_PRESS_COUNT_NUMBER计数时间内,获取了第二次按键按下,那么此次动作忽略,做消抖处理
				{
					key_param[i].count = 0;  // 计数清零
					key_param[i].state = KEY_STATE_IDLE;  // 跳转到按键空闲状态
					key_param[i].event = KEY_EVENT_SINGLE_CLICK;  // 按键单击事件
				}
				break;
				
			case KEY_STATE_SECOND_PRESS:  // 按键第二次按下状态
				if (key_param[i].key_is_press)  // 若按键被按下
				{
					if (++key_param[i].count >= KEY_SHORT_PRESS_COUNT_NUMBER)  // 若第一次按下的时间计数超过KEY_SHORT_PRESS_COUNT_NUMBER
					{
						key_param[i].count = 0;  // 计数清零
						key_param[i].state = KEY_STATE_SECOND_PRESS_VALID;  // 跳转到按键第二次按下有效状态
					}
				}
				else 
				{
					// 按下时间不够,不处理,按键消抖
					key_param[i].count = 0;  // 计数清零
					key_param[i].state = KEY_STATE_IDLE;  // 跳转到按键空闲状态
					key_param[i].event = KEY_EVENT_SINGLE_CLICK;  // 按键单击事件
				}
				break;
				
			case KEY_STATE_SECOND_PRESS_VALID:  // 按键第二次按下有效状态
				if (key_param[i].key_is_press)  // 若按键被按下
				{
					if (++key_param[i].count >= KEY_LONG_PRESS_COUNT_NUMBER - KEY_SHORT_PRESS_COUNT_NUMBER)  // 若第二次按下的时间计数超过KEY_LONG_PRESS_COUNT_NUMBER
					{
						key_param[i].count = 0;  // 计数清零
						key_param[i].state = KEY_STATE_SHORT_LONG_PRESS;  // 跳转到按键短长按状态
						key_param[i].event = KEY_EVENT_SHORT_LONG_PRESS;  // 按键短长按事件
					}
				}
				else 
				{
					key_param[i].count = 0;  // 计数清零
					key_param[i].state = KEY_STATE_IDLE;  // 跳转到按键空闲状态
					key_param[i].event = KEY_EVENT_DOUBLE_CLICK;  // 按键双击事件
				}
				break;
				
			case KEY_STATE_SECOND_RELEASE:  // 按键第二次释放状态
				// Do nothing!
				break;
			
			case KEY_STATE_LONG_PRESS:
				if (key_param[i].key_is_press)  // 若按键被按下
				{
					if (++key_param[i].count >= KEY_LONG_LONG_PRESS_COUNT_NUMBER - KEY_LONG_PRESS_COUNT_NUMBER)  // 若长按下的时间计数超过KEY_LONG_LONG_PRESS_COUNT_NUMBER
					{
						key_param[i].count = 0;  // 计数清零
						key_param[i].state = KEY_STATE_LONG_LONG_PRESS;  // 跳转到按键超长按状态
						key_param[i].event = KEY_EVENT_LONG_LONG_PRESS;  // 按键超长按事件
					}
				}
				else 
				{
					key_param[i].count = 0;  // 计数清零
					key_param[i].state = KEY_STATE_LONG_RELEASE;  // 跳转到按键长按释放状态
					key_param[i].event = KEY_EVENT_LONG_RELEASE;  // 按键长按释放事件
				}
				break;

			case KEY_STATE_LONG_RELEASE:
				key_param[i].count = 0;  // 计数清零
				key_param[i].state = KEY_STATE_IDLE;  // 跳转到按键空闲状态
				key_param[i].event = KEY_EVENT_IDLE;  // 按键空闲事件
				break;

			case KEY_STATE_SHORT_LONG_PRESS:
				if (key_param[i].key_is_press)  // 若按键被按下
				{
					// 短长按后,不再做处理,等待按键释放
					// Do nothing!
				}
				else 
				{
					key_param[i].count = 0;  // 计数清零
					key_param[i].state = KEY_STATE_SHORT_LONG_RELEASE;  // 跳转到按键短长按释放状态
					key_param[i].event = KEY_EVENT_SHORT_LONG_RELEASE;  // 按键短长按释放事件
				}
				break;
				
			case KEY_STATE_SHORT_LONG_RELEASE:
				key_param[i].count = 0;  // 计数清零
				key_param[i].state = KEY_STATE_IDLE;  // 跳转到按键空闲状态
				key_param[i].event = KEY_EVENT_IDLE;  // 按键空闲事件
				break;

			case KEY_STATE_LONG_LONG_PRESS:
				if (key_param[i].key_is_press)  // 若按键被按下
				{
					// 超长按后,不再做处理,等待按键释放
					// Do nothing!	
				}
				else 
				{
					key_param[i].count = 0;  // 计数清零
					key_param[i].state = KEY_STATE_LONG_LONG_RELEASE;  // 跳转到按键超长按释放状态
					key_param[i].event = KEY_EVENT_LONG_LONG_RELEASE;  // 按键超长按释放事件
				}
				break;

			case KEY_STATE_LONG_LONG_RELEASE:
				key_param[i].count = 0;  // 计数清零
				key_param[i].state = KEY_STATE_IDLE;  // 跳转到按键空闲状态
				key_param[i].event = KEY_EVENT_IDLE;  // 按键空闲事件
				break;
		}
		
		// 扫描按键事件
		switch (key_param[i].event)  // 进入状态机流程
		{
			case KEY_EVENT_IDLE:  
				// Do nothing!
				break;
			
			case KEY_EVENT_SINGLE_CLICK: 
				if (key_param[i].single_click_callback != NULL) key_param[i].single_click_callback();
				break;
			
			case KEY_EVENT_DOUBLE_CLICK:  
				if (key_param[i].double_click_callback != NULL) key_param[i].double_click_callback();
				break;

			case KEY_EVENT_LONG_PRESS: 
				if (key_param[i].long_press_callback != NULL) key_param[i].long_press_callback();
				break;

			case KEY_EVENT_LONG_RELEASE: 
				if (key_param[i].long_release_callback != NULL) key_param[i].long_release_callback();
				break;

			case KEY_EVENT_SHORT_LONG_PRESS:  
				if (key_param[i].short_long_press_callback != NULL) key_param[i].short_long_press_callback();
				break;

			case KEY_EVENT_SHORT_LONG_RELEASE:  
				if (key_param[i].short_long_release_callback != NULL) key_param[i].short_long_release_callback();
				break;

			case KEY_EVENT_LONG_LONG_PRESS: 
				if (key_param[i].long_long_press_callback != NULL) key_param[i].long_long_press_callback();
				break;

			case KEY_EVENT_LONG_LONG_RELEASE: 
				if (key_param[i].long_long_release_callback != NULL) key_param[i].long_long_release_callback();
				break;
		}
		
		key_param[i].event = KEY_EVENT_IDLE;  // 清除按键事件
	}
}

my_key.h文件

#ifndef __MY_KEY_H__
#define __MY_KEY_H__

#include <stdbool.h>
#include <stdint.h>

// 在10ms的定时器中进行计数
#define KEY_SHORT_PRESS_COUNT_NUMBER				(5)	 // 短按时间
#define KEY_DOUBLE_CLICK_INTERVAL_COUNT_NUMBER		(15)  // 双击间隔
#define KEY_LONG_PRESS_COUNT_NUMBER					(200)  // 长按时间
#define KEY_LONG_LONG_PRESS_COUNT_NUMBER			(800)  // 超长按时间

typedef bool (* my_read_key_t)(void);
typedef void (* my_key_callback_t)(void);

#define KEY_NUMBER									(2)  // 按键个数

// 按键状态
typedef enum
{
	KEY_STATE_IDLE,					// 按键空闲状态
	KEY_STATE_FIRST_PRESS,			// 按键第一次按下状态
	KEY_STATE_FIRST_PRESS_VALID,	// 按键第一次按下有效状态
	KEY_STATE_FIRST_RELEASE,		// 按键第一次释放状态
	KEY_STATE_SECOND_PRESS,			// 按键第二次按下状态
	KEY_STATE_SECOND_PRESS_VALID,	// 按键第二次按下有效状态
	KEY_STATE_SECOND_RELEASE,		// 按键第二次释放状态
	KEY_STATE_LONG_PRESS,			// 按键长按状态
	KEY_STATE_LONG_RELEASE,			// 按键长按释放状态
	KEY_STATE_SHORT_LONG_PRESS,		// 按键短长按状态
	KEY_STATE_SHORT_LONG_RELEASE,	// 按键短长按释放状态
	KEY_STATE_LONG_LONG_PRESS,		// 按键超长按状态
	KEY_STATE_LONG_LONG_RELEASE,	// 按键超长按释放状态
} my_key_state_t;

// 按键事件
typedef enum
{
	KEY_EVENT_IDLE,					// 按键空闲事件
	KEY_EVENT_SINGLE_CLICK,			// 按键单击事件
	KEY_EVENT_DOUBLE_CLICK,			// 按键双击事件
	KEY_EVENT_LONG_PRESS,			// 按键长按事件
	KEY_EVENT_LONG_RELEASE,			// 按键长按释放事件
	KEY_EVENT_SHORT_LONG_PRESS,		// 按键短长按事件
	KEY_EVENT_SHORT_LONG_RELEASE,	// 按键短长按释放事件
	KEY_EVENT_LONG_LONG_PRESS,		// 按键超长按事件
	KEY_EVENT_LONG_LONG_RELEASE,	// 按键超长按释放事件
} my_key_event_t;

// 按键参数
typedef struct
{
	my_read_key_t 		read_key;  // 读按键管脚状态
	bool 				key_is_press;  // 按键是否被按下
	uint32_t 			count;  // 按键持续时间定时器计数值
	my_key_state_t 		state;  // 按键状态
	my_key_event_t 		event;  // 按键事件
	my_key_callback_t 	single_click_callback;  // 按键单击回调函数
	my_key_callback_t 	double_click_callback;  // 按键双击回调函数
	my_key_callback_t 	long_press_callback;  // 按键长按回调函数
	my_key_callback_t 	long_release_callback;  // 按键长按释放回调函数
	my_key_callback_t 	short_long_press_callback;  // 按键短长按回调函数
	my_key_callback_t 	short_long_release_callback;  // 按键短长按释放回调函数
	my_key_callback_t 	long_long_press_callback;  // 按键超长按回调函数
	my_key_callback_t 	long_long_release_callback;  // 按键超长按释放回调函数
} my_key_param_t;


void my_key_init(void);  // 按键初始化,必须在定时器初始化之前!!!
void my_key_scan(void);  // 按键扫描


#endif  // __MY_KEY_H__

原创文章,转载请注明,谢谢。

  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,使用定时器也是一种实现按键按短按双击的方法。 具体实现方法可以参考以下步骤: 1. 初始化定时器。选择合适的定时器,并设置定时器的时钟源、计数模式、计数周期等参数。一般推荐使用定时器的计数模式为向上计数,并且设置一个合适的计数周期。 2. 初始化按键。选择合适的引脚作为按键输入,配置引脚的输入模式和上拉/下拉电阻。一般推荐使用上拉电阻,使按键默认为高电平。 3. 在定时器中断服务函数中实现按键状态的检测和处理。在定时器中断服务函数中,读取按键状态,并根据当前状态和前一次状态的变化,判断按键事件的类型。如果按键被按下,则记录按下时间;如果按键被释放,则记录释放时间,并根据时间间隔判断按键事件的类型。 4. 根据按键事件的类型,执行相应的操作。例如,按事件可以用于开启或关闭某个功能;短按事件可以用于切换不同的模式;双击事件可以用于执行快速操作。 注意事项: 1. 在定时器中断服务函数中,需要注意防抖处理。对于按键输入信号,由于存在抖动现象,因此需要使用软件或硬件方式进行防抖处理,以确保检测到的按键状态是稳定的。 2. 在定时器中断服务函数中,需要注意按键状态的检测间隔。检测间隔过短会导致系统负载过高,检测间隔过会影响按键检测的灵敏度,因此需要选择一个合适的检测间隔。 3. 在定时器中断服务函数中,需要注意定时器的溢出问题。如果定时器的计数周期比较短,容易出现定时器溢出的情况,需要进行相应的处理。 总之,使用定时器实现按键按短按双击需要结合具体的硬件平台和软件环境进行综合考虑,根据实际需求选择合适的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值