查看: 3746|回复: 5

[原创] Z-Stack OSAL按键驱动流程详解及按键驱动修改

[复制链接]
  • TA的每日心情
    开心
    2016-11-18 11:38
  • 签到天数: 57 天

    连续签到: 1 天

    [LV.5]常住居民I

    发表于 2016-3-27 19:24:34 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 恶魔花花 于 2016-3-27 19:23 编辑

    最近一直在研究ZigBee,用的是TI的CC2530芯片,协议栈也是TI提供的Z-Stack协议栈,版本为:ZStack-CC2530-2.3.0-1.4.0,Z-Stack中有一个小型的虚拟操作系统OSAL,研究其中的按键驱动有一段时间了,现在将其流程和驱动修改的方法写出来,和大家交流一下。在学习按键流程的时候也参考了网上的一些博客,特此感谢。一下内容为原创,转载请联系作者。
    --------------------------------------------------------------------------------------------------------------------------------------------------
    从MAIN函数开始,涉及到按键初始化的地方有三处:
    1. int main( void )
    2. {
    3.   ……………………..
    4.   InitBoard( OB_COLD );
    5.   // Initialze HAL drivers
    6.   HalDriverInit();
    7. ………………………
    8.   // Final board initialization
    9.   InitBoard( OB_READY );
    10. } // main()
    复制代码
    第一次发生在调用InitBoard( OB_COLD );函数时,OB_COLE的定义为:#define OB_COLD  0
    InitBoard()函数的原型如下:
    1. void InitBoard( uint8 level )
    2. {
    3.   if ( level == OB_COLD )
    4.   {
    5.     // 关闭中断
    6.     osal_int_disable( INTS_ALL );
    7.     // 关闭所有的LED
    8.     HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );
    9.     // Check for Brown-Out reset
    10.     ChkReset();
    11.   }
    12.   else  // !OB_COLD
    13.   {
    14.     /* 初始化按键任务*/
    15.     OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
    16.     HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
    17.   }
    18. }
    复制代码
    可以看到if后的语句被第一次初始化调用,else后的语句被第三次初始化调用。
    第二次和按键初始化有关的语句为: HalDriverInit();函数原型如下:
    1. void HalDriverInit (void)
    2. {
    3. ……………………
    4.   /* KEY */
    5. #if (defined HAL_KEY) && (HAL_KEY == TRUE)
    6.   HalKeyInit();
    7. #endif
    8. ……………………..
    9. }
    复制代码
    在这里如果定义了HAL_KEY为真,则会调用按键初始化程序HalKeyInit();其函数原型如下:
    1. void HalKeyInit( void )
    2. {
    3.   /* Initialize previous key to 0 */
    4.   halKeySavedKeys = 0;

    5.   HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT);    /* 设置引脚功能为通用GPIO*/
    6.   HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT);    /* 设置引脚为输入模式 */

    7.   HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin function to GPIO */
    8.   HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin direction to Input */


    9.   /* 初始化按键回调函数 */
    10.   pHalKeyProcessFunction  = NULL;

    11.   /* Start with key is not configured */
    12.   /*初始化时按键未注册*/
    13.   HalKeyConfigured = FALSE;
    14. }
    复制代码
        可以看到这个函数基本完成了按键对应的相应引脚的配置,包括引脚的模式和输入输出方向,此外,函数初始化将按键回调函数设置为空,初始化时按键设置为未注册。

        第三次按键的初始化为InitBoard( OB_READY );它执行了这两条语句:
        OnboardKeyIntEnable= HAL_KEY_INTERRUPT_DISABLE;
        HalKeyConfig(OnboardKeyIntEnable, OnBoard_KeyCallback);
        可以看出初始化时按键被设置为用轮询的方式的检测。
        在此还调用了HalKeyConfig函数,原型如下:
    1. void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)
    2. {
    3.   /* 使能或者关闭中断使能*/
    4.   Hal_KeyIntEnable = interruptEnable;
    5.   /* 注册回调函数*/
    6.   pHalKeyProcessFunction = cback;
    7.   /* 确定中断是否启用 */
    8.   if (Hal_KeyIntEnable)//如果按键中断被使能才会执行下面的语句
    9.   {
    10.     /* 设置上升或者下降沿来触发中断*/
    11.     PICTL &= ~(HAL_KEY_SW_6_EDGEBIT);    /* Clear the edge bit */
    12.     /* For falling edge, the bit must be set. */
    13.   #if (HAL_KEY_SW_6_EDGE == HAL_KEY_FALLING_EDGE)
    14.     PICTL |= HAL_KEY_SW_6_EDGEBIT;
    15.   #endif


    16.     /* Interrupt configuration:
    17.      * - 使能端口中断
    18.      * - CPU中断使能
    19.      * - 清除中断
    20.      */
    21.     HAL_KEY_SW_6_ICTL |= HAL_KEY_SW_6_ICTLBIT;
    22.     HAL_KEY_SW_6_IEN |= HAL_KEY_SW_6_IENBIT;
    23.     HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT);
    24.     /* Rising/Falling edge configuratinn */

    25.     HAL_KEY_JOY_MOVE_ICTL &= ~(HAL_KEY_JOY_MOVE_EDGEBIT);    /* Clear the edge bit */
    26.     /* For falling edge, the bit must be set. */
    27.   #if (HAL_KEY_JOY_MOVE_EDGE == HAL_KEY_FALLING_EDGE)
    28.     HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_EDGEBIT;
    29.   #endif


    30.     /* Interrupt configuration:
    31.      * - Enable interrupt generation at the port
    32.      * - Enable CPU interrupt
    33.      * - Clear any pending interrupt
    34.      */
    35.     HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_ICTLBIT;
    36.     HAL_KEY_JOY_MOVE_IEN |= HAL_KEY_JOY_MOVE_IENBIT;
    37.     HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT);


    38.     /* Do this only after the hal_key is configured - to work with sleep stuff */
    39.     if (HalKeyConfigured == TRUE)
    40.     {
    41.       osal_stop_timerEx( Hal_TaskID, HAL_KEY_EVENT);  /* Cancel polling if active */
    42.     }
    43.   }
    44.   else    /* 中断未使能,使用轮询的方式来处理中断 */
    45.   {
    46.     HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don't generate interrupt */
    47.     HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT);   /* Clear interrupt enable bit */

    48.     osal_start_timerEx(Hal_TaskID,HAL_KEY_EVENT,HAL_KEY_POLLING_VALUE);    /* 使用定时器,每HAL_KEY_POLLING_VALUE 时间扫描一次按键*/
    49.   }

    50.   /*按键注册完成 */
    51.   HalKeyConfigured = TRUE;
    52. }
    复制代码
        Z-Stack中提供了两种方式来处理按键,中断和轮询。可以看出,在按键注册函数中判断决定了使用哪种方式来处理按键,如果使用中断方法,则需要在第三次按键初始化时传递一个参数来使能中断,即:
    else  // !OB_COLD
      {
        OnboardKeyIntEnable =HAL_KEY_INTERRUPT_ENABLE;
        HalKeyConfig(OnboardKeyIntEnable, OnBoard_KeyCallback);
      }
       同时也可以看出协议栈默认使用轮询法来处理按键。下面分别介绍两种按键处理方式的具体流程。
    一、中断法处理按键流程
        一旦按键触发中断,函数的执行会跳转到:
    1. HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )
    2. {
    3.   if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)
    4.   {
    5.     halProcessKeyInterrupt();
    6.   }

    7.   /*
    8. 清除端口0的中断
    9. PxIFG必须在PxIF之前被清除
    10.   */
    11.   HAL_KEY_SW_6_PXIFG = 0;
    12.   HAL_KEY_CPU_PORT_0_IF = 0;
    13. }
    复制代码
        在函数里如果有中断发生,就会调用函数:halProcessKeyInterrupt(),原型为:
    1. void halProcessKeyInterrupt (void)
    2. {
    3.   bool valid=FALSE;
    4.   if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)  /* Interrupt Flag has been set */
    5.   {
    6.     HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Clear Interrupt Flag */
    7.     valid = TRUE;
    8.   }

    9.   if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT)  /* Interrupt Flag has been set */
    10.   {
    11.     HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Clear Interrupt Flag */
    12.     valid = TRUE;
    13.   }
    14.   if (valid)
    15.   {
    16. <font color="#ff0000">  osal_start_timerEx(Hal_TaskID,HAL_KEY_EVENT,HAL_KEY_DEBOUNCE_VALUE);</font>
    17.   }
    18. }
    复制代码
        在这个函数中最主要的部分为上边红色的部分,HAL_KEY_DEBOUNCE_VALUE实现了软件的消抖,这条语句将会把按键事件传递给Hal任务,由Hal_ProcessEvent来处理,此函数在hal_driver.c中,函数原型如下:
    1. uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )
    2. {
    3.   uint8 *msgPtr;
    4.   (void)task_id;  // Intentionally unreferenced parameter

    5.   if ( events & SYS_EVENT_MSG )
    6.   {
    7.     msgPtr = osal_msg_receive(Hal_TaskID);

    8.     while (msgPtr)
    9.     { /* Do something here - for now, just deallocate the msg and move on */
    10.       /* De-allocate */
    11.       osal_msg_deallocate( msgPtr );
    12.       /* Next */
    13.       msgPtr = osal_msg_receive( Hal_TaskID );
    14.     }
    15.     return events ^ SYS_EVENT_MSG;
    16.   }
    17. <font color="#ff0000">  if (events & HAL_KEY_EVENT)
    18.   {

    19. #if (defined HAL_KEY) && (HAL_KEY == TRUE)
    20.     /* Check for keys */
    21.     HalKeyPoll();
    22. </font>
    23.     /* if interrupt disabled, do next polling */
    24.     if (!Hal_KeyIntEnable)
    25.     {
    26.       osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
    27.     }
    28. #endif // HAL_KEY

    29.     return events ^ HAL_KEY_EVENT;
    30.   }
    31. …………………
    32.   /* Nothing interested, discard the message */
    33.   return 0;

    34. }
    复制代码
        从上面红色的部分可以看出,如果有按键事件发生,将会调用HalKeyPoll()函数,此函数在hal_key.c中,函数原型如下:
    1. void HalKeyPoll (void)
    2. {
    3.   uint8 keys = 0;

    4.   if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT))  /* Key is active HIGH */
    5.   {
    6.     keys = halGetJoyKeyInput();
    7.   }

    8.   /* 如果中断没有被使能,将会比较按键先前的状态和现在的状态来判断一个按键是否被按下*/
    9.   if (!Hal_KeyIntEnable)//使用中断法时不经过这里
    10.   {
    11.     if (keys == halKeySavedKeys)
    12.     {
    13.       /* Exit - since no keys have changed */
    14.       return;
    15.     }
    16.     /* Store the current keys for comparation next time */
    17.     halKeySavedKeys = keys;
    18.   }
    19.   Else //使用中断法将会进入这里
    20.   {
    21.     /* 在这里添加中断处理*/
    22.   }

    23.   if (HAL_PUSH_BUTTON1())
    24.   {
    25.     keys |= HAL_KEY_SW_6;//在这里keys得到了按键的值
    26.   }

    27.   /* 如果有按键按下则调用回调函数*/
    28. <font color="#ff0000">  if (keys && (pHalKeyProcessFunction))
    29.   {
    30.     (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
    31.   }</font>
    32. }
    33. 上面所说的回调函数即OnBoard_KeyCallback,在OnBoard.c中,其函数原型如下:
    34. void OnBoard_KeyCallback ( uint8 keys, uint8 state )
    35. {
    36.   uint8 shift;
    37.   (void)state;

    38.   /* Get shift key status */
    39.   shift = ((keys & HAL_KEY_SW_6) ? true : false);

    40. <font color="#ff0000">  if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )</font>
    41.   {
    42.     // Process SW1 here
    43.     if ( keys & HAL_KEY_SW_1 )  // Switch 1
    44.     {
    45.     }
    46.     // Process SW2 here
    47.     if ( keys & HAL_KEY_SW_2 )  // Switch 2
    48.     {
    49.     }
    50.     // Process SW3 here
    51.     if ( keys & HAL_KEY_SW_3 )  // Switch 3
    52.     {
    53.     }
    54.     // Process SW4 here
    55.     if ( keys & HAL_KEY_SW_4 )  // Switch 4
    56.     {
    57.     }
    58.     // Process SW5 here
    59.     if ( keys & HAL_KEY_SW_5 )  // Switch 5
    60.     {
    61.     }
    62.     // Process SW6 here
    63.     if ( keys & HAL_KEY_SW_6 )  // Switch 6
    64.     {
    65.     }
    66.   }
    67. }
    复制代码
        注意上边红色的部分,在这里调用了一个函数OnBoard_SendKeys(),这个函数同样在此文件中,函数的原型如下;
    1. uint8 OnBoard_SendKeys( uint8 keys, uint8 state )
    2. {
    3.   keyChange_t *msgPtr;
    4. <font color="#ff0000">  if ( registeredKeysTaskID != NO_TASK_ID )//判断有没有注册事件</font>
    5.   {
    6.     // Send the address to the task
    7.     msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );
    8.     if ( msgPtr )
    9.     {
    10.       msgPtr->hdr.event = KEY_CHANGE;
    11.       msgPtr->state = state;
    12.       msgPtr->keys = keys;

    13.       osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );
    14.     }
    15.     return ( ZSuccess );
    16.   }
    17.   else
    18.     return ( ZFailure );
    19. }
    复制代码
        可以看一下函数前边的注释:
    Send"Key Pressed" message to application.
    //将按键按下的消息发送到应用层
    keys  - keys that were pressed
            state shifted
    //keys即被按下的按键,shifted为按键的状态
        可以看到原来它才是真正传递消息的函数,它将按键事件发送给任务KEY_CHANGE。上面的红色部分会事先判断事件有没有注册,如果事件未注册的话,在接下来也就不会产生KEY_CHANGE这个事件,那么按键事件是在哪里注册的呢?
        我们可以看一下GenericApp.c文件下的GenericApp_Init函数,里面有这样一个语句:RegisterForKeys( GenericApp_TaskID );它调用了OnBoard.c文件里的函数RegisterForKeys,其函数原型如下:
    1. uint8 RegisterForKeys( uint8 task_id )
    2. {
    3.   // Allow only the first task
    4.   if ( registeredKeysTaskID == NO_TASK_ID )
    5.   {
    6.     registeredKeysTaskID = task_id;//任务注册,和任务的ID关联
    7.     return ( true );
    8.   }
    9.   else
    10.     return ( false );
    11. }
    复制代码
        这样便完成了按键事件的注册。
        在OnBoard_SendKeys函数的开始keyChange_t *msgPtr 定义了一个结构体类型,Keychange_t的格式如下:
    1. typedef struct
    2. {
    3.   osal_event_hdr_t hdr;
    4.   uint8             state; // shift
    5.   uint8             keys;  // keys
    6. } keyChange_t;
    复制代码
        在OnBoard_SendKeys函数的最后调用了osal_msg_send函数,原型如下:
    1. uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
    2. {
    3.   if ( msg_ptr == NULL )
    4.     return ( INVALID_MSG_POINTER );

    5.   if ( destination_task >= tasksCnt )
    6.   {
    7.     osal_msg_deallocate( msg_ptr );
    8.     return ( INVALID_TASK );
    9.   }

    10.   // Check the message header
    11.   if ( OSAL_MSG_NEXT( msg_ptr ) != NULL ||
    12.        OSAL_MSG_ID( msg_ptr ) != TASK_NO_TASK )
    13.   {
    14.     osal_msg_deallocate( msg_ptr );
    15.     return ( INVALID_MSG_POINTER );
    16.   }

    17.   OSAL_MSG_ID( msg_ptr ) = destination_task;

    18.   // queue message
    19.   osal_msg_enqueue( &osal_qHead, msg_ptr );

    20.   // Signal the task that a message is waiting
    21. <font color="#ff0000">  osal_set_event( destination_task, SYS_EVENT_MSG );</font>

    22.   return ( SUCCESS );
    23. }
    复制代码
        在osal_set_event()函数中,设置了tasksEvents指针的值,然后在主循环中,不停的检测这个数组中的值,如果有变化,就调用任务号对应的事件处理函数,例如GenericApp_ProcessEvent函数。这就是整个按键中断一步步运行到用户定义的任务事件处理函数中的过程,任务的id号和事件是紧密联系在一起的。通过前面的注册函数RegisterForKeys(GenericApp_TaskID )。

        osal_set_event函数原型如下:
    1. byte osal_set_event( byte task_id, UINT16 event_flag )
    2. {
    3.   if ( task_id < tasksCnt )
    4.   {
    5.    halIntState_t   intState;
    6.     HAL_ENTER_CRITICAL_SECTION(intState);    // Hold off interrupts
    7.     tasksEvents[task_id] |= event_flag;  // Stuff the event bit(s)
    8.     HAL_EXIT_CRITICAL_SECTION(intState);     // Release interrupts
    9.   }
    10.    else
    11.     return ( INVALID_TASK );

    12.   return ( ZSUCCESS );
    13. }
    复制代码
    二、轮询法处理按键
        轮询法相对于中断法少了许多步骤,在HalKeyConfig函数的最后使用语句
    osal_start_timerEx(Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE)来实现了对按键的轮询,轮询的周期为HAL_KEY_POLLING_VALUE,默认定义是100,即每100毫秒对按键进行一次扫描,然后同中断法一样,这条语句将会把按键事件传递给Hal任务,由Hal_ProcessEvent来处理,其后的流程都和中断法类似,不再一一分析。

    三、轮询法处理按键驱动修改的方法
        引脚的对应关系为:SWITCH3在P0.4口。首先初始化部分不用改变,因为协议栈的默认配置就是使用轮询法来处理按键事件的,然后在hal_key.c中增加已下对HAL_KEY_SW_3的定义:
    1. *按键3被注册为P0_4 */
    2. #define HAL_KEY_SW_3_PORT   P0
    3. #define HAL_KEY_SW_3_BIT    BV(4)
    4. #define HAL_KEY_SW_3_SEL    P0SEL
    5. #define HAL_KEY_SW_3_DIR    P0DIR
    6. /*     SW3   edge interrupt    */

    7. #define HAL_KEY_SW_3_EDGEBIT  BV(4)//边缘掩码  
    8. #define HAL_KEY_SW_3_EDGE     HAL_KEY_FALLING_EDGE //按键6触发边缘选择
    9. /* SW_3 interrupts */
    10. #define HAL_KEY_SW_3_IEN      IEN1  /* CPU interrupt mask register P0中断使能*/
    11. #define HAL_KEY_SW_3_IENBIT   BV(5) /* Mask bit for all of Port_0 */
    12. #define HAL_KEY_SW_3_ICTL     P0IEN /* Port Interrupt Control register P0位使能*/
    13. #define HAL_KEY_SW_3_ICTLBIT  BV(4) /* P0IEN - P0.4 enable/disable bit */
    14. #define HAL_KEY_SW_3_PXIFG    P0IFG /* Interrupt flag at source P0中断标志*/

    15. /**          END             **/
    复制代码
        接着在HalKeyInit函数中屏蔽掉默认的SW6的引脚功能和方向设置,修改为SW3的引脚功能和方向设置,如下所示:
    1. /*
    2.   HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT);    // 设置引脚功能为通用//GPIO
    3.   HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT);    //设置引脚为输入模式
    4.   */
    5.   HAL_KEY_SW_3_SEL &= ~(HAL_KEY_SW_3_BIT);
    6.   HAL_KEY_SW_3_DIR &= ~(HAL_KEY_SW_3_BIT);
    复制代码
        HalKeyConfig函数中else中修改如下:
    1. else    /* Interrupts NOT enabled */
    2.   {
    3.     /*屏蔽掉默认的,配置SW3
    4.     HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); //don't generate interrupt
    5.     HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT);   // Clear interrupt enable bit
    6.     */
    7.     HAL_KEY_SW_3_ICTL &= ~(HAL_KEY_SW_3_ICTLBIT); //don't generate interrupt
    8.     HAL_KEY_SW_3_IEN &= ~(HAL_KEY_SW_3_IENBIT);   // Clear interrupt enable bit

    9.     osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE);    /* Kick off polling */
    10.   }
    复制代码
        然后在hal_board_cfg.h中添加如下的宏定义:
    1. /* S3*/
    2. #define PUSH3_BV          BV(4)
    3. #define PUSH3_SBIT        P0_4
    4. #define PUSH3_POLARITY    ACTIVE_LOW //我用的底板按键是低有效的
    复制代码
        再将Push Buttons的宏定义修改如下:
    1. /* ----------- Push Buttons ---------- */
    2. #define HAL_PUSH_BUTTON1()        (PUSH1_POLARITY (PUSH1_SBIT))
    3. #define HAL_PUSH_BUTTON2()        (PUSH2_POLARITY (PUSH2_SBIT))
    4. #define HAL_PUSH_BUTTON3()        (PUSH3_POLARITY (PUSH3_SBIT))
    5. #define HAL_PUSH_BUTTON4()        (0)
    6. #define HAL_PUSH_BUTTON5()        (0)
    7. #define HAL_PUSH_BUTTON6()        (0)
    复制代码
        然后回到hal_key.c中,修改HalKeyRead函数如下:
    1. uint8 HalKeyRead ( void )
    2. { uint8 keys = 0;
    3. /*屏蔽掉默认的设置
    4.   if (HAL_PUSH_BUTTON1())
    5.   {keys |= HAL_KEY_SW_6;
    6.   }
    7.   */
    8.   if (HAL_PUSH_BUTTON3())//在这里检测P0_4是否有低电平
    9.   {keys |= HAL_KEY_SW_3; //将键值保存到keys中
    10.   }
    11.   if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT))  /*Key is active low */
    12.   { keys |= halGetJoyKeyInput();
    13.   }
    14.   return keys;
    15. }
    复制代码
        然后修改HalKeyPoll函数如下:
    1. void HalKeyPoll (void)
    2. {
    3.   uint8 keys = 0;
    4.   if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT))  /* Key is active HIGH */
    5.   {
    6.     keys = halGetJoyKeyInput();
    7.   }
    8.   /*在这里添加按键读取函数*/
    9.   keys=HalKeyRead();//轮询时在这里获取到了按键的值
    10.   if (!Hal_KeyIntEnable)
    11.   {
    12.     if (keys == halKeySavedKeys)//halKeySavedKeys被初始化为0
    13.     {
    14.       /* 和上一次相比,如果按键的值未改变,就直接跳出*/
    15.       return;
    16.     }
    17.     /* Store the current keys for comparation next time */
    18.     halKeySavedKeys = keys;
    19.   }
    20.   else
    21.   {
    22.     /* Key interrupt handled here */
    23.     /*使用轮询法时不会进入这里*/
    24.   }
    25. /*屏蔽掉默认的,在上面HalKeyRead函数中已经读取了按键值
    26.   if (HAL_PUSH_BUTTON1())
    27.   {
    28.     keys |= HAL_KEY_SW_6;
    29.   }
    30.   */
    31.   /* 如果有按键按下则调用回调函数 */
    32. //这里调用的回调函数实际上是OnBoard_KeyCallback
    33.   if (keys && (pHalKeyProcessFunction))
    34.   {
    35.     (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
    36.   }
    37. }
    复制代码
        回调函数如下:
    1. void OnBoard_KeyCallback ( uint8 keys, uint8 state )
    2. {
    3.   uint8 shift;
    4.   (void)state;

    5.   /* Get shift key status */
    6.   shift = ((keys & HAL_KEY_SW_6) ? true : false);
    7.   if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )
    8.   {
    9.     // Process SW1 here
    10.     if ( keys & HAL_KEY_SW_1 )  // Switch 1
    11.     {
    12.     }
    13.     // Process SW2 here
    14.     if ( keys & HAL_KEY_SW_2 )  // Switch 2
    15.     {
    16.     }
    17.     // Process SW3 here
    18.     if ( keys & HAL_KEY_SW_3 )  // Switch 3
    19.     {
    20.       /*    在这里不处理,传送到GenericApp里处理      */
    21.     }
    22.     // Process SW4 here
    23.     if ( keys & HAL_KEY_SW_4 )  // Switch 4
    24.     {
    25.     }
    26.     // Process SW5 here
    27.     if ( keys & HAL_KEY_SW_5 )  // Switch 5
    28.     {
    29.     }
    30.     // Process SW6 here
    31.     if ( keys & HAL_KEY_SW_6 )  // Switch 6
    32.     {
    33.     }
    34.   }
    35. }
    复制代码
        从上面我们可以看到,在回调函数里我们可以直接对按键事件进行处理,但是为了能够在GenericApp层统一开发,我们不在这里进行处理,而是把按键事件传送到GenericApp层进行处理,而在这里其传送功能的关键函数就是蓝色标记部分的OnBoard_SendKeys函数,然后我们在GenericApp.c文件下的按键事件处理函数GenericApp_HandleKeys下修改如下:
    1. void GenericApp_HandleKeys( byte shift, byte keys )
    2. {
    3.   zAddrType_t dstAddr;
    4.   
    5. <font color="#008000">  /**Shift is used to make each button/switch dual purpose.
    6.   ***shift作为辅助组合键的状态,默认为HAL_HEY_SW_6
    7.   ***如果shift为真则进入组合键判断程序
    8.   ***如果要使用HAL_KEY_SW_6为独立的按键,则需要将shift强制设置为false*/</font>
    9.   if ( shift )
    10.   {
    11.     if ( keys & HAL_KEY_SW_1 )
    12.     {
    13.     }
    14.     if ( keys & HAL_KEY_SW_2 )
    15.     {
    16.     }
    17.     if ( keys & HAL_KEY_SW_3 )
    18.     {
    19.     }
    20.     if ( keys & HAL_KEY_SW_4 )
    21.     {
    22.     }
    23.   }
    24.   else
    25.   {
    26.     if ( keys & HAL_KEY_SW_1 )
    27.     {
    28.     }

    29.     if ( keys & HAL_KEY_SW_2 )
    30.     {
    31.       
    32.     }

    33.     if ( keys & HAL_KEY_SW_3 )
    34.     {
    35. <font color="#008000">      HalLedSet ( HAL_LED_2, HAL_LED_MODE_TOGGLE );//按键3按下,LED2状态翻转</font>
    36.     }

    37.     if ( keys & HAL_KEY_SW_4 )
    38.     {
    39.       
    40.     }
    41.   }
    42. }
    复制代码
        注意上边绿色部分的注释,它说明了辅助组合键的一些用法,我们这里没有使用SW6,即辅助组合键,所以会直接跳到else里来处理按键,在按键处理函数中我们让SW3按下时LED2的状态翻转一次。至此,按键部分的驱动就修改完毕了,将程序烧到开发板上,我们便可以观察效果了:
    最上边是LED1接下来是LED2,中间的那个按键是SW3。
    P60326-000818.gif


    四、中断法处理按键驱动修改方法
            在开始修改之前,我要先提出对TI协议栈的一点疑问。在协议栈中,中断法按键检测的流程为:先检测按键端口的中断标志位是否为1,如果为1,就清除端口相应位的中断标志位,再清除端口的中断标志位,然后使用osal_start_timerEx(Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE)延时一 个HALHAL_KEY_DEBOUNCE_VALUE(默认为25毫秒)时间来触发按键的事件,然后再检测按键端口的中断标志位是否被置1,因此来达到按键消抖的目的,但是我在实际的实验中发现用TI的方法并不能很好地检测到按键,仔细分析后发现,TI按键消抖的方法必须满足一下条件:一是在按键按下时会产生机械抖动;二是机械抖动必须要能产生最少两个有效的下降沿或者上升沿(以自己设置的中断触发方式而定);三是第一次下降沿和后面抖动产生的下降沿之间的时间必须和消抖时间有很好的吻合性。但是上面的三个条件在我进行实验的时候往往不能很好地满足。也就是说TI的消抖方法可能需要改动。我用示波器检测了一下我的按键信号,发现基本上没有抖动,如下所示:
    4Y(X}HSM}(`YF$CDF99I_HB.jpg


    也就是说TI 的驱动在我的板子上完全用不了(自己做的开发板,用的按键机械性能比较好),所以我便开始自己尝试去改驱动,步骤如下。

        在InitBoard函数中使能按键中断:OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;

        在hal_key.c中添加按键3的有关宏定义:
    1. #define HAL_KEY_SW_3_PORT   P0
    2. #define HAL_KEY_SW_3_BIT    BV(4)
    3. #define HAL_KEY_SW_3_SEL    P0SEL
    4. #define HAL_KEY_SW_3_DIR    P0DIR
    5. /*     SW3   edge interrupt    */

    6. #define HAL_KEY_SW_3_EDGEBIT  BV(4)//边缘掩码  
    7. #define HAL_KEY_SW_3_EDGE     HAL_KEY_FALLING_EDGE //按键6触发边缘选择
    8. /* SW_3 interrupts */
    9. #define HAL_KEY_SW_3_IEN      IEN1  /* CPU interrupt mask register P0中断使能*/
    10. #define HAL_KEY_SW_3_IENBIT   BV(5) /* Mask bit for all of Port_0 */
    11. #define HAL_KEY_SW_3_ICTL     P0IEN /* Port Interrupt Control register P0位使能*/
    12. #define HAL_KEY_SW_3_ICTLBIT  BV(4) /* P0IEN - P0.4 enable/disable bit */
    13. #define HAL_KEY_SW_3_PXIFG    P0IFG /* Interrupt flag at source P0中断标志*/
    复制代码
        HalKeyInit函数修改如下:
    1. void HalKeyInit( void )
    2. {
    3.   /* Initialize previous key to 0 */
    4.   halKeySavedKeys = 0;
    5.   HAL_KEY_SW_3_SEL &= ~(HAL_KEY_SW_3_BIT);
    6.   HAL_KEY_SW_3_DIR &= ~(HAL_KEY_SW_3_BIT);
    7.   HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin function to GPIO */
    8.   HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin direction to Input */
    9.   /* 初始化按键回调函数 */
    10.   pHalKeyProcessFunction  = NULL;
    11.   /* Start with key is not configured */
    12.   /*初始化时按键未注册*/
    13.   HalKeyConfigured = FALSE;
    14. }
    复制代码
        HalKeyConfig函数修改如下:
    1. void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)
    2. {
    3.   Hal_KeyIntEnable = interruptEnable;
    4.   pHalKeyProcessFunction = cback;
    5.   /* Determine if interrupt is enable or not */
    6.   if (Hal_KeyIntEnable)//使用中断时在这里修改
    7.   {
    8.     /* Rising/Falling edge configuratinn */
    9.     PICTL &= ~(HAL_KEY_SW_3_EDGEBIT);    /* Clear the edge bit */
    10.     /* For falling edge, the bit must be set. */
    11.   #if (HAL_KEY_SW_3_EDGE == HAL_KEY_FALLING_EDGE)//下降沿触发中断
    12.     PICTL |= HAL_KEY_SW_3_EDGEBIT;
    13.   #endif
    14.     HAL_KEY_SW_3_ICTL |= HAL_KEY_SW_3_ICTLBIT;
    15.     HAL_KEY_SW_3_IEN |= HAL_KEY_SW_3_IENBIT;
    16.     HAL_KEY_SW_3_PXIFG = ~(HAL_KEY_SW_3_BIT);
    17.     /* Rising/Falling edge configuratinn */
    18.     HAL_KEY_JOY_MOVE_ICTL &= ~(HAL_KEY_JOY_MOVE_EDGEBIT);    /* Clear the edge bit */
    19.     /* For falling edge, the bit must be set. */
    20.   #if (HAL_KEY_JOY_MOVE_EDGE == HAL_KEY_FALLING_EDGE)
    21.     HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_EDGEBIT;
    22.   #endif
    23.     HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_ICTLBIT;
    24.     HAL_KEY_JOY_MOVE_IEN |= HAL_KEY_JOY_MOVE_IENBIT;
    25.     HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT);
    26.     /* Do this only after the hal_key is configured - to work with sleep stuff */
    27.     if (HalKeyConfigured == TRUE)
    28.     {
    29.       osal_stop_timerEx( Hal_TaskID, HAL_KEY_EVENT);  /* Cancel polling if active */
    30.     }
    31.   }
    32.   else    /* Interrupts NOT enabled */
    33.   {
    34.     HAL_KEY_SW_3_ICTL &= ~(HAL_KEY_SW_3_ICTLBIT); //don't generate interrupt
    35.     HAL_KEY_SW_3_IEN &= ~(HAL_KEY_SW_3_IENBIT);   // Clear interrupt enable bit

    36.     osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE);    /* Kick off polling */
    37.   }
    38.   /* Key now is configured */
    39.   HalKeyConfigured = TRUE;
    40. }
    复制代码
        HAL_ISR_FUNCTION函数修改如下:
    1. HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )
    2. {
    3.   if (HAL_KEY_SW_3_PXIFG & HAL_KEY_SW_3_BIT)
    4.   {
    5.     halProcessKeyInterrupt();
    6.   }
    7. }
    复制代码
        halProcessKeyInterrupt函数修改如下:
    1. void halProcessKeyInterrupt (void)
    2. { bool valid=FALSE;
    3.   if (HAL_KEY_CPU_PORT_0_IF)  /* Interrupt Flag has been set */
    4.   {
    5.     valid = TRUE;
    6.   }
    7.   if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT)  /* Interrupt Flag has been set */
    8.   {
    9.     HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Clear Interrupt Flag */
    10.     valid = TRUE;
    11.   }
    12.   if (valid)
    13.   {
    14.     osal_set_event(Hal_TaskID, HAL_KEY_EVENT);
    15.   }
    16. }
    复制代码
         在if(valid)中,因为我使用的按键没有什么机械抖动,所以就没有进行按键消抖,二是直接使用osal_set_event函数来触发了按键事件,然后按键的处理转到了函数HalKeyPoll中,修改如下:
    1. void HalKeyPoll (void)
    2. {uint8 keys = 0;
    3.   if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT))  /* Key is active HIGH */
    4.   {keys = halGetJoyKeyInput();
    5.   }
    6.   /*在这里添加按键读取函数*/
    7.   keys=HalKeyRead();
    8.   if (!Hal_KeyIntEnable)
    9.   {
    10.     if (keys == halKeySavedKeys)//halKeySavedKeys被初始化为0
    11.     {return;
    12.     }
    13.     /* Store the current keys for comparation next time */
    14.     halKeySavedKeys = keys;
    15.   }
    16.   else
    17.   {
    18.     if (HAL_KEY_SW_3_PXIFG & HAL_KEY_SW_3_BIT)//得到按键的值
    19.     {
    20.         keys |= HAL_KEY_SW_3;
    21.         HAL_KEY_SW_3_PXIFG = 0;
    22.         HAL_KEY_CPU_PORT_0_IF = 0;
    23.     }
    24.   }
    25.   /* 如果有按键按下则调用回调函数 */
    26.   if (keys && (pHalKeyProcessFunction))
    27.   {
    28.     (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
    29.   }
    30. }
    复制代码
        剩余其他部分的修改和轮询法相同,不在一一列出,修改后烧到开发板上,观察现象:
    P60327-162949.gif

    回复

    使用道具 举报

  • TA的每日心情
    开心
    2016-11-18 11:38
  • 签到天数: 57 天

    连续签到: 1 天

    [LV.5]常住居民I

     楼主| 发表于 2016-3-27 19:25:31 | 显示全部楼层
    代码的颜色没改了,不知道怎么回事!!!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2016-8-15 09:28
  • 签到天数: 222 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2016-3-28 10:34:48 | 显示全部楼层
    楼主写的非常棒!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2020-9-28 10:10
  • 签到天数: 1018 天

    连续签到: 1 天

    [LV.10]以坛为家III

    发表于 2016-3-28 10:35:14 | 显示全部楼层
    恶魔花花 发表于 2016-3-27 19:25
    代码的颜色没改了,不知道怎么回事!!!

    没改?啥意思?不太懂
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2016-11-18 11:38
  • 签到天数: 57 天

    连续签到: 1 天

    [LV.5]常住居民I

     楼主| 发表于 2016-4-17 20:13:27 | 显示全部楼层
    小菜儿 发表于 2016-3-28 10:35
    没改?啥意思?不太懂

    代码的颜色改不了,变成了  font color="#008000”
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2016-11-18 11:38
  • 签到天数: 57 天

    连续签到: 1 天

    [LV.5]常住居民I

     楼主| 发表于 2016-4-17 20:13:59 | 显示全部楼层
    奋斗哥 发表于 2016-3-28 10:34
    楼主写的非常棒!

    谢谢支持!
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-4-19 06:16 , Processed in 0.166175 second(s), 26 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.