嵌入式 > 技术百科 > 详情

单片机模块化一:按键思考

发布时间:2024-10-30 发布时间:
|

    从事单片机工作有几年了,一直想做一个系列总结,正好赶上今天下雨,俗话说:下雨天,宅家天。吃饱喝足,闲来无事,正好写篇博客,算是这个系列的开头第一篇,以后有时间就写点,这个“系列死了”也不奇怪。有不对的地方或者您有什么好的建议请留言,思想是碰撞的火花,请大家畅所欲言。

    按键是单片机系统中最常用的一个东东,简单人机交互界面基本都有按键存在。犹记得刚开始接触单片机时候,读取一个按键IO值,就当做键值来处理,发现一次按键经常识别为好几次,查阅资料才发现要“消抖”,于是加个delay_ms(5),连续读取几次,确定单次有效值。这就是后来不怎么跑多任务,不注重实时性时经常‘玩’的方法,如果你还在用,那么下面的文字对你有用,如果你已经知道了,那就请飞过吧,反正我也是随便写写,你也就随性看看。


下面就是一个很典型的例子:


  1. bool key_get_value(void)  

  2. {  

  3.             bool    bNewIoValue     =   0;  

  4.     static  bool    s_bOldIoValue   =   0;  

  5.     uint8_t i=0;  

  6.       

  7.     for(i=0;i<4;i++){  

  8.         bNewIoValue = io_read();  

  9.         if(s_bOldIoValue != bNewIoValue){  

  10.             i=0;  

  11.             s_bOldIoValue = bNewIoValue;  

  12.             continue;  

  13.         }  

  14.         delay_ms(5);  

  15.     }  

  16.     return s_bOldIoValue;  

  17. }  


我们来看看代码“阻塞在什么地方”?有代码分析可知,主要在delay_ms(5)这里,在这里通过“软件延时”来达到消除抖动的目的。那么我们换一种思路,假如我们的task执行到delay_ms(5)这里,便退出(让出运行权),同时启动一个“计数时钟”,当到5MS时候,就再起运行起来,这样就变为了“非阻塞”。为了达到这个目的,不管如何实现都需要一个“硬时基”,用于异步计数。

说下思路:就是tick时钟到,调用一次io_read(),并做消抖,然后获取一次有效按键值。好,到这里我们消除了delay_ms(5),那么我们继续思考,对于按键的值的“获得”我们可以说有“实时性要求”,例如:我按下按键,你要能够及时获得这个动作。但是按键值获得之后,按键的状态判断(down、short_press、long_press、repeat_press、up)相对来说可以暂缓,还有按键状态获得之后,其对应的处理函数又可分为紧急和非紧急两类(紧急类的先不讨论),非紧急类的又可以通过key_message_queue暂存按键消息,然后慢慢去执行消化。数据流程图如图所示:





这么处理的原因,是把有实时要求的任务和没有要求的任务分开,运用“生产者-消费者”模型。



思路介绍完了,下面说说具体模块:


注意:

1、最多255个按键;

2、每个按键都有按下、短按、长按、双击、抬起五个状态;

下载地址:http://download.csdn.net/download/wuhenyouyuyouyu/9952815

(下载分数限制,为什么不能设置为0分了?)


一、配置


public.h
宏KEY_MODE_SCHEDULER_IS_USER_CALL


1:按键的调度函数由用户调度;
0:按键的调度函数由系统自动调度;


宏KEY_NUM 配置按键个数

app_cfg.h


模块配置
#define KEY_ELIMINATE_JITTER_NUM            5       //按键消抖次数


#define KEY_MODE_QUEUE_LONG                 10      //key消息队列大小


#define KEY_NORMAL_VALUE                    1       //定义按键常态值


#define KEY_SCAN_CYCLE                      10      //键值扫描周期:单位ms


#define KEY_SHORT_PRESSED_TIMER             1000    //短按判定时间:单位ms


#define KEY_LONG_PRESSED_TIMER              3000    //长按判定时间:单位ms


#define KEY_REPEAT_TIMER                    500     //连按判定周期:单位ms


#define KEY_GET_VALUE_IS_INLINE_FUNCTION    0       //为inline函数还是callback函数


按键消息配置:为0,则不发送;为1,则发送
#define KEY_MODE_IS_ENABLE_MESSAGE_DOWN     1
#define KEY_MODE_IS_ENABLE_MESSAGE_SHORT    1
#define KEY_MODE_IS_ENABLE_MESSAGE_LONG     1
#define KEY_MODE_IS_ENABLE_MESSAGE_REPEAT   1
#define KEY_MODE_IS_ENABLE_MESSAGE_UP       1


加锁控制


......



二、用户实现函数
用户需提供按键扫描函数,原型bool    key_mode_get_key_value(uint8_t  chKeyID)


bool        按键状态
chKeyID     按键的ID


宏KEY_GET_VALUE_IS_INLINE_FUNCTION控制函数为inline类型,还是callback。


三、使用


int main(void)
{
    key_message_t   tKeyMessage;
    //初始化
    //关中断
    
    ......
    
    USER_KEY_MODE_INIT(NULL);//key_mode_get_key_value()为inline函数
    
    ......
    
    //开中断
    
    while(1){
        #if(KEY_MODE_SCHEDULER_IS_USER_CALL)
            USER_KEY_MODE_SCHEDULER();
        #endif


        if(key_mode_get_message(&tKeyMessage)){
            key_function[tKeyMessage.chKeyID](tKeyMessage.chKeyMessage);//按键处理
        }
    }
}


//心跳定时器
void    systick(void)
{
    ......
    USER_KEY_MODE_SCAN();
    ......
}


四、补充


#define __C99__         


#ifndef __C99__
    #define SAFE_ATOM_CODE(__CODE)     {\
            istate_t tState = GET_GLOBAL_INTERRUPT_STATE();\
            DISABLE_GLOBAL_INTERRUPT();\
            {\
                __CODE;\
            }\
            SET_GLOBAL_INTERRUPT_STATE(tState);\
        }
#else
    #define SAFE_ATOM_CODE(...)     {\
            istate_t tState = GET_GLOBAL_INTERRUPT_STATE();\
            DISABLE_GLOBAL_INTERRUPT();\
            {\
                __VA_ARGS__;\
            }\
            SET_GLOBAL_INTERRUPT_STATE(tState);\
        }


#endif


五、技术交流群号


如果有BUG请留言或者进入技术群:344659218


六、修改记录

2017.08.29

1、修复BUG,用下面的函数替换掉原来的


  1. /***************************************************************************** 

  2. * Function:         key_mode_scan_sub_state 

  3. * PreCondition:     None 

  4. * Input:            void 

  5. * Output:           void 

  6. * Side Effects:     None 

  7. * Overview:         KEY模块扫描子函数 

  8. * Note:             被key_mode_scheduler调用 

  9. *****************************************************************************/  

  10. static  fsm_rt_t    key_mode_judge_sub_state(key_mode_temp_t*  ptKeyStruct)  

  11. {  

  12.     key_mode_key_queue_t    *ptThis =   NULL;  

  13.   

  14.     #define     RESET_FMS_JUDGE_SBU(__ID)           {s_tKeyArray[__ID].tState    =   KEY_MODE_JUDGE_FSM_START;}  

  15.   

  16.     #define     KEY_MODE_POST_MESSAGE(__MESSAGE)    do{                                                         \  

  17.                                                         ptThis  =   malloc_key_queue();                         \  

  18.                                                         if(NULL != ptThis){                                     \  

  19.                                                             this.chKeyID        =   ptKeyStruct->chKeyID;       \  

  20.                                                             this.tKeyMessage    =   __MESSAGE;                  \  

  21.                                                             this.ptNext         =   NULL;                       \  

  22.                                                             if(NULL == s_ptKeyModeMessageQueueTail){            \  

  23.                                                                 s_ptKeyModeMessageQueueHead         =   ptThis; \  

  24.                                                             }else{                                              \  

  25.                                                                 s_ptKeyModeMessageQueueTail->ptNext =   ptThis; \  

  26.                                                             }                                                   \  

  27.                                                             s_ptKeyModeMessageQueueTail             =   ptThis; \  

  28.                                                         }                                                       \  

  29.                                                     }while(0)  

  30.       

  31.     switch(s_tKeyArray[ptKeyStruct->chKeyID].tState){  

  32.     case KEY_MODE_JUDGE_FSM_START:  

  33.         s_tKeyArray[ptKeyStruct->chKeyID].tState    =   KEY_MODE_JUDGE_FSM_DOWN;  

  34.         s_tKeyArray[ptKeyStruct->chKeyID].tCnt      =   0;  

  35.         //break;  

  36.     case KEY_MODE_JUDGE_FSM_DOWN:  

  37.         if(0 == s_tKeyArray[ptKeyStruct->chKeyID].tLastValue && 0 != ptKeyStruct->chKeyValue){  

  38.             s_tKeyArray[ptKeyStruct->chKeyID].tState    =   KEY_MODE_JUDGE_FSM_SHORT;  

  39.             s_tKeyArray[ptKeyStruct->chKeyID].tCnt++;  

  40.             #if(KEY_MODE_IS_ENABLE_MESSAGE_DOWN)  

  41.             KEY_MODE_POST_MESSAGE(KEY_MESSAGE_DOWN);  

  42.             #endif  

  43.         }  

  44.   

  45.         SAFE_ATOM_CODE(  

  46.             s_tKeyArray[ptKeyStruct->chKeyID].tLastValue = ptKeyStruct->chKeyValue;  

  47.         )  

  48.         break;  

  49.     case KEY_MODE_JUDGE_FSM_SHORT:  

  50.         if(0 != ptKeyStruct->chKeyValue){  

  51.             s_tKeyArray[ptKeyStruct->chKeyID].tCnt++;  

  52.             if(s_tKeyArray[ptKeyStruct->chKeyID].tCnt >= (KEY_SHORT_PRESSED_TIMER/(KEY_SCAN_CYCLE * KEY_ELIMINATE_JITTER_NUM * KEY_NUM))){  

  53.                 s_tKeyArray[ptKeyStruct->chKeyID].tState    =   KEY_MODE_JUDGE_FSM__LONGorREPEAT;  

  54.                 s_tKeyArray[ptKeyStruct->chKeyID].tCnt      =   0;             

  55.             }  

  56.         }else{  

  57.             #if(KEY_MODE_IS_ENABLE_MESSAGE_UP)  

  58.             KEY_MODE_POST_MESSAGE(KEY_MESSAGE_UP);  

  59.             #endif  

  60.             RESET_FMS_JUDGE_SBU(ptKeyStruct->chKeyID);  

  61.             SAFE_ATOM_CODE(  

  62.                 s_tKeyArray[ptKeyStruct->chKeyID].tLastValue = ptKeyStruct->chKeyValue;  

  63.             )  

  64.             break;  

  65.         }  

  66.   

  67.         break;  

  68.     case KEY_MODE_JUDGE_FSM__LONGorREPEAT:  

  69.         if(0 != ptKeyStruct->chKeyValue){  

  70.             s_tKeyArray[ptKeyStruct->chKeyID].tCnt++;  

  71.             if(s_tKeyArray[ptKeyStruct->chKeyID].tCnt >= (KEY_LONG_PRESSED_TIMER/(KEY_SCAN_CYCLE * KEY_ELIMINATE_JITTER_NUM * KEY_NUM))){  

  72.                 s_tKeyArray[ptKeyStruct->chKeyID].tState    =   KEY_MODE_JUDGE_FSM_UP;  

  73.                 #if(KEY_MODE_IS_ENABLE_MESSAGE_LONG)  

  74.                 KEY_MODE_POST_MESSAGE(KEY_MESSAGE_LONG);  

  75.                 #endif  

  76.             }  

  77.         }else{  

  78.             s_tKeyArray[ptKeyStruct->chKeyID].tState    =   KEY_MODE_JUDGE_FSM_REPEAT;  

  79.             s_tKeyArray[ptKeyStruct->chKeyID].tCnt      =   0;  

  80.             SAFE_ATOM_CODE(  

  81.                 s_tKeyArray[ptKeyStruct->chKeyID].tLastValue = ptKeyStruct->chKeyValue;  

  82.             )  

  83.             #if(KEY_MODE_IS_ENABLE_MESSAGE_UP)  

  84.             KEY_MODE_POST_MESSAGE(KEY_MESSAGE_UP);  

  85.             #endif  

  86.         }  

  87.         break;  

  88.     case KEY_MODE_JUDGE_FSM_REPEAT:  

  89.         if(0 == ptKeyStruct->chKeyValue){  

  90.             s_tKeyArray[ptKeyStruct->chKeyID].tCnt++;  

  91.             if(s_tKeyArray[ptKeyStruct->chKeyID].tCnt >= (KEY_REPEAT_TIMER/(KEY_SCAN_CYCLE * KEY_ELIMINATE_JITTER_NUM * KEY_NUM))){  

  92.                 #if(KEY_MODE_IS_ENABLE_MESSAGE_SHORT)  

  93.                 KEY_MODE_POST_MESSAGE(KEY_MESSAGE_SHORT);  

  94.                 #endif  

  95.                 RESET_FMS_JUDGE_SBU(ptKeyStruct->chKeyID);  

  96.                 //return fsm_rt_cpl;  

  97.             }  

  98.         }else{  

  99.             s_tKeyArray[ptKeyStruct->chKeyID].tState    =   KEY_MODE_JUDGE_FSM_UP;  

  100.             #if(KEY_MODE_IS_ENABLE_MESSAGE_REPEAT)  

  101.             KEY_MODE_POST_MESSAGE(KEY_MESSAGE_REPEAT);  

  102.             #endif             

  103.         }  

  104.         break;  

  105.     case KEY_MODE_JUDGE_FSM_UP:  

  106.         if(0 == ptKeyStruct->chKeyValue){  

  107.             #if(KEY_MODE_IS_ENABLE_MESSAGE_UP)  

  108.             KEY_MODE_POST_MESSAGE(KEY_MESSAGE_UP);  

  109.             #endif  

  110.             RESET_FMS_JUDGE_SBU(ptKeyStruct->chKeyID);  

  111.             SAFE_ATOM_CODE(  

  112.                 s_tKeyArray[ptKeyStruct->chKeyID].tLastValue = ptKeyStruct->chKeyValue;  

  113.             )  

  114.         }  

  115.         break;  

  116.     default:  

  117.         while(1);  

  118.   

  119.     }  

  120.     return  fsm_rt_on_going;  

  121. }  



  1.   
  2.   
  3.   
  4.   
  5.   
  6.   
  7.   
  8.   
  9.           

 


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
汽轮机旁路控制策略的研制与应用