本帖最后由 hpdell 于 2019-11-26 11:05 编辑
分享一个支持按键单双击的程序,在v7 按键的基础上修改而来,欢迎指正及纠正
目前已知问题,同时按下 组合键松手后貌似会触发 单击 发送 消息,这个有什么方法可以解决啊 ????????????
#define HARD_KEY_NUM 6 /* 实体按键个数 */
#define KEY_COUNT (HARD_KEY_NUM + 1) /* 6个独立建 + 1个组合按键 */
/*
定义键值代码, 必须按如下次序定时每个键的按下、弹起和长按事件
推荐使用enum, 不用#define,原因:
(1) 便于新增键值,方便调整顺序,使代码看起来舒服点
(2) 编译器可帮我们避免键值重复。
*/
#define KEY_STATE_NUM (4) //按键状态数目
typedef enum
{
KEY_NONE = 0, /* 0 表示按键事件 无任何按键按下 */
KEY_1_DOWN, /* 1键按下 */
KEY_1_UP, /* 1键弹起 */
KEY_1_LONG, /* 1键长按 */
KEY_1_CLOCK_2 , /* 按键双击*/
// 3击
// 4击 等..... ,程序只需做简单的相关更改就可以了,同时修改 KEY_STATE_NUM 这个对应的数值即可
KEY_2_DOWN, /* 2键按下 */
KEY_2_UP, /* 2键弹起 */
KEY_2_LONG, /* 2键长按 */
KEY_2_CLOCK_2 , /* 按键双击*/
KEY_3_DOWN, /* 3键按下 */
KEY_3_UP, /* 3键弹起 */
KEY_3_LONG, /* 3键长按 */
KEY_3_CLOCK_2 , /* 按键双击*/
KEY_4_DOWN, /* 4键按下 */
KEY_4_UP, /* 4键弹起 */
KEY_4_LONG, /* 4键长按 */
KEY_4_CLOCK_2 , /* 按键双击*/
KEY_5_DOWN, /* 5键按下 */
KEY_5_UP, /* 5键弹起 */
KEY_5_LONG, /* 5键长按 */
KEY_5_CLOCK_2 , /* 按键双击*/
KEY_6_DOWN, /* 6键按下 */
KEY_6_UP, /* 6键弹起 */
KEY_6_LONG, /* 6键长按 */
KEY_6_CLOCK_2 , /* 按键双击*/
/* 组合键 */
KEY_7_DOWN, /* 7键按下 */
KEY_7_UP, /* 7键弹起 */
KEY_7_LONG, /* 7键长按 */
KEY_7_CLOCK_2 , /* 按键双击*/
}KEY_ENUM;
/* 按键FIFO用到变量 */
#define KEY_FIFO_SIZE KEY_COUNT
typedef struct
{
unsigned char Buf[KEY_FIFO_SIZE]; /* 键值缓冲区 */
unsigned char Read; /* 缓冲区读指针1 */
unsigned char Write; /* 缓冲区写指针 */
unsigned char Read2; /* 缓冲区读指针2 */
// 以下为双击使用变量定义
unsigned char DoubleClickOutTime; // 双击超时
unsigned char DoubleClickN;
unsigned char DoubleClickK; //用于判断按键是双击、三击、四击等判断变量
unsigned char DoubleClickValBak;
unsigned char DoubleClickIdBak;
}KEY_FIFO_T;
void bsp_DetectKey(uchar i)
{
KEY_T *pBtn;
/*
如果没有初始化按键函数,则报错
if (s_tBtn.IsKeyDownFunc == 0)
{
printf("Fault : DetectButton(), s_tBtn.IsKeyDownFunc undefine");
}
*/
pBtn = &s_tBtn;
if (pBtn->IsKeyDownFunc())
{
if (pBtn->Count < KEY_FILTER_TIME)
{
pBtn->Count = KEY_FILTER_TIME;
}
else if(pBtn->Count < (2 * KEY_FILTER_TIME))
// else if(pBtn->Count < ( KEY_FILTER_TIME << 1)) //提高程序运行速度
{
pBtn->Count++;
}
else
{
if (pBtn->State == 0)
{
pBtn->State = 1;
/* 发送按钮按下的消息 */
bsp_PutKey((unsigned char)((KEY_STATE_NUM * i) + 1)); // +1 必须与 KEY_ENUM 这个枚举里面的定义的一致
}
if (pBtn->LongTime > 0)
{
if (pBtn->LongCount < pBtn->LongTime)
{
/* 发送按钮持续按下的消息 */
if (++pBtn->LongCount == pBtn->LongTime)
{
/* 键值放入按键FIFO */
bsp_PutKey((unsigned char)((KEY_STATE_NUM * i) + 3)); // +3 必须与 KEY_ENUM 这个枚举里面的定义的一致
}
}
else
{
if (pBtn->RepeatSpeed > 0)
{
if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
{
pBtn->RepeatCount = 0;
/* 常按键后,每隔10ms发送1个按键 */
bsp_PutKey((unsigned char)((KEY_STATE_NUM * i) + 1));
}
}
}
}
}
}
else
{
if(pBtn->Count > KEY_FILTER_TIME)
{
pBtn->Count = KEY_FILTER_TIME;
}
else if(pBtn->Count != 0)
{
pBtn->Count--;
}
else
{
if (pBtn->State == 1) // 按钮弹起
{
pBtn->State = 0;
// 500ms 以内必须按下相同的按键才算是 某个按钮双击
s_tKey.DoubleClickOutTime = 50; // 每10ms调用一次, 50*10ms = 500ms
s_tKey.DoubleClickK ++;
s_tKey.DoubleClickIdBak = i;
s_tKey.DoubleClickN = (unsigned char)((KEY_STATE_NUM * i) + 2); // 计算按键抬起后的按键值,+2 必须与 KEY_ENUM 这个枚举里面的定义的一致
// 以下为增加的 双击,目前双击调试基本成功 ,只是不太完美 2019.11.25
if(s_tKey.DoubleClickK == 1) // 按键按下抬起第一次
{
s_tKey.DoubleClickValBak = s_tKey.DoubleClickN; // 备份备份按键抬起键值
}
}
}
pBtn->LongCount = 0;
pBtn->RepeatCount = 0;
}
// 按键抬起后,判断是双击还是单击事件, 基本实现,只是不太完美,后续再研究研究 ? 2019.11.25
if((s_tKey.DoubleClickK >0) && (s_tKey.DoubleClickOutTime == 0)) {
switch(s_tKey.DoubleClickK)
{
case 1:
// 发送按钮弹起的消息
bsp_PutKey((unsigned char)(s_tKey.DoubleClickValBak));
s_tKey.DoubleClickK =0;
s_tKey.DoubleClickN =0;
break;
case 2:
s_tKey.DoubleClickN = (unsigned char)((KEY_STATE_NUM * s_tKey.DoubleClickIdBak) + 2); // 计算按键抬起后的按键值,+2 必须与 KEY_ENUM 这个枚举里面的定义的一致
if(s_tKey.DoubleClickValBak == s_tKey.DoubleClickN) { //说明是 某个按键的 双击 事件
// 发送双击按钮弹起的消息
bsp_PutKey((unsigned char)((KEY_STATE_NUM * s_tKey.DoubleClickIdBak) + 4)); // +4 必须与 KEY_ENUM 这个枚举里面的定义的一致
}
s_tKey.DoubleClickValBak =0;
s_tKey.DoubleClickN =0;
s_tKey.DoubleClickK =0;
break;
// case 3: // 三击,目前没有使用
// break;
default:
s_tKey.DoubleClickValBak =0;
s_tKey.DoubleClickN =0;
s_tKey.DoubleClickK =0;
break;
}
}
}
/*
*********************************************************************************************************
* 函 数 名: bsp_KeyScan
* 功能说明: 扫描所有按键。非阻塞,被systick中断 或者 定时器中断 周期性的调用,二者选择其一即可, 每隔10ms调用一次
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_KeyScan(void)
{
u8 i;
for (i = 0; i < KEY_COUNT; i++)
{
bsp_DetectKey(i);
}
if(s_tKey.DoubleClickOutTime > 0) s_tKey.DoubleClickOutTime--;
}
//---------------------------按键扫描---------------------------//
// 主程序调用
void KEY_San(void)
{
uchar ucKeyCode = KEY_NONE; /* 按键代码 */
/* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
if (ucKeyCode != KEY_NONE)
{
switch (ucKeyCode)
{
case KEY_SELECT_UP: //--------------------- 按键松手后,程序执行---------------------
break;
case KEY_SELECT_LONG:
{
}
break;
case KEY_LEFT_UP: //--------------------- 按键松手后,程序执行---------------------
{
UART0_printf(&quot;发送按钮 单 击的消息
&quot;);
}
break;
case KEY_LEFT_CLICK_2: // 双击测试
{
if(g_tMenu.menu == MAIN_DISPLAY_MENU)
{
UART0_printf(&quot;发送按钮 双 击的消息
&quot;);
}
}
break;
case KEY_SYS_DOWN: // return 与 select 组合按键按下
{
if(g_tMenu.menu == MAIN_DISPLAY_MENU)
{
BEEP_KeyTone();
UART0_printf(&quot;return 与 select 组合按键按下
&quot;);
}
}
break;
default : break;
}
}
} |