按键的状态机方法实现

  按键的状态机方法实现 为什么要使用状态机?使用状态机有啥好处?是不是装13? 要我说,很简单的一个按键,可以不必使用,要是你有很多按键,或者需要判断长按,短按,双击666,那么,状态机方法可以让你设计简洁,功能稳定,不会出现你自己测试好了,别人测试就会出问题;或者今天测试没有问题,第二天不稳定了,是不是很糟心?功能复杂势必会声明很多个变量一大丢,然后把功能也 写进按键处理里面,是不是很乱,不好读懂?按键识别就是按键部分,不要有任何功能上面的处理,要通过接口去传递。这么说,本文主要就是两个思想:状态机和接口。懂得人自然懂。下面详细说说,实例上菜。 开局一张图,内容全靠编。

正确,合理的状态划分是做好任何功能的前提,根据按键全生命周期的发生顺序,我划分了上面4个状态。去抖大家都清楚,按键值持续2-30Ms才进入正式的识别按键值状态,否则退回idle状态。我说的是持续,不是间隔2--30Ms再次去确认一次。好了,我把程序里面需要用到的一些数据类型都贴出来吧:

  1 enum KEYDOWNSTATE
  2 {//按键状态机
  3 MKEY_IDLE, ///<待机状态
  4 MKEY_DELAY, ///<去抖状态
  5 MKEY_GETVALUE,///<取值状态
  6 MKEY_UNFIX ///<取值成功状态
  7 };
  8 typedef struct
  9 {
 10 unsigned char modeKey : 1;
 11 unsigned char levelKey : 1;
 12 unsigned char powerKey : 1;
 13 unsigned char datb3 : 1;
 14 unsigned char datb4 : 1;
 15 unsigned char datb5 : 1;
 16 unsigned char datb6 : 1;
 17 unsigned char datb7 : 1;
 18 }datbit; //使用位段
 19 typedef union {
 20 unsigned char bytedat;
 21 datbit data_bits;
 22 }ByteChar; //非常方便采集数据和取值的一个联合体
 23 enum EKEYVALUE
 24 {//按键值
 25 MMODE = 0XFE,
 26 MLEVEL = 0XFD,
 27 MREUSE = 0XFC,
 28 MPOWERON = 0XFB,
 29 MTEST = 0X07
 30 }; //无论是独立式按键还是adc方式,都会有个值或者范围
 31 enum EKEYTYPE
 32 {
 33 MNONE,
 34 MSHORTKEY = 0X13, //短按,随意定的一个值
 35 M2SKEY, //长按
 36 M10KEY, //复位按
 37 }; //代表着按键长按,短按,复位 
 38 typedef struct {
 39 bit getkeyFlag; //按键值获取成功
 40 ByteChar gkeyValueIn; //按键值
 41 UINT8 gDelayType; //根据按键delay长短确定的类型
 42 }keyInterface_t; 
 43 keyInterface_t keyInterface; //按键驱动模块与外部的接口
 44 /************************************************************************************************************
 45 * void keyDriver(void)
 46 * 注意:间隔10ms左右调用一下本函数
 47 * 本函数采用状态机方式编程,适合于独立按键,可以识别出长按,短按,超长10S按。
 48 ************************************************************************************************************/
 49   static void keyDriver(void)
 50 {
 51  ByteChar keyValueIn;
 52  keyValueIn.bytedat = 0xFF;
 53  keyValueIn.data_bits.modeKey = MODEKEY;
 54  keyValueIn.data_bits.levelKey = LevelKEY;
 55  keyValueIn.data_bits.powerKey = PowerKEY;
 56  switch (keyState)
 57  {
 58  case MKEY_IDLE: /* constant-expression */
 59  keyDownConts = 0;
 60  keyInterface.gDelayType = MNONE;
 61  if (keyValueIn.bytedat != 0xFF)
 62  {
 63  keyInterface.gkeyValueIn.bytedat = keyValueIn.bytedat;
 64  keyState = MKEY_DELAY;
 65  keyDownConts = 0;
 66  }
 67  break;
 68  case MKEY_DELAY: /* 去抖动 */
 69  if (keyInterface.gkeyValueIn.bytedat ==keyValueIn.bytedat)
 70  {
 71  keyDownConts++;
 72  }
 73  else
 74  {
 75  keyDownConts = 0;
 76  keyState = MKEY_IDLE;
 77  }
 78  if (keyDownConts > 3)
 79  {
 80  keyState = MKEY_GETVALUE;
 81  }
 82  break;
 83  case MKEY_GETVALUE: /* 进入判断按键 */
 84  keyDownConts++;
 85  if (keyDownConts > KEY10s) //如果是复原按,则无需等待松开按键
 86  {
 87  keyInterface.gDelayType = M10KEY; //复位按
 88  keyState = MKEY_UNFIX;
 89  }
 90  if (keyDownConts > KEY2s) //如果是长按,则无需等待松开按键
 91  {
 92  keyInterface.gDelayType = M2SKEY; //长按
 93  keyState = MKEY_UNFIX;
 94  }
 95  if (keyValueIn.bytedat == 0xFF)
 96  {
 97  keyUpConts++;
 98  }
 99  else
100  {
101  keyUpConts = 0;
102  }
103  if (keyUpConts > 2)
104  {
105  if (keyDownConts > KEYSHORTMIN &&keyDownConts < KEYSHORTMAX) //如果是短按,则需等待松开按键
106  {
107  keyInterface.gDelayType = MSHORTKEY; //短按
108  keyState = MKEY_UNFIX;
109  }
110  else
111  {
112  keyState = MKEY_IDLE;
113  }
114  }
115  break;
116  case MKEY_UNFIX: /* 此状态代表按键可以正常取值 */
117  _nop_();
118  break;
119  default:
120  _nop_();
121  break;
122  }
123 }

再次总结一下,keyState变量就是交际花,在不同的状态切换;按键驱动模块与外部的接口keyInterface_t keyInterface ,使用一个结构体变量,实现了模块化。

针对本文中所有的错误和不足,欢迎交流,交流技术和项目合作均可。等你来撩我哦!加我请说明来意,谢谢。

原文地址:https://www.cnblogs.com/zhihui-3669/p/13525383.html