五、按键报告处理

一、映射表

在按键、灯光扫描章节里,按键扫描是按照硬件排布进行处理的,但和实际键盘不一定是一一对应的,如果没有灯效的情况下,倒不一定需要这个映射表,只需要直接查询对应的键值进行发送即可,不过做灯效的话,很多灯效需要和实际键盘坐标相关,就需要映射表了。

1、按键功能表

这个表也就是第四章的按键功能,根据实际键盘位置填入按键出厂功能。大概长下面这样:

const KEYFNINFO_S KeyFunc[KEY_NUM] =
{
	{0X01,0X29,0X00},{0X00,0X00,0X00},{0X01,0X3A,0X00},{0X01,0X3B,0X00},{0X01,0X3C,0X00},{0X01,0X3D,0X00},{0X01,0X3E,0X00},{0X01,0X3F,0X00},{0X01,0X40,0X00},{0X01,0X41,0X00},{0X01,0X42,0X00},{0X01,0X43,0X00},{0X01,0X44,0X00},{0X01,0X45,0X00},{0X01,0X46,0X00},{0X01,0X47,0X00},{0X01,0X48,0X00},{0X00,0X00,0X00},{0X00,0X00,0X00},{0X00,0X00,0X00},{0X00,0X00,0X00},
    {0X01,0X35,0X00},{0X01,0X1E,0X00},{0X01,0X1F,0X00},{0X01,0X20,0X00},{0X01,0X21,0X00},{0X01,0X22,0X00},{0X01,0X23,0X00},{0X01,0X24,0X00},{0X01,0X25,0X00},{0X01,0X26,0X00},{0X01,0X27,0X00},{0X01,0X2D,0X00},{0X01,0X2E,0X00},{0X01,0X2A,0X00},{0X01,0X49,0X00},{0X01,0X4A,0X00},{0X01,0X4B,0X00},{0X01,0X53,0X00},{0X01,0X54,0X00},{0X01,0X55,0X00},{0X01,0X56,0X00},
    {0X01,0X2B,0X00},{0X01,0X14,0X00},{0X01,0X1A,0X00},{0X01,0X08,0X00},{0X01,0X15,0X00},{0X01,0X17,0X00},{0X01,0X1C,0X00},{0X01,0X18,0X00},{0X01,0X0C,0X00},{0X01,0X12,0X00},{0X01,0X13,0X00},{0X01,0X2F,0X00},{0X01,0X30,0X00},{0X01,0X31,0X00},{0X01,0X4C,0X00},{0X01,0X4D,0X00},{0X01,0X4E,0X00},{0X01,0X5F,0X00},{0X01,0X60,0X00},{0X01,0X61,0X00},{0X01,0X57,0X00},
    {0X01,0X39,0X00},{0X01,0X04,0X00},{0X01,0X16,0X00},{0X01,0X07,0X00},{0X01,0X09,0X00},{0X01,0X0A,0X00},{0X01,0X0B,0X00},{0X01,0X0D,0X00},{0X01,0X0E,0X00},{0X01,0X0F,0X00},{0X01,0X33,0X00},{0X01,0X34,0X00},{0X00,0X00,0X00},{0X01,0X28,0X00},{0X00,0X00,0X00},{0X00,0X00,0X00},{0X00,0X00,0X00},{0X01,0X5C,0X00},{0X01,0X5D,0X00},{0X01,0X5E,0X00},{0X00,0X00,0X00},
    {0X01,0XE1,0X00},{0X01,0X1D,0X00},{0X01,0X1B,0X00},{0X01,0X06,0X00},{0X01,0X19,0X00},{0X01,0X05,0X00},{0X01,0X11,0X00},{0X01,0X10,0X00},{0X01,0X36,0X00},{0X01,0X37,0X00},{0X01,0X38,0X00},{0X00,0X00,0X00},{0X01,0XE5,0X00},{0X00,0X00,0X00},{0X00,0X00,0X00},{0X01,0X52,0X00},{0X00,0X00,0X00},{0X01,0X59,0X00},{0X01,0X5A,0X00},{0X01,0X5B,0X00},{0X01,0X58,0X00},
    {0X01,0XE0,0X00},{0X01,0XE3,0X00},{0X01,0XE2,0X00},{0X00,0X00,0X00},{0X00,0X00,0X00},{0X01,0X2C,0X00},{0X00,0X00,0X00},{0X00,0X00,0X00},{0X00,0X00,0X00},{0X00,0X00,0X00},{0X01,0XE6,0X00},{0X01,0XFF,0X00},{0X01,0X65,0X00},{0X01,0XE4,0X00},{0X01,0X50,0X00},{0X01,0X51,0X00},{0X01,0X4F,0X00},{0X01,0X62,0X00},{0X00,0X00,0X00},{0X01,0X63,0X00},{0X00,0X00,0X00},
};

2、中转表

这个表是用于将硬件提供的行列信息找到对应按键功能表的位置:

typedef struct
{
	u8 keyCodeMapRow; //键值表行序号
	u8 keyCodeMapCol; //键值表列序号
}hw_sw_t;

#define KEY_COM 6u
#define KEY_SEG 21u

const hw_sw_t keyHwToSw[KEY_COM*KEY_SEG]=
{
	{0,0},{0,1},{0,2},{0,3},{0,4},{0,5},{0,6},{0,7},{0,8},{0,9},{0,10},{0,11},{0,12},{0,13},{0,14},{0,15},{0,16},{0,17},{0,18},{0,19},{0,20},
	{1,0},{1,1},{1,2},{1,3},{1,4},{1,5},{1,6},{1,7},{1,8},{1,9},{1,10},{1,11},{1,12},{1,13},{1,14},{1,15},{1,16},{1,17},{1,18},{1,19},{1,20},
	{2,0},{2,1},{2,2},{2,3},{2,4},{2,5},{2,6},{2,7},{2,8},{2,9},{2,10},{2,11},{2,12},{2,13},{2,14},{2,15},{2,16},{2,17},{2,18},{2,19},{2,20},
	{3,0},{3,1},{3,2},{3,3},{3,4},{3,5},{3,6},{3,7},{3,8},{3,9},{3,10},{3,11},{3,12},{3,13},{3,14},{3,15},{3,16},{3,17},{3,18},{3,19},{3,20},
	{4,0},{4,1},{4,2},{4,3},{4,4},{4,5},{4,6},{4,7},{4,8},{4,9},{4,10},{4,11},{4,12},{4,13},{4,14},{4,15},{4,16},{4,17},{4,18},{4,19},{4,20},
	{5,0},{5,1},{5,2},{5,3},{5,4},{5,5},{5,6},{5,7},{5,8},{5,9},{5,10},{5,11},{5,12},{5,13},{5,14},{5,15},{5,16},{5,17},{5,18},{5,19},{5,20},
};

这样即便后续硬件再怎么变化,只需要改动中转表即可,无需更改上层软件;

二、报告处理

在得到按键功能之后,就可以根据用途、功能码来填写报告了,完整代码:

/// @brief 生成按键报告
/// @param index 索引号
/// @param set 按键状态,2代表长按,1代表按下,0代表释放
/// @param fn 组合键标志
static void NKeyboard_Handle(u8 index,u8 set,u8 fn)
{
	u8 usagepage;
	u8 usage[2];
	
	if(!fn) //根据Fn键确定读取的键值功能表
	{
		usagepage = KeyFunc[index].usagePage; //非Fn组合键功能表
		usage[0] = KeyFunc[index].usage[0]; 
		usage[1] = KeyFunc[index].usage[1]; 
	}
	else
	{
		usagepage = FnKeyFunc[index].usagePage; //Fn组合键功能表
		usage[0] = FnKeyFunc[index].usage[0]; 
		usage[1] = FnKeyFunc[index].usage[1];
	}

	if(set!=2)	//非长按
	{
		switch(usagepage) //首字节为功能用途
		{
			case KeyFn_StdKeyboard: //标准键盘
			{
				Keyboard_EnterBtnReport(usage[0],set);
			}
			break;
			case KeyFn_CustomKeyboard: //自定义键盘功能
			{
				Keyboard_EnterBtnReport(usage[1],set);
				if(set)
				{
					StdKeyboardReport.SpecialKey |= usage[0];
				}
				else
				{
					StdKeyboardReport.SpecialKey &= (u8)(~usage[0]);
				}
				bStdKeySend = Valid;
			}
			break;
			case KeyFn_Consumer: //多媒体功能
				if(set)
				{
					ConsumerReport.Code[0] = usage[0];
					ConsumerReport.Code[1] = usage[1];
				}
				else
				{
					ConsumerReport.Code[0] = 0;
					ConsumerReport.Code[1] = 0;
				}
				bConsumerSend = Valid;
			break;
			case KeyFn_MouseBtn: //鼠标按键
				Mouse_EnterBtnReport(usage[0],set);
			break;
			case KeyFn_SysCtrl: //系统控制功能
				if(set)
					SysCtrlReport.Flag = usage[0];
				else
					SysCtrlReport.Flag = 0;
				bSysCtrlSend = Valid;
			break;
			case KeyFn_PlayMacro: //宏定义
				if(!set)
				{
					if(!Task_Comps[Task_Macro].run)
					{
						MacroParam.fileNum = usage[0]; //文件编号
						MacroParam.loopSet = usage[1]; //循环次数
						MacroParam.loopCnt = 0;
						Macro_FindMacroInfo();
					}
				}
			break;
			case KeyFn_Custom: //自定义
			{
				if(!set) //按键释放触发功能
				{
					u8 callback = 1;
					u8 param = 0;
					switch(usage[0])
					{
						case LockGUI: //左GUI锁定
							if(KeyFunc[LGui_Pos].usagePage!=KeyFn_DisableBtn)
							{
								SetBtnUsage(LGui_Pos, KeyFn_DisableBtn, 0, 0, 0);
								param = 1;
							}
							else
							{
								SetBtnUsage(LGui_Pos, KeyFn_StdKeyboard, 0, 0, 0);
							}
						break;
						case Ble1: //蓝牙设备1回连
						case Ble2:
						case Ble3:
						case G24:
						case USB:
							if(presspos!=0XFF)
							{
								Keyboard_ShiftDevice((u8)(usage[0] - Ble1));
							}
							else
								callback = 0;
						break;
						default:
						break;
					}
					if(callback)
						led_evt_callback(usage[0],param,0); //调用灯效
				}
			}
			break;
			default:break;
		}
	}
	else	//长按
	{
		if(usagepage==KeyFn_Custom)
		{
			switch(usage[0])
			{
				case Ble1: //蓝牙设备1配对
				case Ble2: //蓝牙设备2配对
				case Ble3: //蓝牙设备3配对
				case G24: //2.4G设备配对
				{
					led_evt_callback(usage[0],1,0);
					Keyboard_ShiftDevice((u8)(usage[0] - Ble1));
				}
				break;
				default:break;
			}
		}
	}
}

这里面在填完报告后还会将参数通知到灯效处理里。

三、按下、释放、Fn组合键检测

按键一般在按下、释放后才发送报告给PC,所以需要一个数组用来记录上一次按键状态:

u32 KeyMapBack[KEY_ROW];

接下来的步骤:

1、每过一定时间开始对比KeyMapBack和KeyMap;

2、有变化查找对应的硬件行列信息,通过映射表得到功能;

3、对Fn组合键需要特殊处理,Fn按下和未按下应该查找不同的按键功能表;

完整代码如下:

/// @brief 按键处理
/// @param  
void Keyboard_Event(void)
{
	for(u8 i=0;i<KEY_COM;i++)
	{
		u32 keystate = KeyMap[i];
		u32 temp = keystate^KeyMapBack[i];	//检测当前行按键是否变化
		
		if(temp)
		{
			KeyMapBack[i] = keystate;
			for(u8 j=0;j<KEY_SEG;j++) //遍历当前行按键
			{
				if(GetBit(temp, 0)) //检测最低位
				{
					u8 keypos = CalHwKeyPos(i, j); //计算按键位置
					u8 row = keyHwToSw[keypos].keyCodeMapRow; //取出映射表中的行列
					u8 col = keyHwToSw[keypos].keyCodeMapCol;
					u8 pos = CalKeyPos(row,col);	//计算按键在功能表中的位置
					if(GetBit(keystate, 0))	//按键释放
					{
						btnNum--;
						if(KeyFunc[pos].usagePage == KeyFn_StdKeyboard && KeyFunc[pos].usage[0] == Fn)
						{
							FnParam.state = InValid;
							if(FnParam.pos!=0)	//释放fn,对应组合键未释放
							{
								NKeyboard_Handle(FnParam.pos, 0, 1); //取消组合键功能
								NKeyboard_Handle(FnParam.pos, 1, 0); //恢复原按键功能
							}
						}
						else
						{
							if((FnParam.state) && (FnParam.pos == pos)) //fn未释放,对应组合键释放
							{
								FnParam.pos = 0;
								NKeyboard_Handle(pos, 0, 1); //取消组合键功能
							}
							else
								NKeyboard_Handle(pos, 0, 0);
						}
						
						if(presspos==pos)
						{
							presspos = 0XFF;
							Task_State(Task_BtnLongPress, 0);
						}
						led_evt_callback(BtnRelease,row,col);
					}
					else				//按键按下
					{
						btnNum++;
						presspos = pos;
						presstimer = 0;
						if(KeyFunc[pos].usagePage == KeyFn_StdKeyboard && KeyFunc[pos].usage[0] == Fn)
						{
							FnParam.state = Valid;
							FnParam.pos = 0;
						}
						else
						{
							if(FnParam.state)
							{
								if(FnParam.pos!=0)
									NKeyboard_Handle(FnParam.pos, 0, 1); //先取消上次组合键
								FnParam.pos = pos;
							}
							
							NKeyboard_Handle(pos, 1, FnParam.state);
						}
						
						Task_State(Task_BtnLongPress, 1);
						led_evt_callback(BtnPress,row,col);
					}
				}
				temp >>= 1;
				keystate >>= 1;
			}
		}
	}
}

这里面有个尚待优化的地方,也就是Fn的判断,现在这里面的Fn判断及操作有点繁琐,效率并不高。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值