本帖最后由 armstrong 于 2012-5-26 15:43 编辑
在单片机开发中,由于资源受限而没有平台的支持,每次开发都要重写很多代码,应用的千奇百怪的需求更是加剧了这种困难。解决问题的办法是,总结常见的需求,分析它,得出即高效有通用的解决方案。 今天我就来为大家提供一种按键的解决方案,它易用,高效,节省资源! 先给出这个按键模块解决方案的全部代码,稍后再来分析。 keyif.h内容: 1: #ifndef__KEY_IF_H__ 2: #define__KEY_IF_H__ 3: //////////////////////////////////////////////////////////////////////////////// 4: typedefunsigned char u8_t; 5: 6: #defineKEY_NUM_MAX (8) 7: 8: #defineKEY_STA_BEGIN (0) 9: #defineKEY_STA_KEEP (1) 10: #defineKEY_STA_END (2) 11: 12: #defineKEY_PERIOD_MS (25) 13: 14: voidkif_Init(void); 15: voidkif_TickHook(void); 16: 17: /* 18: kindex 按键索引,从0开始,而小于KEY_NUM_MAX。 19: 返回值:按键被按下返回非零,按键被抬起返回零。 20: */ 21: externu8_t KeyRead(u8_t kindex); 22: /* 23: kindex 按键索引,从0开始,而小于KEY_NUM_MAX。 24: ksta 按键状态,取值为KEY_STA_系列宏。 25: ktick 按键计时,以KEY_PERIOD_MS时间为计数单位。 26: 返回值:如果本次按键操作已经处理妥当,就返回非零。 27: */ 28: externu8_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_tnow; 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 = 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 += 1; 68: kstats[grp] &= ~msk; 69: KeyProc(i, KEY_STA_END,kticks); 70: } 71: } 72: else if(now){ 73: // 按键保持按下状态。 74: kticks += 1; 75: if(kvalid[grp] & msk){ 76: // 按键处于有效状态。 77: if(KeyProc(i, KEY_STA_KEEP, kticks)){ 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_tktick) 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_tkindex); u8_t KeyProc(u8_tkindex, 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()函数。
|