按键扫描(单击、双击等多次连击判定)

按键扫描是嵌入式开发中常用的功能,有按键 的地方就有它。一套优雅的按键扫描代码每个项目都可以用。本着这个思想我写了一套按键处理的函数,可以当作模块使用。

按键处理的需求,主要有识别单击、双击、多击、长按等需求。当有按键触发我们来处理一些逻辑,本套代码使用函数指针的方法实现按键回调,每个按键可编写一个回调函数来执行相关操作。

下面来看代码:

key.h内容

//按键数量与名称
typedef enum
{
	KEY1,
	KEY2,
	KEY3,
	KEY4,
	KEY5,
	KEYNUM //放在最后统计按键数量
} V_KEY_NAME;
//按键回调函数类型
typedef void(*v_key_fun)(int param);
void key_config(void);//初始化
void key_run(void); //按键扫描

int set_key_callback(V_KEY_NAME keyname, v_key_fun param);//回调函数绑定

这里说一下枚举类型操作,前几个成员是我们给我们的按键的名字,最后是利用枚举类型如果不指定值其成员值从0开始增加最后一个值正好记录共有多少个按键。

key.c

#include "key.h"
/*KEY_DOWN 在单片机中换成相应io口读取函数*/
#define KEY_DOWN(vKey) ((GetAsyncKeyState(vKey) & 0x8000) ? 1:0) //获取键盘对应按键状态

#define DEB_TIME 3 //按键消抖参数
//操作结束判定时间 例如按键扫描10ms执行一次 则30 即是300ms,在300ms内的点击会被算为连击
#define OP_ED_TIME 30 

#define LTICK_TIME 150 //长按判定时间
typedef enum
{
	IO_SET,        //按键触发
	IO_RESET    //按键未触发
}IO_STATE;


/*按键结构体*/
typedef struct 
{
	v_key_fun key_callback; //按键回调函数
    //按键硬件接口(在例如tm32单片机中可以再定义一个port GPIO端口号)
	unsigned int key_pin; 
	IO_STATE io_state;   //按键状态
	unsigned int press_counter; //按键按下滤波
	unsigned int lift_counter; //按键抬起滤波
	unsigned int press_time;  //按下计时
	unsigned int lift_time; //抬起计时
	unsigned int key_level;//多击判断
}keyStruct;


/*按键结构体数组*/
static keyStruct key_array[KEYNUM];


/*按键初始化配置*/
void key_config(void)
{
	key_array[KEY1].key_pin = 'A'; //这里由于我使用的电脑按键,移植到单片机中绑定对应的引脚
	key_array[KEY2].key_pin = 'S';
	key_array[KEY3].key_pin = 'D';
	key_array[KEY4].key_pin = 'W';
	key_array[KEY5].key_pin = 'F';
	for (int i = 0; i < KEYNUM; i++)
	{
		key_array[i].key_callback = NULL;
		key_array[i].lift_counter = 0;
		key_array[i].press_counter = 0;
		key_array[i].io_state = IO_RESET;
		key_array[i].press_time = 0;
		key_array[i].lift_time = 0;
		key_array[i].key_level = 0;
	}
}

/*key_run 是按键执行的主函数  定时调用推荐5-15ms调用一次*/
void key_run(void)
{
	static uint32_t key_run_times = 0;
	key_run_times++;
	for (int i = 0; i < KEYNUM; i++)
	{
		/*滤波*/
		if (KEY_DOWN(key_array[i].key_pin))
		{
			key_array[i].press_counter++;
			key_array[i].lift_counter = 0;
			if (key_array[i].press_counter == DEB_TIME)
			{
				if (key_array[i].io_state == IO_RESET)
				{
					key_array[i].io_state = IO_SET;
					key_array[i].press_time = key_run_times;

				}
			}
		}
		else
		{
			key_array[i].press_counter = 0;
			key_array[i].lift_counter++;
			if (key_array[i].lift_counter == DEB_TIME)
			{
				if (key_array[i].io_state == IO_SET)
				{
					key_array[i].io_state = IO_RESET;
					key_array[i].lift_time = key_run_times;

				}
			}
		}




		/*N次击判定*/
		if (key_array[i].press_time == key_run_times)
		{
				key_array[i].key_level++;
		}
		/*操作结束执行*/
		else if ((unsigned int)(key_run_times -  key_array[i].lift_time)   == OP_ED_TIME && key_array[i].io_state != IO_SET && key_array[i].key_level != 0)
		{
			if (key_array[i].key_callback != NULL )
			{
				key_array[i].key_callback(key_array[i].key_level);//执行按键回调函数
			}
			key_array[i].key_level = 0;
			
		}
		/*长按操作*/
		if (key_array[i].io_state == IO_SET && (unsigned int)(key_run_times - key_array[i].press_time) == LTICK_TIME)
		{
			if (key_array[i].key_callback != NULL)
			{
				key_array[i].key_callback(-1);
			}
			key_array[i].key_level = 0;
		}
	}
}

/*
* 绑定回调函数  成功返回 1 错误返回 0
*/
int set_key_callback(V_KEY_NAME keyname,v_key_fun param)
{
	if (param != NULL)
	{
		key_array[keyname].key_callback = param;
		return 1;
	}
	return 0;
}
/*
*  取消对应的回调函数
*/

void remove_key_callback(V_KEY_NAME keyname)
{
    key_array[keyname].key_callback = NULL;
}


C文件主要写了按键多击判定逻辑,由于进行了多连击的判定,当在一定的时间的多级按键操作被判定为连击,所有当我们操作结束后并不会立即结束,程序会等带一定时间判定无新的操作,在这里key_level数值代码按了几次。

按键模块的使用

main.c

#include <stdio.h>
#include <stdint.h>
#include <Windows.h>
#include "sys_tick.h"
#include "key.h"
/*供按键绑定的回调函数*/
void key1callback(int param);

int main()
{
	sys_tick_init();
	key_config(); //按键配置函数
	set_key_callback(KEY1, key1callback);//设置按键对用的回调函数
	while (1)
	{
		/*每10ms扫描一次*/
		key_run();
		delay_ms(10);
		
	}
	return 0;
}

void key1callback(int param)
{
	switch (param)
	{
	case 1:
		printf("单击操作\n"); break;
	case 2:
		printf("双击操作\n"); break;
	case 3:
		printf("三连击操作\n"); break;
	case 4:
		printf("四连击操作\n"); break;
	case -1:
		printf("长按操作\n"); break;
	default:
		break;
	}
}

通过回调函数来执行按键操作可以方便的启用或关闭相关按键。后续可以配合状态机使用,达到不同状态不同执行不同的按键功能

至此结束,内容原创禁止转载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值