打印
[其他ST产品]

用状态机实现STM32各种按键方式

[复制链接]
73|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
两只袜子|  楼主 | 2025-1-9 09:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
常见的按键判定程序,如正点原子按键例程,只能判定单击事件,对于双击、长按等的判定逻辑较复杂,且使用main函数循环扫描的方式,容易被阻塞,或按键扫描函数会阻塞其他程序的执行。
使用定时器设计状态机可以规避这一问题。
功能介绍
本程序功能:
使用定时器状态机实现按键单击、双击、长按、连按功能。
消抖时间可调,长按时间可调,双击判定时间可调,连按单击间隔可调,可选择使能长按、连按、双击功能,无延时不阻塞,稳定触发。
移植只需修改读IO函数,结构体初始化和宏定义时间参数即可。
注:
在定时器状态机判定产生事件标志,在主函数处理并清除事件标志。
单击是最基本事件,除以下情况外,经过消抖后,在按键释放时触发单击事件。
使能长按后,若按键按下时间大于长按判定时间,则释放时触发长按事件,若不使能,释放时触发单击事件。
使能连按后,按住按键时持续触发连按事件,可自定义等效为单击事件。
无论是否使能长按,按键长按不释放,先经过长按判定时间触发第一次连按事件,然后循环进行连按计时,每次计时结束后都会触发一次连按事件,直到按键释放,触发长按事件(使能长按),或单击事件(不使能长按)。
使能双击后,若两次单击行为之间,由释放到按下的时间小于双击判定时间,则第一次单击行为释放时不触发单击事件,第二次单击行为在释放时触发双击事件。
一次单击行为在双击判定时间内无按键按下动作,之后才触发单击事件。
无论是否使能长按,若上述第二次行为是长按,则第二次释放时不会触发双击事件,而是到达长按判定时间后先触发属于第一次的单击事件,然后在第二次释放按键时触发长按事件(使能长按),或单击事件(不使能长按)。

使用特权

评论回复
沙发
两只袜子|  楼主 | 2025-1-9 09:59 | 只看该作者
代码
头文件 my_key.h
#ifndef ___MY_KEY_H__#define ___MY_KEY_H__#include "main.h"#define ARR_LEN(arr) ((sizeof(arr)) / (sizeof(arr[0]))) //数组大小宏函数
#define KEY_DEBOUNCE_TIME 10      //消抖时间#define KEY_LONG_PRESS_TIME 500   //长按判定时间#define KEY_QUICK_CLICK_TIME 100  //连按时间间隔#define KEY_DOUBLE_CLICK_TIME 200 //双击判定时间#define KEY_PRESSED_LEVEL 0 //按键被按下时的电平
//按键动作typedef enum{  KEY_Action_Press,   //按住  KEY_Action_Release, //松开} KEY_Action_TypeDef;
//按键状态typedef enum{  KEY_Status_Idle,             //空闲  KEY_Status_Debounce,         //消抖  KEY_Status_ConfirmPress,     //确认按下  KEY_Status_ConfirmPressLong, //确认长按  KEY_Status_WaitSecondPress,  //等待再次按下  KEY_Status_SecondDebounce,   //再次消抖  KEY_Status_SecondPress,      //再次按下} KEY_Status_TypeDef;
//按键事件typedef enum{  KEY_Event_Null,        //空事件  KEY_Event_SingleClick, //单击  KEY_Event_LongPress,   //长按  KEY_Event_QuickClick,  //连击  KEY_Event_DoubleClick, //双击} KEY_Event_TypeDef;
//按键模式使能选择typedef enum{  KEY_Mode_OnlySinge = 0x00,         //只有单击  KEY_Mode_Long = 0x01,              //单击长按  KEY_Mode_Quick = 0x02,             //单击连按  KEY_Mode_Long_Quick = 0x03,        //单击长按连按  KEY_Mode_Double = 0x04,            //单击双击  KEY_Mode_Long_Double = 0x05,       //单击长按双击  KEY_Mode_Quick_Double = 0x06,      //单击连按双击  KEY_Mode_Long_Quick_Double = 0x07, //单击长按连按双击} KEY_Mode_TypeDef;
//按键配置typedef struct{  uint8_t KEY_Label;             //按键标号  KEY_Mode_TypeDef KEY_Mode;              //按键模式  uint16_t KEY_Count;            //按键按下计时  KEY_Action_TypeDef KEY_Action; //按键动作,按下或释放  KEY_Status_TypeDef KEY_Status; //按键状态  KEY_Event_TypeDef KEY_Event;   //按键事件} KEY_Configure_TypeDef;
extern KEY_Configure_TypeDef KeyConfig[];extern KEY_Event_TypeDef key_event[];
void KEY_ReadStateMachine(KEY_Configure_TypeDef *KeyCfg);
#endif

‍源文件 my_key.c
#include "my_key.h"
static uint8_t KEY_ReadPin(uint8_t key_label){  switch (key_label)  {  case 0:    return (uint8_t)HAL_GPIO_ReadPin(K0_GPIO_Port, K0_Pin);  case 1:    return (uint8_t)HAL_GPIO_ReadPin(K1_GPIO_Port, K1_Pin);  case 2:    return (uint8_t)HAL_GPIO_ReadPin(K2_GPIO_Port, K2_Pin);  case 3:    return (uint8_t)HAL_GPIO_ReadPin(K3_GPIO_Port, K3_Pin);  case 4:    return (uint8_t)HAL_GPIO_ReadPin(K4_GPIO_Port, K4_Pin);  // case X:  //   return (uint8_t)HAL_GPIO_ReadPin(KX_GPIO_Port, KX_Pin);  }  return 0;}
KEY_Configure_TypeDef KeyConfig[] = {    {0, KEY_Mode_Long_Quick_Double, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},    {1, KEY_Mode_Long_Quick_Double, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},    {2, KEY_Mode_Long_Quick_Double, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},    {3, KEY_Mode_Long_Quick_Double, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},    {4, KEY_Mode_Long_Quick_Double, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},    // {X, KEY_Mode_Long_Quick_Double, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},};
KEY_Event_TypeDef key_event[ARR_LEN(KeyConfig)] = {KEY_Event_Null}; //按键事件//按键状态处理void KEY_ReadStateMachine(KEY_Configure_TypeDef *KeyCfg){  static uint16_t tmpcnt[ARR_LEN(KeyConfig)] = {0};  //按键动作读取  if (KEY_ReadPin(KeyCfg->KEY_Label) == KEY_PRESSED_LEVEL)    KeyCfg->KEY_Action = KEY_Action_Press;  else    KeyCfg->KEY_Action = KEY_Action_Release;
  //状态机  switch (KeyCfg->KEY_Status)  {  //状态:空闲  case KEY_Status_Idle:    if (KeyCfg->KEY_Action == KEY_Action_Press) //动作:按下    {      KeyCfg->KEY_Status = KEY_Status_Debounce; //状态->消抖      KeyCfg->KEY_Event = KEY_Event_Null;       //事件->无    }    else //动作:默认动作,释放    {      KeyCfg->KEY_Status = KEY_Status_Idle; //状态->维持      KeyCfg->KEY_Event = KEY_Event_Null;   //事件->无    }    break;
  //状态:消抖  case KEY_Status_Debounce:    if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_DEBOUNCE_TIME)) //动作:保持按下,消抖时间已到    {      KeyCfg->KEY_Count = 0;                        //计数清零      KeyCfg->KEY_Status = KEY_Status_ConfirmPress; //状态->确认按下      KeyCfg->KEY_Event = KEY_Event_Null;           //事件->无    }    else if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_DEBOUNCE_TIME)) //动作:保持按下,消抖时间未到    {      KeyCfg->KEY_Count++;                      //消抖计数      KeyCfg->KEY_Status = KEY_Status_Debounce; //状态->维持      KeyCfg->KEY_Event = KEY_Event_Null;       //事件->无    }    else //动作:释放,消抖时间未到,判定为抖动    {      KeyCfg->KEY_Count = 0;                //计数清零      KeyCfg->KEY_Status = KEY_Status_Idle; //状态->空闲      KeyCfg->KEY_Event = KEY_Event_Null;   //事件->无    }    break;
  //状态:确认按下  case KEY_Status_ConfirmPress:    if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_LONG_PRESS_TIME)) //动作:保持按下,长按时间已到    {      KeyCfg->KEY_Count = KEY_QUICK_CLICK_TIME;         //计数置数,生成第一次连按事件      KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->确认长按      KeyCfg->KEY_Event = KEY_Event_Null;               //事件->无    }    else if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_LONG_PRESS_TIME)) //动作:保持按下,长按时间未到    {      KeyCfg->KEY_Count++;                          //长按计数      KeyCfg->KEY_Status = KEY_Status_ConfirmPress; //状态->维持      KeyCfg->KEY_Event = KEY_Event_Null;           //事件->无    }    else //动作:长按时间未到,释放    {      if ((uint8_t)(KeyCfg->KEY_Mode) & 0x04) //双击模式      {        KeyCfg->KEY_Count = 0;                           //计数清零        KeyCfg->KEY_Status = KEY_Status_WaitSecondPress; //状态->等待再按        KeyCfg->KEY_Event = KEY_Event_Null;              //事件->无      }      else //非双击模式      {        KeyCfg->KEY_Count = 0;                     //计数清零        KeyCfg->KEY_Status = KEY_Status_Idle;      //状态->空闲        KeyCfg->KEY_Event = KEY_Event_SingleClick; //事件->单击****      }    }    break;
  //状态:确认长按  case KEY_Status_ConfirmPressLong:    if (KeyCfg->KEY_Action == KEY_Action_Press) //动作:保持按下    {      if ((uint8_t)KeyCfg->KEY_Mode & 0x02) //连按模式      {        if (KeyCfg->KEY_Count >= KEY_QUICK_CLICK_TIME) //连按间隔时间已到        {          KeyCfg->KEY_Count = 0;                            //计数清零          KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->维持          KeyCfg->KEY_Event = KEY_Event_QuickClick;         //事件->连按****        }        else //连按间隔时间未到        {          KeyCfg->KEY_Count++;                              //连按计数          KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->维持          KeyCfg->KEY_Event = KEY_Event_Null;               //事件->无        }      }      else //非连按模式      {        KeyCfg->KEY_Count = 0;                            //计数清零        KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->维持        KeyCfg->KEY_Event = KEY_Event_Null;               //事件->无      }    }    else //动作:长按下后释放    {      if ((uint8_t)KeyCfg->KEY_Mode & 0x01) //长按模式      {        KeyCfg->KEY_Count = 0;                   //计数清零        KeyCfg->KEY_Status = KEY_Status_Idle;    //状态->空闲        KeyCfg->KEY_Event = KEY_Event_LongPress; //事件->长按****      }      else //非长按模式      {        KeyCfg->KEY_Count = 0;                     //计数清零        KeyCfg->KEY_Status = KEY_Status_Idle;      //状态->空闲        KeyCfg->KEY_Event = KEY_Event_SingleClick; //事件->单击****      }    }    break;
  //状态:等待是否再次按下  case KEY_Status_WaitSecondPress:    if ((KeyCfg->KEY_Action != KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_DOUBLE_CLICK_TIME)) //动作:保持释放,双击等待时间已到    {      KeyCfg->KEY_Count = 0;                     //计数清零      KeyCfg->KEY_Status = KEY_Status_Idle;      //状态->空闲      KeyCfg->KEY_Event = KEY_Event_SingleClick; //事件->单击****    }    else if ((KeyCfg->KEY_Action != KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_DOUBLE_CLICK_TIME)) //动作:保持释放,双击等待时间未到    {      KeyCfg->KEY_Count++;                             //双击等待计数      KeyCfg->KEY_Status = KEY_Status_WaitSecondPress; //状态->维持      KeyCfg->KEY_Event = KEY_Event_Null;              //事件->无    }    else //动作:双击等待时间内,再次按下    {      tmpcnt[KeyCfg->KEY_Label] = KeyCfg->KEY_Count;  //计数保存      KeyCfg->KEY_Count = 0;                          //计数清零      KeyCfg->KEY_Status = KEY_Status_SecondDebounce; //状态->再次消抖      KeyCfg->KEY_Event = KEY_Event_Null;             //事件->无    }    break;
  //状态:再次消抖  case KEY_Status_SecondDebounce:    if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_DEBOUNCE_TIME)) //动作:保持按下,消抖时间已到    {      KeyCfg->KEY_Count = 0;                       //计数清零      KeyCfg->KEY_Status = KEY_Status_SecondPress; //状态->确认再次按下      KeyCfg->KEY_Event = KEY_Event_Null;          //事件->无    }    else if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_DEBOUNCE_TIME)) //动作:保持按下,消抖时间未到    {      KeyCfg->KEY_Count++;                            //消抖计数      KeyCfg->KEY_Status = KEY_Status_SecondDebounce; //状态->维持      KeyCfg->KEY_Event = KEY_Event_Null;             //事件->无    }    else //动作:释放,消抖时间未到,判定为抖动    {      KeyCfg->KEY_Count = KeyCfg->KEY_Count + tmpcnt[KeyCfg->KEY_Label]; //计数置数      KeyCfg->KEY_Status = KEY_Status_WaitSecondPress;                   //状态->等待再按      KeyCfg->KEY_Event = KEY_Event_Null;                                //事件->无    }    break;
  //状态:再次按下  case KEY_Status_SecondPress:    if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_LONG_PRESS_TIME)) //动作:保持按下,长按时间已到    {      KeyCfg->KEY_Count = 0;                            //计数清零      KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->确认长按      KeyCfg->KEY_Event = KEY_Event_SingleClick;        //事件->先响应单击    }    else if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_LONG_PRESS_TIME)) //动作:保持按下,长按时间未到    {      KeyCfg->KEY_Count++;                         //计数      KeyCfg->KEY_Status = KEY_Status_SecondPress; //状态->维持      KeyCfg->KEY_Event = KEY_Event_Null;          //事件->无    }    else //动作:释放,长按时间未到    {      KeyCfg->KEY_Count = 0;                     //计数清零      KeyCfg->KEY_Status = KEY_Status_Idle;      //状态->空闲      KeyCfg->KEY_Event = KEY_Event_DoubleClick; //事件->双击    }    break;  }
  if (KeyCfg->KEY_Event != KEY_Event_Null) //事件记录    key_event[KeyCfg->KEY_Label] = KeyCfg->KEY_Event;}

使用特权

评论回复
板凳
两只袜子|  楼主 | 2025-1-9 10:03 | 只看该作者
//按键模式使能选择typedef enum{  KEY_Mode_OnlySinge = 0x00,         //只有单击  KEY_Mode_Long = 0x01,              //单击长按  KEY_Mode_Quick = 0x02,             //单击连按  KEY_Mode_Long_Quick = 0x03,        //单击长按连按  KEY_Mode_Double = 0x04,            //单击双击  KEY_Mode_Long_Double = 0x05,       //单击长按双击  KEY_Mode_Quick_Double = 0x06,      //单击连按双击  KEY_Mode_Long_Quick_Double = 0x07, //单击长按连按双击} KEY_Mode_TypeDef;
//按键配置typedef struct{  uint8_t KEY_Label;             //按键标号  KEY_Mode_TypeDef KEY_Mode;              //按键模式  uint16_t KEY_Count;            //按键按下计时  KEY_Action_TypeDef KEY_Action; //按键动作,按下或释放  KEY_Status_TypeDef KEY_Status; //按键状态  KEY_Event_TypeDef KEY_Event;   //按键事件} KEY_Configure_TypeDef;
extern KEY_Configure_TypeDef KeyConfig[];extern KEY_Event_TypeDef key_event[];
void KEY_ReadStateMachine(KEY_Configure_TypeDef *KeyCfg);
#endif

‍源文件 my_key.c
#include "my_key.h"
static uint8_t KEY_ReadPin(uint8_t key_label){  switch (key_label)  {  case 0:    return (uint8_t)HAL_GPIO_ReadPin(K0_GPIO_Port, K0_Pin);  case 1:    return (uint8_t)HAL_GPIO_ReadPin(K1_GPIO_Port, K1_Pin);  case 2:    return (uint8_t)HAL_GPIO_ReadPin(K2_GPIO_Port, K2_Pin);  case 3:    return (uint8_t)HAL_GPIO_ReadPin(K3_GPIO_Port, K3_Pin);  case 4:    return (uint8_t)HAL_GPIO_ReadPin(K4_GPIO_Port, K4_Pin);  // case X:  //   return (uint8_t)HAL_GPIO_ReadPin(KX_GPIO_Port, KX_Pin);  }  return 0;}
KEY_Configure_TypeDef KeyConfig[] = {    {0, KEY_Mode_Long_Quick_Double, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},    {1, KEY_Mode_Long_Quick_Double, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},    {2, KEY_Mode_Long_Quick_Double, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},    {3, KEY_Mode_Long_Quick_Double, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},    {4, KEY_Mode_Long_Quick_Double, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},    // {X, KEY_Mode_Long_Quick_Double, 0, KEY_Action_Release, KEY_Status_Idle, KEY_Event_Null},};
KEY_Event_TypeDef key_event[ARR_LEN(KeyConfig)] = {KEY_Event_Null}; //按键事件//按键状态处理void KEY_ReadStateMachine(KEY_Configure_TypeDef *KeyCfg){  static uint16_t tmpcnt[ARR_LEN(KeyConfig)] = {0};  //按键动作读取  if (KEY_ReadPin(KeyCfg->KEY_Label) == KEY_PRESSED_LEVEL)    KeyCfg->KEY_Action = KEY_Action_Press;  else    KeyCfg->KEY_Action = KEY_Action_Release;
  //状态机  switch (KeyCfg->KEY_Status)  {  //状态:空闲  case KEY_Status_Idle:    if (KeyCfg->KEY_Action == KEY_Action_Press) //动作:按下    {      KeyCfg->KEY_Status = KEY_Status_Debounce; //状态->消抖      KeyCfg->KEY_Event = KEY_Event_Null;       //事件->无    }    else //动作:默认动作,释放    {      KeyCfg->KEY_Status = KEY_Status_Idle; //状态->维持      KeyCfg->KEY_Event = KEY_Event_Null;   //事件->无    }    break;
  //状态:消抖  case KEY_Status_Debounce:    if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_DEBOUNCE_TIME)) //动作:保持按下,消抖时间已到    {      KeyCfg->KEY_Count = 0;                        //计数清零      KeyCfg->KEY_Status = KEY_Status_ConfirmPress; //状态->确认按下      KeyCfg->KEY_Event = KEY_Event_Null;           //事件->无    }    else if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_DEBOUNCE_TIME)) //动作:保持按下,消抖时间未到    {      KeyCfg->KEY_Count++;                      //消抖计数      KeyCfg->KEY_Status = KEY_Status_Debounce; //状态->维持      KeyCfg->KEY_Event = KEY_Event_Null;       //事件->无    }    else //动作:释放,消抖时间未到,判定为抖动    {      KeyCfg->KEY_Count = 0;                //计数清零      KeyCfg->KEY_Status = KEY_Status_Idle; //状态->空闲      KeyCfg->KEY_Event = KEY_Event_Null;   //事件->无    }    break;
  //状态:确认按下  case KEY_Status_ConfirmPress:    if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_LONG_PRESS_TIME)) //动作:保持按下,长按时间已到    {      KeyCfg->KEY_Count = KEY_QUICK_CLICK_TIME;         //计数置数,生成第一次连按事件      KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->确认长按      KeyCfg->KEY_Event = KEY_Event_Null;               //事件->无    }    else if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_LONG_PRESS_TIME)) //动作:保持按下,长按时间未到    {      KeyCfg->KEY_Count++;                          //长按计数      KeyCfg->KEY_Status = KEY_Status_ConfirmPress; //状态->维持      KeyCfg->KEY_Event = KEY_Event_Null;           //事件->无    }    else //动作:长按时间未到,释放    {      if ((uint8_t)(KeyCfg->KEY_Mode) & 0x04) //双击模式      {        KeyCfg->KEY_Count = 0;                           //计数清零        KeyCfg->KEY_Status = KEY_Status_WaitSecondPress; //状态->等待再按        KeyCfg->KEY_Event = KEY_Event_Null;              //事件->无      }      else //非双击模式      {        KeyCfg->KEY_Count = 0;                     //计数清零        KeyCfg->KEY_Status = KEY_Status_Idle;      //状态->空闲        KeyCfg->KEY_Event = KEY_Event_SingleClick; //事件->单击****      }    }    break;
  //状态:确认长按  case KEY_Status_ConfirmPressLong:    if (KeyCfg->KEY_Action == KEY_Action_Press) //动作:保持按下    {      if ((uint8_t)KeyCfg->KEY_Mode & 0x02) //连按模式      {        if (KeyCfg->KEY_Count >= KEY_QUICK_CLICK_TIME) //连按间隔时间已到        {          KeyCfg->KEY_Count = 0;                            //计数清零          KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->维持          KeyCfg->KEY_Event = KEY_Event_QuickClick;         //事件->连按****        }        else //连按间隔时间未到        {          KeyCfg->KEY_Count++;                              //连按计数          KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->维持          KeyCfg->KEY_Event = KEY_Event_Null;               //事件->无        }      }      else //非连按模式      {        KeyCfg->KEY_Count = 0;                            //计数清零        KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->维持        KeyCfg->KEY_Event = KEY_Event_Null;               //事件->无      }    }    else //动作:长按下后释放    {      if ((uint8_t)KeyCfg->KEY_Mode & 0x01) //长按模式      {        KeyCfg->KEY_Count = 0;                   //计数清零        KeyCfg->KEY_Status = KEY_Status_Idle;    //状态->空闲        KeyCfg->KEY_Event = KEY_Event_LongPress; //事件->长按****      }      else //非长按模式      {        KeyCfg->KEY_Count = 0;                     //计数清零        KeyCfg->KEY_Status = KEY_Status_Idle;      //状态->空闲        KeyCfg->KEY_Event = KEY_Event_SingleClick; //事件->单击****      }    }    break;
  //状态:等待是否再次按下  case KEY_Status_WaitSecondPress:    if ((KeyCfg->KEY_Action != KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_DOUBLE_CLICK_TIME)) //动作:保持释放,双击等待时间已到    {      KeyCfg->KEY_Count = 0;                     //计数清零      KeyCfg->KEY_Status = KEY_Status_Idle;      //状态->空闲      KeyCfg->KEY_Event = KEY_Event_SingleClick; //事件->单击****    }    else if ((KeyCfg->KEY_Action != KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_DOUBLE_CLICK_TIME)) //动作:保持释放,双击等待时间未到    {      KeyCfg->KEY_Count++;                             //双击等待计数      KeyCfg->KEY_Status = KEY_Status_WaitSecondPress; //状态->维持      KeyCfg->KEY_Event = KEY_Event_Null;              //事件->无    }    else //动作:双击等待时间内,再次按下    {      tmpcnt[KeyCfg->KEY_Label] = KeyCfg->KEY_Count;  //计数保存      KeyCfg->KEY_Count = 0;                          //计数清零      KeyCfg->KEY_Status = KEY_Status_SecondDebounce; //状态->再次消抖      KeyCfg->KEY_Event = KEY_Event_Null;             //事件->无    }    break;
  //状态:再次消抖  case KEY_Status_SecondDebounce:    if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_DEBOUNCE_TIME)) //动作:保持按下,消抖时间已到    {      KeyCfg->KEY_Count = 0;                       //计数清零      KeyCfg->KEY_Status = KEY_Status_SecondPress; //状态->确认再次按下      KeyCfg->KEY_Event = KEY_Event_Null;          //事件->无    }    else if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_DEBOUNCE_TIME)) //动作:保持按下,消抖时间未到    {      KeyCfg->KEY_Count++;                            //消抖计数      KeyCfg->KEY_Status = KEY_Status_SecondDebounce; //状态->维持      KeyCfg->KEY_Event = KEY_Event_Null;             //事件->无    }    else //动作:释放,消抖时间未到,判定为抖动    {      KeyCfg->KEY_Count = KeyCfg->KEY_Count + tmpcnt[KeyCfg->KEY_Label]; //计数置数      KeyCfg->KEY_Status = KEY_Status_WaitSecondPress;                   //状态->等待再按      KeyCfg->KEY_Event = KEY_Event_Null;                                //事件->无    }    break;
  //状态:再次按下  case KEY_Status_SecondPress:    if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count >= KEY_LONG_PRESS_TIME)) //动作:保持按下,长按时间已到    {      KeyCfg->KEY_Count = 0;                            //计数清零      KeyCfg->KEY_Status = KEY_Status_ConfirmPressLong; //状态->确认长按      KeyCfg->KEY_Event = KEY_Event_SingleClick;        //事件->先响应单击    }    else if ((KeyCfg->KEY_Action == KEY_Action_Press) && (KeyCfg->KEY_Count < KEY_LONG_PRESS_TIME)) //动作:保持按下,长按时间未到    {      KeyCfg->KEY_Count++;                         //计数      KeyCfg->KEY_Status = KEY_Status_SecondPress; //状态->维持      KeyCfg->KEY_Event = KEY_Event_Null;          //事件->无    }    else //动作:释放,长按时间未到    {      KeyCfg->KEY_Count = 0;                     //计数清零      KeyCfg->KEY_Status = KEY_Status_Idle;      //状态->空闲      KeyCfg->KEY_Event = KEY_Event_DoubleClick; //事件->双击    }    break;  }
  if (KeyCfg->KEY_Event != KEY_Event_Null) //事件记录    key_event[KeyCfg->KEY_Label] = KeyCfg->KEY_Event;}

定时器中断调用和主函数使用
中断周期为1ms

//调用uint32_t tim_cnt = 0;void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){  if (htim->Instance == htim1.Instance)  {    tim_cnt++;    if (tim_cnt % 1 == 0) // 1ms    {      KEY_ReadStateMachine(&KeyConfig[0]);      KEY_ReadStateMachine(&KeyConfig[1]);      KEY_ReadStateMachine(&KeyConfig[2]);      KEY_ReadStateMachine(&KeyConfig[3]);      KEY_ReadStateMachine(&KeyConfig[4]);    }  }}
int main(void){  while (1)  {    if (key_event[1] == KEY_Event_SingleClick) //单击    {      something1();    }    if (key_event[2] == KEY_Event_LongPress) //长按    {      something2();    }    if ((key_event[3] == KEY_Event_QuickClick) || (key_event[3] == KEY_Event_SingleClick)) //连按    {      something3();    }    if (key_event[4] == KEY_Event_DoubleClick) //双击    {      something4();    }    memset(key_event, KEY_Event_Null, sizeof(key_event)); //清除事件  }}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2073

主题

7542

帖子

10

粉丝