写过一段时间单片机程序,也看了一些单片机程序,书上的,网上的,基本都是非常原始的读取按键,延时判断,这些按键相关代码穿插于整个程序,着实不够美观,最重要的是不能复用,so俺就想做个按键框架,只需提供按键处理代码即可,言归正传,先简介一下几个文件,ringfifo.c提供了一个简单的环形缓冲队列,用以通知按键消息,key.c 实现的按键的框架处理(读取按键,调用按键处理函数),key_port.c 需提供底层硬件接口及用户的按键处理函数,目前支持的按键消息类型有4类:keyDown,keyRelease,longPress, double click.
key.h中提供了4个函数:InitKey()为初始化, KeyProc() 须在主循环中调用,KeyScan()须定时(10-20ms)调用,SetKeyHandler()用来设置按键处理函数
1 #ifndef __KEY_H__ 2 #define __KEY_H__ 3 4 #define NULL 0 5 6 typedef enum{ 7 KEY_EVENT_DOWN = 0, 8 KEY_EVENT_UP, 9 KEY_EVENT_LONGPRESS, 10 KEY_EVENT_DOUBLECLICK, 11 KEY_EVENT_COMBO_KEYDOWN, 12 }KeyEventType; 13 14 typedef void (*pKeyEventCB)(void *para); 15 16 typedef struct _tagKeyInfo 17 { 18 unsigned char keyCode; 19 unsigned char keyStatus; 20 unsigned char comboKeyFlag; 21 unsigned int keyDbClickTime; 22 unsigned int keyDownTime; 23 unsigned int keyRepeatTime; 24 pKeyEventCB FuncCB[5]; 25 void * para[5]; 26 }KeyInfo; 27 28 typedef struct _tagMessageInfo 29 { 30 unsigned int keyEvent; 31 unsigned int keyCode; 32 KeyInfo* pContext; 33 }MessageInfo; 34 35 36 37 void InitKey(void); // 38 39 void KeyProc(void);// call this func in main loop 40 41 void KeyScan(void); // call it in timer service, 20ms 42 43 pKeyEventCB SetKeyHandler(unsigned char key,KeyEventType event,pKeyEventCB pFunc,void *para); 44 45 #endif
1 #include "key_port.h" 2 #include "key.h" 3 #include "ringfifo.h" 4 #include <string.h> 5 6 #define KEY_STATE_UP 0X0 7 #define KEY_STATE_DOWN 0X3 8 #define KEY_STATE_LONGRESS 0XF 9 #define KEY_STATE_RELEASE 0XC 10 11 static KeyInfo kInfo[KEY_AMOUNT]; 12 static RingFifo MsgQ; 13 14 15 void PostMessage(MessageInfo* pMsg) 16 { 17 PushFifo(&MsgQ,pMsg); 18 } 19 20 unsigned char GetMessage(MessageInfo*pMsg) 21 { 22 return PopFifo(&MsgQ,pMsg); 23 } 24 25 // call it in timer service 26 void KeyScan(void) 27 { 28 int i = 0; 29 unsigned char c = 0; 30 KeyInfo * pInfo = kInfo; 31 MessageInfo Msg; 32 for(i=0;i<KEY_AMOUNT;++i) 33 { 34 Msg.pContext = pInfo; 35 Msg.keyCode = pInfo->keyCode; 36 c = ReadKeyStatus(Msg.keyCode); // 37 pInfo->keyStatus |= c; 38 pInfo->keyStatus &= 0xF; 39 switch(pInfo->keyStatus) 40 { 41 case KEY_STATE_UP: 42 pInfo->keyDownTime = 0; 43 break; 44 case KEY_STATE_DOWN: 45 Msg.keyEvent = KEY_EVENT_DOWN; 46 PostMessage(&Msg); // post event 47 break; 48 case KEY_STATE_LONGRESS: 49 if(pInfo->keyDownTime < KEY_LONGPRESS_TIME /POLLING_INTERVAL) 50 { 51 pInfo->keyDownTime++; 52 } 53 else 54 { 55 if(pInfo->keyRepeatTime < KEY_LONGPRESS_REPEAT_INTERVAL/POLLING_INTERVAL) 56 { 57 ++pInfo->keyRepeatTime; 58 } 59 else 60 { 61 pInfo->keyRepeatTime = 0; 62 Msg.keyEvent = KEY_EVENT_LONGPRESS; 63 PostMessage(&Msg); // post event 64 } 65 } 66 break; 67 case KEY_STATE_RELEASE: 68 Msg.keyEvent = KEY_EVENT_UP; 69 PostMessage(&Msg); // post event 70 if(pInfo->keyDbClickTime) //double click 71 { 72 Msg.keyEvent = KEY_EVENT_DOUBLECLICK; 73 PostMessage(&Msg); // post event 74 } 75 pInfo->keyDbClickTime = KEY_DBCLICK_TIME /POLLING_INTERVAL; 76 break; 77 } 78 if(pInfo->keyDbClickTime)pInfo->keyDbClickTime--; 79 pInfo->keyStatus <<= 1; 80 ++pInfo; 81 } 82 83 } 84 85 86 87 // call this func in main() 88 void KeyProc(void) 89 { 90 KeyInfo * pInfo = 0; 91 int k = 10; 92 MessageInfo msg; 93 while(GetMessage(&msg) &&(k--)) 94 { 95 pInfo = msg.pContext; 96 if(pInfo->FuncCB[msg.keyEvent]) 97 { 98 pInfo->FuncCB[msg.keyEvent](pInfo->para[msg.keyEvent]); 99 } 100 } 101 } 102 103 pKeyEventCB SetKeyHandler(unsigned char key,KeyEventType event,pKeyEventCB pFunc,void *para) 104 { 105 pKeyEventCB pf = NULL; 106 int i = 0; 107 for(i = 0;i<KEY_AMOUNT;++i) 108 { 109 if(key == kInfo[i].keyCode) 110 { 111 pf = kInfo[i].FuncCB[event]; 112 kInfo[i].FuncCB[event] = pFunc; 113 kInfo[i].para[event] = para; 114 break; 115 } 116 } 117 return pf; 118 } 119 120 void InitKey(void) 121 { 122 int i = 0; 123 InitFifo(&MsgQ); 124 memset(kInfo,0,sizeof(kInfo)); 125 for(i = 0;i<KEY_AMOUNT;++i) 126 { 127 kInfo[i].keyCode = KeyCodeTable[i]; 128 } 129 130 RegisterKeyHandler(); 131 }
1 #ifndef __RINGFIFO_H__ 2 #define __RINGFIFO_H__ 3 4 #include "key.h" 5 6 #define FIFO_SIZE 10 7 8 typedef MessageInfo ElemDataType; 9 10 typedef struct _tagFifo 11 { 12 ElemDataType data[FIFO_SIZE]; // message 13 unsigned char read; // 14 unsigned char write; // 15 }RingFifo; 16 17 void InitFifo(RingFifo * pFifo); 18 unsigned char PopFifo(RingFifo * pFifo,ElemDataType * pMsg); 19 unsigned char PushFifo(RingFifo * pFifo,ElemDataType * pMsg); 20 unsigned char PeekFifo(RingFifo * pFifo,ElemDataType * pMsg); 21 22 #endif
1 #include "RingFifo.h" 2 #include <string.h> 3 //#include <assert.h> 4 5 #define assert(x) 6 7 void InitFifo(RingFifo * pFifo) 8 { 9 assert(pFifo); 10 memset(pFifo,0,sizeof(RingFifo)); 11 } 12 13 unsigned char PopFifo(RingFifo * pFifo,ElemDataType * pMsg) 14 { 15 assert(pFifo&&pMsg); 16 if(pFifo->read == pFifo->write) 17 { 18 return 0; 19 } 20 else 21 { 22 memcpy(pMsg,pFifo->data+pFifo->read,sizeof(ElemDataType)); 23 pFifo->read = (pFifo->read+1)%FIFO_SIZE; 24 return 1; 25 } 26 27 } 28 29 unsigned char PushFifo(RingFifo * pFifo,ElemDataType * pMsg) 30 { 31 assert(pFifo&&pMsg); 32 memcpy(pFifo->data+pFifo->write,pMsg,sizeof(ElemDataType)); 33 pFifo->write = (pFifo->write + 1) % FIFO_SIZE; 34 return 1; 35 } 36 37 unsigned char PeekFifo(RingFifo * pFifo,ElemDataType * pMsg) 38 { 39 assert(pFifo&&pMsg); 40 if(pFifo->read == pFifo->write) 41 { 42 return 0; 43 } 44 else 45 { 46 memcpy(pMsg,&pFifo->data[pFifo->read],sizeof(ElemDataType)); 47 return 1; 48 } 49 }
key_port.h中, KEY_AMOUNT代表能识别的按键个数, 用户需实现ReadKeyStatus(),RegisterKeyHandler()这2个函数,其中
ReadKeyStatus()用来读取硬件按键状态,具体可参见key_port.c示例
RegisterKeyHandler()用来设置按键处理函数。
1 #ifndef __KEY_PORT_H__ 2 #define __KEY_PORT_H__ 3 4 #define KEY_AMOUNT 6 5 6 #define KEY_LONGPRESS_TIME 3000 // 3S 7 #define KEY_DBCLICK_TIME 500 8 #define POLLING_INTERVAL 20 //timer interval 9 #define KEY_LONGPRESS_REPEAT_INTERVAL 500 10 extern unsigned char KeyCodeTable[KEY_AMOUNT]; 11 unsigned char ReadKeyStatus(unsigned char key); 12 void RegisterKeyHandler(void); 13 14 #endif
1 #include "key_port.h" 2 #include "key.h" 3 #include "stm32f10x.h" 4 #include "timer.h" 5 #include "stepper.h" 6 7 #define KEY_1 1 8 #define KEY_2 2 9 #define KEY_3 3 10 #define KEY_4 4 11 #define KEY_5 5 12 #define KEY_6 6 13 14 unsigned char KeyCodeTable[KEY_AMOUNT]= 15 { 16 KEY_1, 17 KEY_2, 18 KEY_3, 19 KEY_4, 20 KEY_5, 21 KEY_6 22 }; 23 24 void startStateMachine(void); 25 void key1handler(void *p) // 26 { 27 startStateMachine(); 28 } 29 30 void key2handler(void *p) //reset 31 { 32 33 } 34 35 36 void key3handler(void *p) // reserve 37 { 38 39 40 } 41 42 void key3handler_up(void *p) // 43 { 44 45 } 46 47 void key4handler(void *p) 48 { 49 50 } 51 52 void key5handler(void *p) 53 { 54 55 } 56 57 void key6handler(void *p) 58 { 59 60 } 61 62 // get key status,return 1 if key down , 63 unsigned char ReadKeyStatus(unsigned char key) 64 { 65 unsigned char c = 0; 66 switch(key) 67 { 68 case KEY_1: 69 c = !GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_5); 70 break; 71 case KEY_2: 72 c = !GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_15); 73 break; 74 case KEY_3: 75 c = GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_4); 76 break; 77 case KEY_4: 78 79 c = 0; 80 break; 81 case KEY_5: 82 83 c= 0; 84 break; 85 case KEY_6: 86 c= 0; 87 break; 88 } 89 return c; 90 } 91 92 void RegisterKeyHandler(void) 93 { 94 // Add you key event func here 95 SetKeyHandler(KEY_1,KEY_EVENT_DOWN,key1handler ,0); 96 SetKeyHandler(KEY_2,KEY_EVENT_DOWN,key2handler ,0); 97 SetKeyHandler(KEY_3,KEY_EVENT_DOWN,key3handler ,0); 98 SetKeyHandler(KEY_3,KEY_EVENT_UP,key3handler_up ,0); 99 SetKeyHandler(KEY_4,KEY_EVENT_DOWN,key4handler ,0); 100 SetKeyHandler(KEY_5,KEY_EVENT_DOWN,key5handler ,0); 101 SetKeyHandler(KEY_6,KEY_EVENT_UP,key6handler ,0); 102 }
在key_port.c中,提供了一个示例,分别设置了3个按键key_1,key_2,key_3的keydown处理函数,同时设置了key_3的keyUp处理函数。