硬汉嵌入式论坛

 找回密码
 立即注册
查看: 3047|回复: 9
收起左侧

[STM32H7] 分享一个支持按键单双击的程序,在v7 按键的基础上修改而来,欢迎指正及纠正

[复制链接]

610

主题

3062

回帖

4912

积分

至尊会员

积分
4912
发表于 2019-11-26 10:59:03 | 显示全部楼层 |阅读模式
本帖最后由 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("发送按钮 单 击的消息\r\n");
        }
        break;



    case  KEY_LEFT_CLICK_2:   // 双击测试
      {
        if(g_tMenu.menu == MAIN_DISPLAY_MENU)
        {
          UART0_printf("发送按钮 双 击的消息\r\n");
        }
      }
      break;  

    case  KEY_SYS_DOWN:   // return 与 select 组合按键按下
      {
        if(g_tMenu.menu == MAIN_DISPLAY_MENU)
        {
          BEEP_KeyTone();
          UART0_printf("return 与 select 组合按键按下\r\n");
        }
      }
      break;  

      default : break;
    }
  }
}










回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107077
QQ
发表于 2019-11-26 11:03:02 | 显示全部楼层
双击不用修改源码,有个简单的办法。

我们的那个按键程序是标准的PC键盘驱动,各种按键效果不用修改底层,仅需用我们bsp_timer.C里面软件定时器,开启一个,如果在一定时间间隔内再次按下,就认为是双击,否则是下这一次单击。
回复

使用道具 举报

610

主题

3062

回帖

4912

积分

至尊会员

积分
4912
 楼主| 发表于 2019-11-26 11:07:51 | 显示全部楼层
本帖最后由 hpdell 于 2019-11-26 11:25 编辑
eric2013 发表于 2019-11-26 11:03
双击不用修改源码,有个简单的办法。

我们的那个按键程序是标准的PC键盘驱动,各种按键效果不用修改底层 ...

好的,我再按照你说的方法捣鼓看看,我目前定义的时间间隔是 500ms , 多谢多谢啊
回复

使用道具 举报

610

主题

3062

回帖

4912

积分

至尊会员

积分
4912
 楼主| 发表于 2019-11-26 12:06:51 | 显示全部楼层
eric2013 发表于 2019-11-26 11:03
双击不用修改源码,有个简单的办法。

我们的那个按键程序是标准的PC键盘驱动,各种按键效果不用修改底层 ...

你好,刚刚想了一下,貌似还是没有完全想明白啊 ?

我的大概理解你的意思是重新开启了一个定时器,那么当按键第一次按下并松手后是否得备份一次 按下的减值啊 ?

此时给超时变量赋值,在定时器里面做 -- 动作或者是 ++ 运算,只到达到设定的值为准,如果在 做 -- 或者 ++ 时按下第2次按键,此时需要再次判断是否是之前的同一个按键按下,如果是就发送双击消息,不是就发送单击消息 ,??
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107077
QQ
发表于 2019-11-26 14:29:26 | 显示全部楼层
hpdell 发表于 2019-11-26 12:06
你好,刚刚想了一下,貌似还是没有完全想明白啊 ?

我的大概理解你的意思是重新开启了一个定时器,那 ...

你说的这个可以的。

我有个更简洁的办法,一般我们都有一个GetRunTime之类的时间基准函数,记录本次的时间和上次的时间,每次求一个差值即可。这样是不是方便不少。
回复

使用道具 举报

610

主题

3062

回帖

4912

积分

至尊会员

积分
4912
 楼主| 发表于 2019-11-26 15:11:56 | 显示全部楼层
eric2013 发表于 2019-11-26 14:29
你说的这个可以的。

我有个更简洁的办法,一般我们都有一个GetRunTime之类的时间基准函数,记录本次的 ...

你说的这个方法应该是更加的高效,我改一下我的程序
回复

使用道具 举报

4

主题

84

回帖

96

积分

初级会员

积分
96
发表于 2019-12-25 16:28:58 | 显示全部楼层
实现双击可以参考这个代码:
https://github.com/murphyzhao/FlexibleButton
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
107077
QQ
发表于 2019-12-25 16:58:31 | 显示全部楼层
回复

使用道具 举报

610

主题

3062

回帖

4912

积分

至尊会员

积分
4912
 楼主| 发表于 2019-12-26 17:04:14 | 显示全部楼层
eric2013 发表于 2019-12-25 16:58
已经有新版了,H7-TOOL里面做了
https://github.com/armfly/H7-TOOL_STM32H7_App/blob/master/User/bsp/ ...

多谢多谢啊,这个方法比我的好很多倍啊,
回复

使用道具 举报

610

主题

3062

回帖

4912

积分

至尊会员

积分
4912
 楼主| 发表于 2019-12-26 17:04:30 | 显示全部楼层
huohua1991 发表于 2019-12-25 16:28
实现双击可以参考这个代码:
https://github.com/murphyzhao/FlexibleButton

多谢多谢啊
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-17 04:04 , Processed in 0.177303 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表