1. 程式人生 > >ZIGBEE-通過協議棧實現按鍵功能

ZIGBEE-通過協議棧實現按鍵功能

  • 協議棧已經自帶了按鍵的驅動和使用函式。
  • 網蜂 ZigBee 開發套件按鍵 S1 連線的是 P0.0 口,按鍵 S2 連線的是 P0.1 口。
  • 在 ZMain.c 的 main 主函式中跟按鍵相關的有: HalDriverInit();    InitBoard( OB_READY );
  • 按鍵流程分析

A.非中斷情況下:

第一步:進入 HalDriverInit()中的 HalKeyInit()  

void HalKeyInit( void )

{

  /* Initialize previous key to 0 */

  halKeySavedKeys = 0;





   /*這裡主要是初始化按鍵相關的引腳,HAL_KEY_SW_6 就是對應 P01*/

  HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT);    /* Set pin function to GPIO */

  HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT);    /* Set pin direction to Input */





   /*而HAL_KEY_JOY 就是 TI 板子上的 J-STICK 搖桿,這裡我們不用用到,直接註釋或刪掉。*/

  //HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin function to GPIO  */

  //HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin direction to Input*/


  /* Initialize callback function */

  pHalKeyProcessFunction  = NULL;


  /* Start with key is not configured */

  HalKeyConfigured = FALSE;

}

第二步:分析 InitBoard( OB_READY )  

void InitBoard( uint8 level )

{

  if ( level == OB_COLD )

  {

    // IAR does not zero-out this byte below the XSTACK.

    *(uint8 *)0x0 = 0;

    // Interrupts off

    osal_int_disable( INTS_ALL );

    // Check for Brown-Out reset

    ChkReset();

  }

  else  // !OB_COLD

  {

    /* Initialize Key stuff */





   /*對按鍵的具體配置(重點):配置按鍵的檢測方式和按鍵的回撥函式 */

    HalKeyConfig(HAL_KEY_INTERRUPT_ENABLE, OnBoard_KeyCallback);





  //HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);

  }

}

第三步:進入按鍵的檢測方式和按鍵的回撥函式 HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);

void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)
{
  /* Enable/Disable Interrupt or */
  Hal_KeyIntEnable = interruptEnable;

  /* Register the callback fucntion */   


  /* 註冊回撥函式---重點 */ 
  pHalKeyProcessFunction = cback;




  /* Determine if interrupt is enable or not */
  if (Hal_KeyIntEnable)
  {
    /* Rising/Falling edge configuratinn */

    PICTL &= ~(HAL_KEY_SW_6_EDGEBIT);    /* Clear the edge bit */
    /* For falling edge, the bit must be set. */
  #if (HAL_KEY_SW_6_EDGE == HAL_KEY_FALLING_EDGE)
    PICTL |= HAL_KEY_SW_6_EDGEBIT;
  #endif


    /* Interrupt configuration:
     * - Enable interrupt generation at the port
     * - Enable CPU interrupt
     * - Clear any pending interrupt
     */
    HAL_KEY_SW_6_ICTL |= HAL_KEY_SW_6_ICTLBIT;
    HAL_KEY_SW_6_IEN |= HAL_KEY_SW_6_IENBIT;
    HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT);



    /* Rising/Falling edge configuratinn */

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


    /* Interrupt configuration:
     * - Enable interrupt generation at the port
     * - Enable CPU interrupt
     * - Clear any pending interrupt
     */
    HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_ICTLBIT;
    HAL_KEY_JOY_MOVE_IEN |= HAL_KEY_JOY_MOVE_IENBIT;
    HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT);


    /* Do this only after the hal_key is configured - to work with sleep stuff */
    if (HalKeyConfigured == TRUE)
    {
      osal_stop_timerEx(Hal_TaskID, HAL_KEY_EVENT);  /* Cancel polling if active */
    }
  }
  else    /* Interrupts NOT enabled */
  {
    HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don't generate interrupt */
    HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT);   /* Clear interrupt enable bit */


    
    /********* 啟動 HAL_KEY_EVENT 事件 ***************/
    osal_set_event(Hal_TaskID, HAL_KEY_EVENT);  




  }


/*從上面的配置我們知道按鍵檢測有兩種方式,一種為中斷,一種為定時檢測;
定 時 檢 測 的 話 在 配 置 時 就 會 直 接 啟 動 HAL_KEY_EVENT 事 件 , 那 麼 在
HAL_KEY_EVENT 中會做什麼事情呢?找到此事件的處理函式。 hal_drivers.c 中的 Hal_ProcessEvent()函式: */


  
  /* Key now is configured */
  HalKeyConfigured = TRUE;
}

第四步:啟 動 HAL_KEY_EVENT 事 件 後,進入hal_drivers.c 中的 Hal_ProcessEvent()函式

 /***************檢測按鍵*********************************/
  
  if (events & HAL_KEY_EVENT)
  {

#if (defined HAL_KEY) && (HAL_KEY == TRUE)
    


    /* Check for keys */
    HalKeyPoll();          //檢測按鍵——重點 



    /* if interrupt disabled, do next polling */

    //如果不是中斷方式的話就定時啟動此事件檢測按鍵 ,啟用定時器,定時啟動HAL_KEY_EVent事件。
    if (!Hal_KeyIntEnable)
    {
      osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
    }
#endif // HAL_KEY



    return events ^ HAL_KEY_EVENT;
  }

第五步:在上一步中定時進入HalKeyPoll()檢測按鍵。


void HalKeyPoll (void)
{

  uint8 keys = 0; //檢測按鍵 S2 是否按下 

//  if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT))  /* Key is active HIGH */
//  {
//    keys = halGetJoyKeyInput();
//  }

  
/*************************按鍵的檢測 **********************/
  if (HAL_PUSH_BUTTON1())   //這裡需要改成低電平按下 
  {
    keys |= HAL_KEY_SW_6;
  }


  /* If interrupts are not enabled, previous key status and current key status
   * are compared to find out if a key has changed status.
  
    當按鍵按下時就傳遞給上面註冊過的回撥函式 OnBoard_KeyCallback,傳遞進去的就是
    相應的鍵值keys ;找到之前註冊的回撥函式OnBoard_KeyCallback(在 OnBoard.c 中) 
   */
  if (!Hal_KeyIntEnable)
  {
     //按鍵延時,防止按鍵按下發送多次按鍵事件 
    if (keys == halKeySavedKeys)
    {
      /* Exit - since no keys have changed */
      return;
    }
    /* Store the current keys for comparation next time */
    halKeySavedKeys = keys;
  }
  else
  {
    /* Key interrupt handled here */
  }


  /* Invoke Callback if new keys were depressed */
  /* 呼叫註冊的回撥函式,即上面的 OnBoard_KeyCallback */ 
  if (keys && (pHalKeyProcessFunction))
  {
    (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
  }
}

第六步:當按鍵按下時就傳遞給上面註冊過的回撥函式 OnBoard_KeyCallback,傳遞 進 去 的 就 是 相 應 的 鍵 值 keys ; 找 到 之 前 注 冊 的 回 調 函 數OnBoard_KeyCallback(在 OnBoard.c 中) 。


void OnBoard_KeyCallback ( uint8 keys, uint8 state )// 回 調 函 數
{
  uint8 shift;
  (void)state;

  shift = (keys & HAL_KEY_SW_6) ? true : false;

  if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )
  {
    // Process SW1 here
    if ( keys & HAL_KEY_SW_1 )  // Switch 1
    {
    }
    // Process SW2 here



    if ( keys & HAL_KEY_SW_2 )  // Switch 2
    {
      //這裡就是通過 OnBoard_SendKeys( keys, shift )在傳送系統資訊給使用者的應用任務。
      
    }



    // Process SW3 here
    if ( keys & HAL_KEY_SW_3 )  // Switch 3
    {
    }
    // Process SW4 here
    if ( keys & HAL_KEY_SW_4 )  // Switch 4
    {
    }
    // Process SW5 here
    if ( keys & HAL_KEY_SW_5 )  // Switch 5
    {
    }
    // Process SW6 here
    if ( keys & HAL_KEY_SW_6 )  // Switch 6
    {
    }
  }
}

第七步:通過 OnBoard_SendKeys( keys, shift )傳送系統資訊給使用者的應用任務。

uint8 OnBoard_SendKeys( uint8 keys, uint8 state )
{
  keyChange_t *msgPtr;

  if ( registeredKeysTaskID != NO_TASK_ID )
  {
    // Send the address to the task
    msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );
    if ( msgPtr )
    {
      msgPtr->hdr.event = KEY_CHANGE;
      msgPtr->state = state;
      msgPtr->keys = keys;


      osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );
      /*registeredKeysTaskID 任務     這個就 是 我 們 用 戶 根 據 自 己 的 需 要 
      選 擇 按 鍵 的 傳 遞 的 任 務 號 ,  可 通 過RegisterForKeys( xxx_TaskID )
      註冊,此工程中在 SampleApp_Init()中已經呼叫此函式註冊到 SampleApp_TaskID 
      中了,就是說按鍵資訊會傳遞到此任務中。 */



    }
    return ( ZSuccess );
  }
  else
    return ( ZFailure );
}

第八步:傳遞到SampleApp_ProcessEvent( uint8 task_id, uint16 events )中的 SampleApp_HandleKeys()按鍵事件及處理函式.

uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
  afIncomingMSGPacket_t *MSGpkt;
  (void)task_id;  // Intentionally unreferenced parameter

  if ( events & SYS_EVENT_MSG )   //系統資訊傳送事件
  {
    MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
    while ( MSGpkt )
    {
      switch ( MSGpkt->hdr.event )
      {


        // Received when a key is pressed
        case KEY_CHANGE:  //按鍵事件及處理函式
          SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );



          break;

        // Received when a messages is received (OTA) for this endpoint
        case AF_INCOMING_MSG_CMD:
          SampleApp_MessageMSGCB( MSGpkt );
          break;

第九步:在SampleApp_HandleKeys()按鍵事件及處理函式中 根據具體的鍵值做相應的處理,這裡利用串列埠列印提示按鍵按下。


void SampleApp_HandleKeys( uint8 shift, uint8 keys )
{
  (void)shift;  // Intentionally unreferenced parameter
  
  if ( keys & HAL_KEY_SW_6 )
  {
    /* This key sends the Flash Command is sent to Group 1.此鍵傳送Flash命令傳送到組1
     * This device will not receive the Flash Command from this
     * device (even if it belongs to group 1).
     * 此裝置將不接收來自該裝置的快閃記憶體命令(即使它屬於第1組)
     */

    
   /*按鍵事件:串列埠列印*/
   HalUARTWrite(0,"KEY S2\n",7);
   // SampleApp_SendFlashMessage( SAMPLEAPP_FLASH_DURATION );




  }

  if ( keys & HAL_KEY_SW_2 )
  {
    /* The Flashr Command is sent to Group 1.
     * This key toggles this device in and out of group 1.
     * If this device doesn't belong to group 1, this application
     * will not receive the Flash command sent to group 1.
     *FLASR命令傳送到第1組。
     *此鍵可將該裝置切換到第1組。
     *如果此裝置不屬於第1組,則此應用程式
     *不會收到傳送到組1的Flash命令。
     */
    HalUARTWrite(0,"KEY S3\n",7);
   }
}

B.中斷方式

在 HalKeyConfig(HAL_KEY_INTERRUPT_ENABLE, OnBoard_KeyCallback);中設定成中斷檢測方式。設定成中斷檢測方式就不會定時啟動 HAL_KEY_EVENT 事件,這樣就會更加節省系統資源,所以一般都是使用中斷方式來檢測按鍵。當按下按鍵時會進入 P0 口中斷服務函式 hal_key.c中的HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )  P0 口中斷服務函式 。

HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )
{
  HAL_ENTER_ISR();

  if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)
  {
    halProcessKeyInterrupt();
  }

  /*
    Clear the CPU interrupt flag for Port_0
    PxIFG has to be cleared before PxIF
  */
  HAL_KEY_SW_6_PXIFG = 0;
  HAL_KEY_CPU_PORT_0_IF = 0;
  
  CLEAR_SLEEP_MODE();
  HAL_EXIT_ISR();
}

並呼叫 halProcessKeyInterrupt()函式處理按鍵中斷事件:

void halProcessKeyInterrupt (void)
{
  bool valid=FALSE;

  if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)  /* Interrupt Flag has been set */
  {
    HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Clear Interrupt Flag */
    valid = TRUE;
  }
//
//  if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT)  /* Interrupt Flag has been set */
//  {
//    HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Clear Interrupt Flag */
//    valid = TRUE;
//  }

  if (valid)
  {
    osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);
  }
}

這裡就又跑去啟動 HAL_KEY_EVENT 事件了。