按键事件
来源:互联网 发布:上海 儿童编程 编辑:程序博客网 时间:2024/05/16 09:13
单片机开发时,经常使用按键去执行一些操作,作者写了一个简单的事件驱动函数,来执行按键任务。
1.获取按键状态
按键的状态可以有多种,短按,长按,双击,或者组合键之类。基于按键的状态机算法即可获取这些状态,这里只是简单的实现了长按和短按的状态识别。
static u8 Key_Read(void){ if( (KEY_UP == 1 || KEY_DOWN == 0 || KEY_LEFT == 0 || KEY_RIGHT == 0)) { //延时消抖 delay_ms(10); if(KEY_UP == 1) return KEY_UP_PRES; else if(KEY_DOWN == 0) return KEY_DOWN_PRES; else if(KEY_LEFT == 0) return KEY_LEFT_PRES; else if(KEY_RIGHT == 0) return KEY_RIGHT_PRES; } return KEY_ALL_OFF;}
这段代码就是读取一次按键值,并进行简单的延时消抖。
#define KEY_LONG_CNT 10static u8 getKeyState(void){ static uint8_t cnt = 0,read_key_state[KEY_LONG_CNT]; uint8_t key_state = 0 , i , key_cnt = 0; key_state = Key_Read(); if((key_state!=KEY_ALL_OFF)) //有按键按下 开始记录按键 和 计数 { read_key_state[cnt++] = key_state; } else if((key_state == KEY_ALL_OFF) && (cnt != 0)) //在未达到计数次数前按键被松开 则为短按 { cnt = 0; key_state = read_key_state[0]; for( i=0 ; i<KEY_LONG_CNT ; i++ ) { read_key_state[i] = 0; } return key_state; } if(cnt == KEY_LONG_CNT ) //如果到了长按的计数次数 进行判断为哪个按键的长按 { key_state = read_key_state[0]; for( i=1 ; i<KEY_LONG_CNT ; i++) { if(key_state == read_key_state[i]) { key_cnt++; read_key_state[i] = 0; } } cnt = 0; if(key_cnt > KEY_LONG_CNT -2 ) //最大扫描次数里有 KEY_LONG_CNT -2 次 跟初始按下情况一样 则为此按键的长按 { while((KEY_UP == 1 || KEY_DOWN == 0 || KEY_LEFT == 0 || KEY_RIGHT == 0)) //等待此按键松开 ; if(key_state == KEY_UP_PRES) { return KEY_UP_LONG_PRES; } else if(key_state == KEY_DOWN_PRES) { return KEY_DOWN_LONG_PRES; } else if(key_state == KEY_LEFT_PRES) { return KEY_LEFT_LONG_PRES; } else if(key_state == KEY_RIGHT_PRES) { return KEY_RIGHT_LONG_PRES; } } else { return key_state; } } return KEY_ALL_OFF;}
记录每次按键的状态,然后计算时间,有大于KEY_LONG_CNT -2 次的按键状态一致,即为按键的长按,否则为短按,最后解析出按键的状态,某个按键和按键的状态(长按还是短按).
此时就已经获取到了按键的状态,不过我们再对这个函数进行一层封装。
static keyEventEnum getEnum(){ KEY_State = getKeyState(); switch(KEY_State) { case KEY_UP_PRES : return UP; case KEY_DOWN_PRES : return DOWN; case KEY_LEFT_PRES : return LEFT; case KEY_ALL_OFF : return NONE; case KEY_RIGHT_PRES : return RIGHT; case KEY_UP_LONG_PRES : return LONG_UP; case KEY_LEFT_LONG_PRES : return LONG_LEFT; case KEY_DOWN_LONG_PRES : return LONG_DOWN; case KEY_RIGHT_LONG_PRES : return LONG_RIGHT; default : return NONE; }}
根据按键状态返回一个枚举类型的变量
2 构造事件驱动
#define KEY_EVENT_NAME_LENGTH 15 //事件名称最大长度typedef struct keyEventType{ struct keyEventType *next; void (*fun)(void *arg); //处理函数 void *arg; //参数 char name[KEY_EVENT_NAME_LENGTH]; //事件名称 keyEventEnum keyState; //事件}keyEventType;
以上述结构体构造一个事件处理链表,然后只要根据前文的getEnum和链表中的每个结构体的keystate进行比较,然后执行相应的事件即可。
首先先看初始化代码
//按键事件头指针keyEventType * currentKeyEvent;void keyInit(void){ /*省略按键io口配置*/ //申请动态内存空间,这个函数可以看我之前写的文章 currentKeyEvent = myMalloc(sizeof(keyEventType)); //指向空 currentKeyEvent->next = NULL;}
初始化很简单,为根指针分配空间,然后指向空
下面为添加事件代码
//一个按键状态只支持一个事件#define KEY_UNIQUE_EVENT 0uint8_t addKeyEvent(char const *name,keyEventEnum state,void (*fun)(void *arg),void *arg){ keyEventType *newKeyEvent = NULL; keyEventType *q = currentKeyEvent; #if KEY_UNIQUE_EVENT == 1 while(q->next != NULL) { //已经存在此按键状态的事件 if(q->next->keyState == state) { return False; } q = q->next; } q = currentKeyEvent; #endif newKeyEvent = myMalloc(sizeof(keyEventType)); if(newKeyEvent == NULL) { return False; } newKeyEvent->arg = arg; newKeyEvent->fun = fun; newKeyEvent->keyState = state; newKeyEvent->next = NULL; strcpy(newKeyEvent->name,name); //添加新的按键事件 while(q->next != NULL) { q = q->next; } q->next = newKeyEvent; return True;}
- 首先可以看到一个预编译的代码段,这个宏定义的含义是一个按键状态是否只支持一种状态,如果是,则要对链表进行一个遍历,如果已经存在此按键状态的事件,则返回,事件添加失败。
- 创建一个新的事件结构体,为其分配内存。然后把各个参数赋给它。
- 对链表进行遍历,将新的按键事件结构体添加到尾部。
有添加就有删除,接着看删除事件的代码。
#if KEY_UNIQUE_EVENT == 1u8 removeKeyEvent(keyEventEnum state){ keyEventType *q = currentKeyEvent; keyEventType *temp; while(q->next != NULL) { //删除事件,回收内存 if(q->next->keyState == state) { temp = q->next; q->next = q->next->next; myFree(temp); return True; } q = q->next; } return False;}#elseu8 removeKeyEvent(char const *name){ keyEventType *q = currentKeyEvent; keyEventType *temp; while(q->next != NULL) { //删除事件,回收内存 if(strcmp(q->next->name,(char*)name) == 0) { temp = q->next; q->next = q->next->next; myFree(temp); return True; } q = q->next; } return False;}#endif
删除事件的函数很简单,不过通过一个预编译命令写成了两个函数。如果宏定义为1,即一个按键状态只支持一种状态,那么就可以通过按键事件的状态去查找要删除的事件结构体。不然的话就只能通过按键事件名称去删除。
通过对链表遍历,找到对应的结构体,调整链表指向,回收内存。
最后来看事件的执行函数
void runKeyEvent(){ keyEventEnum state = getEnum(); keyEventType *q = currentKeyEvent; while(q->next != NULL) { if(q->next->keyState == state) { q->next->fun(q->next->arg); #if KEY_UNIQUE_EVENT == 1 break; #endif } q = q->next; }}
同样就是对链表进行遍历,然后判断是否触发了此按键事件,如果是,则执行相应的事件回调函数。
宏定义的判断是,如果一个按键状态只支持一种状态,那么执行了一次函数回调就可以返回了,因为后面不会再有与此状态对应的事件了。这个函数100ms调用一次就可以了。
至此完成了一个简单的按键事件驱动。
阅读全文
0 0
- 按键事件
- 按键事件
- 按键事件
- Javascript的按键事件
- js按键事件说明
- 按键事件的处理
- symbian 按键事件详解
- 鼠标按键事件基础
- Javascript 按键事件
- 按键事件的处理
- S60 发送按键事件
- Android 按键事件小结
- Android事件处理--按键
- Java按键事件KeyEvent
- Symbian学习--按键事件
- 按键事件处理
- WPF之按键事件
- js的按键事件
- 实现跨域访问需要的条件
- Java面试题全集(上)
- mysql 中limit动态传参
- Fragment的用法
- Java编程思想——操作符
- 按键事件
- 现有c++ 库
- 深入理解Spring系列之十一:SpringMVC-@RequestBody接收json数据报415
- 第一周总结
- 51nod 1266 蚂蚁 【思维题】
- 【bzoj 2662】冻结(还是分层图+spfa)
- 百度地图api开发时显示时只有白格子,没有地图的解决方法
- Loi模拟题之Loi水题汇总 MST+DP+DP+树剖
- c++模板 template