MCU全功能按键接口

在单片机开发中,由于资源受限而没有平台的支持,每次开发都要重写很多代码,应用的千奇百怪的需求更是加剧了这种困难。解决问题的办法是,总结常见的需求,分析它,得出即高效有通用的解决方案。

今天我就来为大家提供一种按键的解决方案,它易用,高效,节省资源!

先给出这个按键模块解决方案的全部代码,稍后再来分析。

keyif.h内容:

   1:  #ifndef __KEY_IF_H__
   2:  #define __KEY_IF_H__
   3:  ////////////////////////////////////////////////////////////////////////////////
   4:  typedef unsigned char   u8_t;
   5:   
   6:  #define KEY_NUM_MAX     (8)
   7:   
   8:  #define KEY_STA_BEGIN   (0)
   9:  #define KEY_STA_KEEP    (1)
  10:  #define KEY_STA_END     (2)
  11:   
  12:  #define KEY_PERIOD_MS   (25)
  13:   
  14:  void kif_Init(void);
  15:  void kif_TickHook(void);
  16:   
  17:  /*
  18:      kindex 按键索引,从0开始,而小于KEY_NUM_MAX。
  19:      返回值:按键被按下返回非零,按键被抬起返回零。
  20:  */
  21:  extern u8_t KeyRead(u8_t kindex);
  22:  /*
  23:      kindex 按键索引,从0开始,而小于KEY_NUM_MAX。
  24:      ksta   按键状态,取值为KEY_STA_系列宏。
  25:      ktick  按键计时,以KEY_PERIOD_MS时间为计数单位。
  26:      返回值:如果本次按键操作已经处理妥当,就返回非零。
  27:  */
  28:  extern u8_t KeyProc(u8_t kindex, u8_t ksta, u8_t ktick);
  29:   
  30:  ////////////////////////////////////////////////////////////////////////////////
  31:  #endif /* __KEY_IF_H__ */
  32:   

keyif.c内容:

   1:  #include "keyif.h"
   2:  #include <string.h>
   3:   
   4:  #if defined(KEY_NUM_MAX) && (KEY_NUM_MAX > 0)
   5:   
   6:  #if KEY_NUM_MAX > 255
   7:  #error 该模块为8位单片机优化,不支持255个以上按键。
   8:  #endif
   9:   
  10:  static u8_t kticks[KEY_NUM_MAX];            // 每个按键需要1字节计时器。
  11:  static u8_t kstats[(KEY_NUM_MAX+7)/8];      // 每个按键需要1BIT的状态标志。
  12:  static u8_t kvalid[(KEY_NUM_MAX+7)/8];      // 每个按键需要1BIT的有效标志。
  13:  ////////////////////////////////////////////////////////////////////////////////
  14:  //|          |
  15:  //| 函数名称 |: kif_Init
  16:  //| 功能描述 |: 
  17:  //|          |: 
  18:  //| 参数列表 |: 
  19:  //|          |: 
  20:  //| 返    回 |: 
  21:  //|          |: 
  22:  //| 备注信息 |: 
  23:  //|          |: 
  24:  ////////////////////////////////////////////////////////////////////////////////
  25:  void kif_Init(void)
  26:  {
  27:      memset(kticks, 0, sizeof(kticks));
  28:      memset(kstats, 0, sizeof(kstats));
  29:      memset(kvalid, 0, sizeof(kvalid));
  30:  }
  31:   
  32:  ////////////////////////////////////////////////////////////////////////////////
  33:  //|          |
  34:  //| 函数名称 |: kif_TickHook
  35:  //| 功能描述 |: 
  36:  //|          |: 
  37:  //| 参数列表 |: 
  38:  //|          |: 
  39:  //| 返    回 |: 
  40:  //|          |: 
  41:  //| 备注信息 |: 
  42:  //|          |: 
  43:  ////////////////////////////////////////////////////////////////////////////////
  44:  void kif_TickHook(void)
  45:  {
  46:      u8_t grp, msk;
  47:      u8_t now;
  48:      u8_t i;
  49:      
  50:      grp = 0;
  51:      msk = 1;
  52:      for(i = 0; i < KEY_NUM_MAX; ++i){
  53:          now = (KeyRead(i) ? msk : 0);
  54:          if((kstats[grp] ^ now) & msk){
  55:              // 按键状态发生变化。
  56:              if(now){
  57:                  // 按键刚被按下。
  58:                  kticks[i] = 0;
  59:                  kstats[grp] |= msk;
  60:                  kvalid[grp] |= msk;
  61:                  if(KeyProc(i, KEY_STA_BEGIN, 0)){
  62:                      kvalid[grp] &= ~msk;
  63:                  }
  64:              }
  65:              else{
  66:                  // 按键刚被抬起。
  67:                  kticks[i] += 1;
  68:                  kstats[grp] &= ~msk;
  69:                  KeyProc(i, KEY_STA_END, kticks[i]);
  70:              }
  71:          }
  72:          else if(now){
  73:              // 按键保持按下状态。
  74:              kticks[i] += 1;
  75:              if(kvalid[grp] & msk){
  76:                  // 按键处于有效状态。
  77:                  if(KeyProc(i, KEY_STA_KEEP, kticks[i])){
  78:                      kvalid[grp] &= ~msk;
  79:                  }
  80:              }
  81:          }
  82:          // 处理用于加速执行的中间变量。
  83:          msk <<= 1;
  84:          if(msk == 0){
  85:              msk = 1;
  86:              grp++;
  87:          }
  88:      }
  89:  }
  90:   
  91:  #else /* KEY_NUM_MAX */
  92:   
  93:  void kif_Init(void){ ; }
  94:  void kif_TickHook(void){ ; }
  95:   
  96:  #endif /* KEY_NUM_MAX */
  97:   

example.c内容:

   1:  #include "keyif.h"
   2:   
   3:  // 读取按键物理状态的函数。
   4:  u8_t KeyRead(u8_t kindex)
   5:  {
   6:      switch(kindex){
   7:      case 0:     // 按键#0
   8:          if(PIN_DOWN(0)){
   9:              return 1;
  10:          }
  11:          return 0;
  12:      case 1:     // 按键#1
  13:          if(PIN_DOWN(1)){
  14:              return 1;
  15:          }
  16:          return 0;
  17:      case 2:     // 按键#2
  18:          if(PIN_DOWN(2)){
  19:              return 1;
  20:          }
  21:          return 0;
  22:      case 3:     // 按键#3
  23:          if(PIN_DOWN(3)){
  24:              return 1;
  25:          }
  26:          return 0;
  27:      }
  28:      return 0;
  29:  }
  30:   
  31:  // 按键事件处理函数。
  32:  u8_t KeyProc(u8_t kindex, u8_t ksta, u8_t ktick)
  33:  {
  34:      switch(kindex){
  35:      case 0:     // 按键#0
  36:          if(ksta == KEY_STA_BEGIN){
  37:              // 按键被按下,TODO SOMETHING。
  38:              
  39:              return 0;
  40:          }
  41:          else if(ksta == KEY_STA_KEEP){
  42:              // 按键被保持,TODO SOMETHING。
  43:              
  44:              return 0;
  45:          }
  46:          else if(ksta == KEY_STA_END){
  47:              // 按键被松开,TODO SOMETHING。
  48:              
  49:              return 0;
  50:          }
  51:          break;
  52:      case 1:     // 按键#1
  53:          if(ksta == KEY_STA_KEEP && ktick == 1000/KEY_PERIOD_MS){
  54:              // 按键按下保持了1000毫秒,TODO SOMETHING。
  55:              
  56:              return 1;
  57:          }
  58:          break;
  59:      case 2:     // 按键#2
  60:          if(ksta == KEY_STA_KEEP && ktick == 1){
  61:              // 按键被按下,具备了间隔KEY_PERIOD_MS毫秒的去抖动时间,TODO SOMETHING。
  62:              
  63:              return 1;
  64:          }
  65:          break;
  66:      case 3:     // 按键#3
  67:          if(ksta == KEY_STA_END){
  68:              // 响应按键松开事件,TODO SOMETHING。
  69:              
  70:          }
  71:          break;
  72:      }
  73:      return 0;
  74:  }
  75:   
  76:  ////////////////////////////////////////////////////////////////////////////////
  77:  //|          |
  78:  //| 函数名称 |: main
  79:  //| 功能描述 |: 
  80:  //|          |: 
  81:  //| 参数列表 |: 
  82:  //|          |: 
  83:  //| 返    回 |: 
  84:  //|          |: 
  85:  //| 备注信息 |: 
  86:  //|          |: 
  87:  ////////////////////////////////////////////////////////////////////////////////
  88:  int main(void)
  89:  {
  90:      // 初始化KEYIF。
  91:      kif_Init();
  92:      
  93:      while(1){
  94:          delay(KEY_PERIOD_MS);
  95:          // 以KEY_PERIOD_MS毫秒为周期调用。
  96:          kif_TickHook();
  97:      }
  98:  }

上面的代码包括了3各文件,keyif.h、keyif.c、example.c。

其中example.c是使用举例,它提供了两个必须的函数,

u8_t KeyRead(u8_t kindex);
u8_t KeyProc(u8_t kindex, u8_t ksta, u8_t ktick);

先来分析KeyRead()函数:

它是读取物理按键的底层函数,该函数被KEYIF模块调用。当按键按下时,它必须返回非零;按键抬起时,返回零即可。例如我们通常把按键引脚设计为低电平为按下状态,那么当读取对应引脚电平状态时,如果读取的引脚为低电平则返回非零,高电平就返回零。

kindex参数是用于区别物理引脚的。例如:kindex为0时读取GPIO_B4,kindex为1时读取GPIO_A1,等等。

再来分析KeyProc()函数:

该函数是用户的按键响应函数,在该函数中用户可以加入一切按键响应代码。该函数被KEYIF模块调用。kindex参数是按键的索引,用于标识按键;ksta参数是按键的当前状态,取值为KEY_STA_BEGIN、KEY_STA_KEEP、KEY_STA_END其中之一。BEGIN代表按键刚被按下,KEEP代表按键保持在按下状态,END代表按键被松开了。ktick参数是KEEP状态的计时器,表示按键按下状态保持了多久,单位是KEY_PERIOD_MS毫秒。

KeyProc()函数的返回值决定了本次按键操作是否还会有后续的KEEP事件,如果用户不需要后续的KEEP事件,返回非零即可。当用户告诉KEYIF不需要后续KEEP事件后,按键保持在按下状态也不会产生KEEP事件,直到下一次按键操作。无论有无KEEP事件,BEGIN和END事件始终会有的,无法关闭!

example.c文件中给出了几个应用示例,其实这个按键框架能实现的操作远不止这些!用户自己根据需求可以轻易写出各种代码。

要使用KEYIF,必须在用户代码开始处调用kif_Init()函数来初始化KEYIF。然后在主循环或者时钟中断里,以KEY_PERIOD_MS毫秒为周期调用kif_TickHook()函数。

http://115.com/file/an06j9if

posted @ 2012-05-26 12:16  专注成就专业  阅读(2164)  评论(0编辑  收藏  举报