1. 程式人生 > >ZigBee Z-Stack 3.15 按鍵驅動2-中斷模式

ZigBee Z-Stack 3.15 按鍵驅動2-中斷模式

(配套原始碼軟體開發板等資源,可移步部落格同名QQ群:拿破崙940911

在Z-Stack協議棧中,對於按鍵的檢測,分為兩種不同的機制,分別稱為“輪詢模式”和“中斷模式”,類比微控制器中的按鍵檢測,還是很好理解的。但是相比我們之前所學所用,Z-Stack協議棧中的按鍵檢測實現還是相對要複雜一些的。

本節將分為4點詳細講述Z-Stack協議棧中的按鍵檢測機制:

1、按鍵檢測機制選擇——“輪詢模式”or“中斷模式”

2、輪詢模式

3、中斷模式

4、HalKeyInit( )

1、按鍵檢測機制選擇——“輪詢模式”or“中斷模式”

實際執行過程中,按鍵檢測到底工作在哪種模式,由InitBoard( )函式中下面這句程式碼決定:

    HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);

HalKeyConfig( )函式中的第一個引數,若是HAL_KEY_INTERRUPT_DISABLE,則是選擇“輪詢模式”;若是HAL_KEY_INTERRUPT_ENABLE,則是選擇“中斷模式”;由上述程式碼可見,Z-Stack協議棧中預設使用的是“輪詢模式”。具體是如何實現的,詳見HalKeyConfig( )函式中的實現程式碼,該函式主要功能如下:

(1)指定按鍵回撥函式為OnBoard_KeyCallback( ):

  /* Register the callback fucntion */
  pHalKeyProcessFunction = cback;
(2)如果要“使能中斷”,則進行相應的中斷配置(如配置觸發邊沿、清除中斷標誌位等):
  /* 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 */
    }
  }
(3)如果不“使能中斷”,則向硬體抽象層任務(Hal_TaskID)傳送按鍵查詢事件(HAL_KEY_EVENT),觸發一次按鍵查詢:
  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 */

    osal_set_event(Hal_TaskID, HAL_KEY_EVENT);
  }

2、輪詢模式

(1)上面已經提及,Z-Stack協議棧中預設使用的就是“輪詢模式”,第一次觸發硬體抽象層任務(Hal_TaskID)中的按鍵查詢事件(HAL_KEY_EVENT)後,隨即也就觸發了間隔為100ms(1秒10次)的定時按鍵檢測(迴圈呼叫HalKeyPoll( )),故稱為“輪詢模式”:

uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )
{
  ......

  if (events & HAL_KEY_EVENT)
  {

#if (defined HAL_KEY) && (HAL_KEY == TRUE)
    /* Check for keys */
    HalKeyPoll();

    /* if interrupt disabled, do next polling */
    if (!Hal_KeyIntEnable)
    {
      osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
    }
#endif // HAL_KEY

    return events ^ HAL_KEY_EVENT;
  }

  ......
}

(2)在HalKeyPoll( )函式中,對各個定義的按鍵狀態做完檢測之後,即呼叫前面指定的按鍵回撥函式OnBoard_KeyCallback( ),對鍵值做進一步處理:

void HalKeyPoll (void)
{
  ......
  /* Invoke Callback if new keys were depressed */
  if (keys && (pHalKeyProcessFunction))
  {
    (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
  }
  ......
}
OnBoard_KeyCallback( )函式的程式碼如下,可見其中又進一步呼叫了OnBoard_SendKeys( ):
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 )
  {
    ......
  }
}
在OnBoard_SendKeys( )函式中,最終將鍵值(keys)封裝成了一個keyChange_t型別的資料包,傳送至了registeredKeysTaskID任務:
uint8 OnBoard_SendKeys( uint8 keys, uint8 state )
{
  ......
    // 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( )函式中被賦值指定的:

uint8 RegisterForKeys( uint8 task_id )
{
  // Allow only the first task
  if ( registeredKeysTaskID == NO_TASK_ID )
  {
    registeredKeysTaskID = task_id;
    return ( true );
  }
  else
    return ( false );
}
而呼叫RegisterForKeys( )函式,則是在我們的應用層任務ProjectApp中,也就是所謂的“註冊按鍵”:
void ProjectApp_Init( uint8 task_id )
{
  ProjectApp_TaskID = task_id;
  ......
  RegisterForKeys( ProjectApp_TaskID );// Register for all key events - This app will handle all key events
  ......
}

(3)緊接著,我們的應用層任務ProjectApp就會收到鍵值所在的資料包,具體處理鍵值的程式碼如下:

uint16 ProjectApp_ProcessEvent( uint8 task_id, uint16 events )
{
  ......
  if ( events & SYS_EVENT_MSG )
  {
    ......
        case KEY_CHANGE         : ProjectApp_HandleKeys(((keyChange_t *)MSGpkt)->state,((keyChange_t *)MSGpkt)->keys);break;
    ......
  }
  ......
}

具體ProjectApp_HandleKeys( )這個鍵值處理函式實現什麼功能,就完全取決於大家自己啦~

3、中斷模式

(1)在InitBoard( )函式中呼叫HalKeyConfig( )時,若第一個引數填為HAL_KEY_INTERRUPT_ENABLE,則會自動選擇為“中斷模式”;

(2)在“中斷模式”下,一旦按鍵所在IO口產生了外部中斷,程式便會立即響應,調到對應的中斷服務函式中(以P0組IO對應中斷為例):

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();
}
Z-Stack協議棧中關於P0組的中斷服務函式已經編寫好,我們只需關心其中的halProcessKeyInterrupt( )函式即可:
void halProcessKeyInterrupt (void)
{
  ......
  if (valid)
  {
    osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);
  }
}

在halProcessKeyInterrupt( )函式中,首先對按鍵引發的外部中斷的有效性進行判斷,若有效,便也會如“輪詢模式”下一樣,向硬體抽象層任務(Hal_TaskID)傳送按鍵查詢事件(HAL_KEY_EVENT),觸發一次按鍵查詢。不同的是,這裡是延時HAL_KEY_DEBOUNCE_VALUE(25)毫秒之後再執行,這裡的延時可以起到“消抖”的功能!

(3)延時HAL_KEY_DEBOUNCE_VALUE毫秒後,程式執行到了硬體抽象層任務(Hal_TaskID)的任務處理函式(Hal_ProcessEvent( ))中的HAL_KEY_EVENT分支,後面的程式碼原理就和“輪詢模式”幾乎沒有任何區別了!唯一一點不同是不會像“輪詢模式”下那樣觸發間隔為100ms(1秒10次)的定時按鍵檢測。

4、HalKeyInit( )

不管選擇的是“輪詢模式”還是“中斷模式”,在系統真正開始進行按鍵檢測之前,一定都要對按鍵所在IO當做是普通IO口來進行相關初始化,對應的函式是HalKeyInit( )。

(配套原始碼軟體開發板等資源,可移步部落格同名QQ群:拿破崙940911