[程序源码] 东拼西凑按键扫描,一键三值,队列循环

[复制链接]
 楼主| ailingg 发表于 2017-12-11 10:07 | 显示全部楼层 |阅读模式
本帖最后由 ailingg 于 2017-12-11 10:34 编辑


       安富莱的按键队列和单按键扫描,本坛的“datouyuan”的“我根据5个IO口扫描25个按键写的的代码”里的按键键值表。这种搞法很费ram,但组合键相当方便。
每个按键都有自己的去抖、长按、重复键值计数器变量,每个按键有自己的参数,包括长按、重复键值、去抖的计数值,每个按键有按下、弹起、长按三个键值。
       组合键,可以任意组合。
  1. /************************************************************************************************************
  2. * 文 件 名   : bsp_Key.c
  3. * 负 责 人   : lll
  4. * 创建日期   : 2017年11月28日
  5. * 文件描述   : 按键扫描与按键事件处理
  6. * 版权说明   : Copyright (c) 2008-2017   xx xx xx xx 技术有限公司
  7. * 其    他   :
  8. * 修改日志   :
  9. **************************************************************************************************************/
  10. #include <stdio.h>
  11. #include <stdlib.h>

  12. #include "bsp_Vacuum260.h"
  13. #include "bsp_Key.h"
  14. #include "bsp_LED.h"
  15. #include "bsp_74hc595.h"



  16. typedef struct
  17. {
  18.     void  (*KeyEventFunc)(void);
  19.     UINT16 LongTime;                        /* 长按键生效时间,0表示非长按键  */
  20.     UINT8  ReentryTime;                     /* 长按键重复生效时间,0表示键值不重复生效  */
  21.     UINT8  FiltTime;                        /* 去抖滤波时间      */
  22.     UINT8  KeyVal;                          /* 按键起始键值,每个按键有三个键值,enum定义依次为“按下”“弹起”“长按”  */
  23.    
  24. }KeyElem_TypeDef;

  25. // 按键元素表
  26. KeyElem_TypeDef const KeyElem[KEY_QTY] = {
  27.     { Key_StopKey,           600,    0,    4,   KEY_0_DOWN },
  28.     { Key_VacuumTimeSet,     0,      0,    4,   KEY_1_DOWN },
  29.     { Key_SealTimeSet,       0,      0,    4,   KEY_2_DOWN },
  30.     { Key_DigitInc,          150,    20,   4,   KEY_3_DOWN },
  31.     { Key_DigitDec,          150,    20,   4,   KEY_4_DOWN },
  32.     { Key_HeatLevelSet,      0,      0,    4,   KEY_5_DOWN },
  33.     { Key_SealTimeMaxInc,    150,    20,   4,   KEY_6_DOWN }, // KEY_0和KEY_3组合
  34.     { Key_SealTimeMaxDec,    150,    20,   4,   KEY_7_DOWN }  // KEY_0和KEY_4组合
  35. };


  36. KeyMode_TypeDef KeyMode;

  37. KeyMsg_TypeDef  KeyMsg;

  38. KeyQueue_TypeDef KeyQueue;                   /* 键值循环队列结构变量  */

  39. QelemType KeyCode[KEY_QTY];                  /* 循环队列键值数组  */

  40. persistent KeyVar_TypeDef  KeyVar[KEY_QTY];  /* 按键扫描结构变量 */

  41. persistent volatile UINT8 KeysState;
  42. persistent volatile UINT8 Key_SetPoint;
  43. persistent volatile UINT8 *gParamPtr;        /* 读写参数缓存区指针  */


  44. extern UINT8 ByteMod( UINT8 dividend, UINT8 divisor );
  45. extern UINT8 ByteDiv( UINT8 dividend, UINT8 divisor );
  46. /***********************************************************************************************************
  47. * [url=home.php?mod=space&uid=247401]@brief[/url]  按键参数初始化
  48. ************************************************************************************************************/
  49. void Key_Init(void)
  50. {
  51.     UINT8 i;
  52.    
  53.     KeyQueue.Buf = KeyCode;
  54.     KeyQueue.rear = 0;
  55.     KeyQueue.front = 0;   
  56.    
  57.     for( i = 0; i < KEY_QTY; i++ )
  58.     {
  59.         KeyVar[i].KeyGet = FALSE;
  60.         KeyVar[i].KeyState = KEY_UP;
  61.         KeyVar[i].FiltCnt = 0;
  62.         KeyVar[i].LongCnt = 0;
  63.         KeyVar[i].ReentryCnt = 0;
  64.     }
  65.     Key_SetPoint = NO;
  66.     KeyMode.bMode = 0; //0=直接加减方式,1=位选加减方式
  67. }

  68. /**************************************************************************************************************
  69. * @brief  按键队列压入键值
  70. * @param  _KeyCode
  71. **************************************************************************************************************/
  72. void Key_QueueIn( UINT8 _KeyCode )
  73. {
  74.     KeyQueue.Buf[KeyQueue.rear] = _KeyCode;

  75.     if( ++KeyQueue.rear >= KEY_QTY )
  76.     {
  77.         KeyQueue.rear = 0;
  78.     }
  79. }
  80. /**************************************************************************************************************
  81. * @brief   按键队列弹出键值
  82. * [url=home.php?mod=space&uid=266161]@return[/url]  键值
  83. **************************************************************************************************************/
  84. UINT8 Key_QueueOut( void )
  85. {
  86.     UINT8 ret;

  87.     if( KeyQueue.front == KeyQueue.rear )
  88.     {
  89.         return KEY_NONE;
  90.     }
  91.     else
  92.     {
  93.         ret = KeyQueue.Buf[KeyQueue.front];

  94.         if( ++KeyQueue.front >= KEY_QTY )
  95.         {
  96.             KeyQueue.front = 0;
  97.         }
  98.         return ret;
  99.     }
  100. }
  101. /**************************************************************************************************************
  102. * @brief   按键扫描,获取按键ID
  103. *          矩阵式扫描,RB7,RB6 为扫描读取口;RB2,RB1,RB0通过NPN三极管反向后作为键盘的扫描口。
  104. * @Param   pPORTx:键盘扫描所在端口指针   
  105. *          pKeyID: 按键ID顺序存放数组指针                                    
  106. * [url=home.php?mod=space&uid=536309]@NOTE[/url]    按键引脚硬件分布
  107. *          矩阵式扫描,RB7,RB6 为扫描读取口;RB2,RB1,RB0通过NPN三极管反向后作为键盘的扫描口。
  108. *
  109. *                          RB0_____。_____|_____________|____________
  110. *                                         |0            |3
  111. *                          RB1_____。_____|_____________|____________
  112. *                                         |1            |4
  113. *                          RB2_____。_____|_____________|____________
  114. *                                         |2            |5
  115. *                          RB6____________|             |
  116. *                                                       |
  117. *                          RB7__________________________|                                          
  118. **************************************************************************************************************/

  119. /* 根据硬件情况,扫描从低位左移,检测从高位右移,因此按键ID表的排列顺序是3,0,4,1, 5,2   */
  120. const UINT8 KeyID_Table[3][2] = { { 3, 0 },{ 4, 1 },{ 5, 2 } };

  121. /* 按键扫描后,按键ID按0-N的排列顺序存入数组 */
  122. UINT8 KeyID[KEY_QTY] = { 0 ,1, 2, 3, 4, 5, 6, 7 };

  123. static void GetKeyID( PORT_TypeDef *pPORTx, UINT8 *pKeyID )
  124. {
  125.     UINT8 idx, i, j;

  126.     for( i = 0; i < KEY_QTY; i++ )
  127.     {
  128.         pKeyID[i] = 255;                         /* 255表示按键未按下 */
  129.     }
  130.     for( i = 0; i < 3; i++ )                     /* 扫描所有按键  */
  131.     {
  132.         *pPORTx &= ~KEY_SCAN_PINS_SET;
  133.         NOP();
  134.         NOP();
  135.         *pPORTx |= 1 << i;
  136.         NOP();
  137.         NOP();
  138.         for ( j = 0; j < 2; j++ )
  139.         {
  140.             if( ( *pPORTx & ( 0x80 >> j ) ) == 0 )
  141.             {
  142.                     idx = KeyID_Table[i][j];
  143.                     pKeyID[idx] = KeyID_Table[i][j]; /* 按键ID按0-N的顺序存入 数组     */
  144.             }
  145.         }
  146.     }      
  147. }
  148. /**************************************************************************************************************
  149. * @brief  单个按键扫描
  150. *         完成按键去抖,获取按键“按下”“弹起”“长按”三个键值,并将键值压入队列。
  151. * @Param  idx:按键索引,表示扫描哪一个按键
  152. *         KeyID:
  153. **************************************************************************************************************/
  154. static void GetKeyValue( UINT8 idx, UINT8 keyID )
  155. {
  156.     KeyVar_TypeDef  *pKeyVar;
  157.     KeyElem_TypeDef const *pKeyElem;
  158.    
  159.     pKeyVar  = &KeyVar[idx];
  160.     pKeyElem = &KeyElem[idx];
  161.    
  162.     if( keyID != 255 )
  163.     {   
  164.         if( pKeyVar->FiltCnt < pKeyElem->FiltTime )
  165.         {
  166.             pKeyVar->FiltCnt = KeyElem->FiltTime;
  167.         }
  168.         if( pKeyVar->FiltCnt < 2 * KeyElem->FiltTime )
  169.         {
  170.             pKeyVar->FiltCnt++;
  171.         }
  172.         else
  173.         {
  174.             /* 如果长按时间设置大于0,说明是长按键  */
  175.             if( pKeyElem->LongTime > 0 )
  176.             {
  177.                 if( pKeyVar->LongCnt < pKeyElem->LongTime )
  178.                 {
  179.                     if( ++pKeyVar->LongCnt == pKeyElem->LongTime )
  180.                     {
  181.                         pKeyVar->KeyState = LONG_DOWN;
  182.                         Key_QueueIn( pKeyElem->KeyVal + 2 );    /* 获取当前按键“长按”键值,一次按下只获取一次  */
  183.                     }
  184.                 }
  185.                 else
  186.                 {   
  187.                     /* 如果键值重复生效时间设置大于0,说明需要重复获取键值  */
  188.                     if( pKeyElem->ReentryTime > 0 )
  189.                     {
  190.                         if( ++pKeyVar->ReentryCnt > pKeyElem->ReentryTime )
  191.                         {
  192.                             pKeyVar->ReentryCnt = 0;
  193.                             pKeyVar->KeyState = KEY_DOWN;
  194.                             Key_QueueIn( pKeyElem->KeyVal );   /* 长按键重复获取“按下”键值  */
  195.                         }
  196.                     }
  197.                     
  198.                 }
  199.             }
  200.             if( pKeyVar->KeyGet == FALSE )                    /* 非长按键一次按下只获取一次“按下”键值  */
  201.             {
  202.                 pKeyVar->KeyGet = TRUE;
  203.                 pKeyVar->KeyState = KEY_DOWN;
  204.                 Key_QueueIn( pKeyElem->KeyVal );               /* 当前按键“按下”键值压入按键队列  */
  205.             }
  206.         }
  207.     }
  208.     else/* 判断按键释放  */
  209.     {
  210.         if( pKeyVar->FiltCnt > KeyElem->FiltTime )
  211.         {
  212.             pKeyVar->FiltCnt = KeyElem->FiltTime;
  213.         }
  214.         if( pKeyVar->FiltCnt > 0 )
  215.         {
  216.             pKeyVar->FiltCnt--;
  217.         }
  218.         else
  219.         {
  220.             if( pKeyVar->KeyGet == TRUE )                    /* 当前按键“弹起”键值只获取一次     */
  221.             {
  222.                 pKeyVar->KeyGet = FALSE;
  223.                 Key_QueueIn( pKeyElem->KeyVal + 1 );          /* 当前按键“弹起”键值压入按键队列  */
  224.             }
  225.             pKeyVar->LongCnt = 0;
  226.             pKeyVar->ReentryCnt = 0;
  227.             pKeyVar->KeyState = KEY_UP;            
  228.         }
  229.     }
  230. }
  231. /**************************************************************************************************************
  232. * @brief  按键扫描函数
  233. *         矩阵式扫描,RB7,RB6 为扫描读取口;RB2,RB1,RB0反向后作为键盘的行扫描。
  234. * @note   RB2,RB1,RB0反向后也是数码管和 LED的位驱动口,因此键盘扫描前要先保存
  235. *         扫描端口的位值,扫描结束后还原。
  236. * @Param  *PORTx    键盘扫描所在端口   
  237. **************************************************************************************************************/
  238. void KeyScan( PORT_TypeDef *pPORTx )
  239. {
  240.     UINT8 i;
  241.     UINT8 PortKeep;

  242.     C595_Pin_OE = 1;                    /* 关闭 74ls595 三态缓冲输出  */
  243.     PortKeep = *pPORTx;                 /* 保存扫描端口  */   
  244.    
  245.     GetKeyID( pPORTx, KeyID );          /* 获取按键ID */
  246.    
  247.     *pPORTx = PortKeep;                 /* 还原 PORT 口  */
  248.     C595_Pin_OE = 0;                    /* 允许 74595 三态缓冲输出      */  
  249.    
  250.     // 组合键ID
  251.     if( ( KeyID[0] != 255 ) && ( KeyID[3] != 255 ) )
  252.     {
  253.         KeyID[6] = 6;
  254.         KeyID[3] = 255;
  255.     }
  256.     if( ( KeyID[0] != 255 ) && ( KeyID[4] != 255 ) )
  257.     {
  258.         KeyID[7] = 7;
  259.         KeyID[4] = 255;
  260.     }   
  261.     KeysState = 0;
  262.     for( i = 0; i < KEY_QTY; i++ )
  263.     {
  264.         GetKeyValue( i, KeyID[i] );
  265.         KeysState |= KeyVar[i].KeyState;
  266.     }
  267. }
  268. /*************************************************************************************************************
  269. * @brief  按键事件处理函数
  270. *         采用状态机判断键值,根据键值调用相应的按键事件函数,键值由按键队列弹出。
  271. *************************************************************************************************************/
  272. void KeyEventFunc( void )
  273. {
  274.     switch( Key_QueueOut( ) ){

  275.         case KEY_0_DOWN:
  276.             KeyElem[0].KeyEventFunc();
  277.             break;
  278.         case KEY_0_LONG:
  279.             Key_SetPoint = SET_SEAL_MAX;
  280.             DispBuffer[DPYCC_C1_TENS] = Segment[ ByteDiv( ParamBuf[P_SEAL_MAX_IDX], 10 ) ] | 0x80;
  281.             DispBuffer[DPYCC_C2_UNIT] = Segment[ ByteMod( ParamBuf[P_SEAL_MAX_IDX], 10 ) ];
  282.             BeepCtrl = SIGNAL;
  283.             break;
  284.         case KEY_0_UP:
  285.             if( Key_SetPoint == SET_SEAL_MAX )
  286.             {
  287.                 KeyMsg.EnterKey = YES;
  288.                 Key_SetPoint = NONE;
  289.             }
  290.             break;              
  291.         case KEY_1_DOWN:
  292.             KeyElem[1].KeyEventFunc();
  293.             break;
  294.         case KEY_2_DOWN:
  295.             KeyElem[2].KeyEventFunc();
  296.             break;
  297.         case KEY_3_DOWN:
  298.             KeyElem[3].KeyEventFunc();
  299.             break;
  300.         case KEY_4_DOWN:
  301.             KeyElem[4].KeyEventFunc();
  302.             break;
  303.         case KEY_5_DOWN:
  304.             KeyElem[5].KeyEventFunc();
  305.             break;
  306.         case KEY_6_DOWN:
  307.             KeyElem[6].KeyEventFunc();
  308.             break;
  309.         case KEY_7_DOWN:
  310.             KeyElem[7].KeyEventFunc();         
  311.             break;
  312.         default:
  313.             break;
  314.     }
  315. }


 楼主| ailingg 发表于 2017-12-11 10:09 | 显示全部楼层
本帖最后由 ailingg 于 2017-12-11 11:23 编辑
  1. #ifndef __bsp_Key_H__
  2. #define __bsp_Key_H__

  3. #include<pic.h>
  4. #include"genericTypeDef.h"



  5. typedef volatile unsigned char PORT_TypeDef;




  6. #define LID_PRESS_ERR            1
  7. // Logic Switch non
  8. #define NEVER                    0
  9. #define ALREADY                  1

  10. // Logic Switch
  11. #define YES                      1
  12. #define NO                       0
  13. #define ENABLE                   1
  14. #define DISABLE                  0
  15. // Key state
  16. #define KEY_UP                   0
  17. #define KEY_DOWN                 1
  18. #define LONG_DOWN                2
  19. // Beep action
  20. #define ALARM                    3  
  21. #define SIGNAL                   4

  22. // Parameter Set Key
  23. #define SET_VACUUM_TIME          5
  24. #define SET_SEAL_TIME            6
  25. #define SET_SEAL_MAX             7
  26. #define NONE                     0


  27. #define KEY_QTY                  8

  28. // Key pins position,                           // PORTB : I I x x  x O O O
  29. #define KEY_SCAN_PINS_SET        0x07           // 因为 RB2 ~ RB0 经过UNL2003反向后作为键盘的行扫描,所以判断是否有按键按下是将所有扫描引脚置高
  30. #define KEY_PORT_DIR             TRISB
  31. #define KEY_PORT                 PORTB

  32. #define GPIO_PIN_KeyScan0        PORTBbits.RB0  // 数码管与按键共用扫描口,在 bsp_LED.h
  33. #define GPIO_PIN_KeyScan1        PORTBbits.RB1
  34. #define GPIO_PIN_KeyScan2        PORTBbits.RB2

  35. /* 按键循环队列结构 */
  36. typedef volatile unsigned char QelemType;
  37. typedef volatile unsigned char QelemInxType;   
  38.    
  39. typedef struct{
  40.    
  41.     QelemType *Buf;
  42.     QelemType rear;
  43.     QelemType front;
  44.     QelemType Lock;
  45.    
  46. }KeyQueue_TypeDef;

  47. extern KeyQueue_TypeDef KeyQueue;

  48. /* 按键模式 */
  49. typedef struct
  50. {
  51.     unsigned bMode    :1;
  52.     unsigned bToggle  :1;
  53.     UINT16   ToggleCnt;
  54.    
  55. }KeyMode_TypeDef;

  56. extern KeyMode_TypeDef KeyMode;

  57. /** 按键消息变量 */
  58. typedef struct{
  59.    
  60.     unsigned Emergency     :1;
  61.     unsigned DataRenew     :1;
  62.     unsigned EnterKey      :1;
  63.     unsigned bSync         :1;
  64.    
  65.     UINT8    Command;
  66.    
  67. }KeyMsg_TypeDef;

  68. extern KeyMsg_TypeDef KeyMsg;


  69. /** 按键扫描变量 */
  70. typedef struct
  71. {
  72.     unsigned KeyGet   :1;  
  73.     unsigned KeyState :2;
  74.     unsigned FuncID   :4;
  75.    
  76.     UINT8    FiltCnt;
  77.     UINT8    ReentryCnt;
  78.     UINT16   LongCnt;
  79.    
  80. }KeyVar_TypeDef;

  81. extern persistent KeyVar_TypeDef KeyVar[KEY_QTY];



  82. extern persistent UINT8 KeysState;
  83. extern persistent UINT8 Key_SetPoint;

  84. /*
  85.         定义键值代码, 必须按如下次序定时每个键的按下、弹起和长按事件
  86.         推荐使用enum, 不用#define,原因:
  87.         (1) 便于新增键值,方便调整顺序,使代码看起来舒服点
  88.         (2) 编译器可帮我们避免键值重复。
  89. */
  90. typedef enum
  91. {
  92.         KEY_NONE = 0,                        /* 0 表示按键事件 */

  93.         KEY_0_DOWN,                                /* 1键按下 */
  94.         KEY_0_UP,                                /* 1键弹起 */
  95.         KEY_0_LONG,                                /* 1键长按 */

  96.         KEY_1_DOWN,                                /* 2键按下 */
  97.         KEY_1_UP,                                /* 2键弹起 */
  98.         KEY_1_LONG,                                /* 2键长按 */

  99.         KEY_2_DOWN,                                /* 3键按下 */
  100.         KEY_2_UP,                                /* 3键弹起 */
  101.         KEY_2_LONG,                                /* 3键长按 */

  102.         KEY_3_DOWN,                                /* 4键按下 */
  103.         KEY_3_UP,                                /* 4键弹起 */
  104.         KEY_3_LONG,                                /* 4键长按 */

  105.         KEY_4_DOWN,                                /* 5键按下 */
  106.         KEY_4_UP,                                /* 5键弹起 */
  107.         KEY_4_LONG,                                /* 5键长按 */

  108.         KEY_5_DOWN,                                /* 6键按下 */
  109.         KEY_5_UP,                                /* 6键弹起 */
  110.         KEY_5_LONG,                                /* 6键长按 */

  111.         /* 组合键 */
  112.         KEY_6_DOWN,                                /* 7键按下 */
  113.         KEY_6_UP,                                /* 7键弹起 */
  114.         KEY_6_LONG,                                /* 7键长按 */

  115.         KEY_7_DOWN,                            /* 8键按下 */
  116.         KEY_7_UP,                                /* 8键弹起 */
  117.         KEY_7_LONG,                            /* 8键长按 */
  118. }KEY_ENUM;

  119. // 函数声明,其他文件条用
  120. extern void Key_Init( void );
  121. extern void KeyScan( PORT_TypeDef *PORTx );
  122. extern void KeyEventFunc(void);
  123. extern void BoardOutputKill( PORT_TypeDef *PORTx );

  124. // 函数声明,本文件内调用
  125. //extern BOOL IsKey_1( void );
  126. //extern BOOL IsKey_2( void );
  127. //extern BOOL IsKey_3( void );
  128. //extern BOOL IsKey_4( void );
  129. //extern BOOL IsKey_5( void );
  130. //extern BOOL IsKey_6( void );
  131. //extern BOOL IsKey_7( void );
  132. //extern BOOL IsKey_8( void );

  133. extern void Key_VacuumTimeSet(void);
  134. extern void Key_SealTimeSet(void);
  135. extern void Key_DigitDec(void);
  136. extern void Key_DigitInc(void);
  137. extern void Key_HeatLevelSet(void);
  138. extern void Key_StopKey(void);
  139. extern void Key_SealTimeMaxInc( void );
  140. extern void Key_SealTimeMaxDec( void );

  141. #endif

zqx1000 发表于 2018-4-10 23:37 | 显示全部楼层
kankan
tlled 发表于 2018-4-11 08:40 | 显示全部楼层
有对应的硬件部分电路吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

18

主题

167

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部

18

主题

167

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部