小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

TI BLE協(xié)議棧 按鍵流程分析

 堆泄露棧溢出 2016-10-10


之前在藍牙技術(shù)群看到好多網(wǎng)友不知道按鍵流程到底是什么情況,平時也沒時間,在群里也一兩句說不明白,也就說了下可以去看下zigbee按鍵流程過程,其實都是相通的,現(xiàn)在特意發(fā)帖分享下,希望能起到一個拋磚引玉的作用。

在介紹藍牙按鍵流程分析之前,我們需要了解一個概念,那就是就是OSAL。什么是OSAL呢?
可能大伙對于OS是比較了解的,學(xué)了計算機的搞過OS的也基本接觸過,簡單來說就是一個操作系統(tǒng)抽象層,可以理解為運行在 CC2540 上的操作系統(tǒng),說操作系統(tǒng)還不能算,TI的OSAL只實現(xiàn)了任務(wù)切換和消息機制。并且把協(xié)議棧的代碼、硬件處理的代碼,用戶程序的代碼等分別放到了 OSAL 層的不同任務(wù)處理函數(shù)中去了,各任務(wù)函數(shù)之間通過消息機制、同一個任務(wù)之間通過事件的的方式來通信。

什么是 EVENT 事件?
OSAL 為每個任務(wù)函數(shù)分配了一個 16 位的事件變量,每一位代表一個事件,最高位為 0x8000
表示為系統(tǒng)事件 SYS_EVENT_MSG。其余的15 位留給用戶自定義需要的事件。通常事件由定時
器啟動,比如一秒后我要點亮 LED2,這就需要發(fā)送一個點亮 LED2 的事件,然后等待定時器1s后溢出,于是啟動點亮 LED2事件,事件會調(diào)用相應(yīng)的hal 層API點亮LED2。


什么是 MSG 消息

MSG 是比 EVENT 事件更具體并且可以攜帶數(shù)據(jù)的一種通信方式,MSG 的標記是按數(shù)值,而不是按位。比如 0x01 和 0x02 是兩個不同的消息,但對于事件 0x03 則是 0x01 事件和 0x02 事件的組合。MSG 收發(fā)使用 osal_msg_send()和 osal_msg_receive();當調(diào)用 osal_msg_send()發(fā)送一個 msg 的同時會在 EVENT 列表中觸發(fā)一個 message ready event。(請注意最后一句話,這句話點出了為什么按鍵時間的觸發(fā)為何會導(dǎo)致系統(tǒng)事件也接受到了)


現(xiàn)在以 SimpleBLEPeripheral 為例說明按鍵流程

在 SimpleBLEPeripheral 任務(wù)初始化函數(shù)中有這樣一條代碼:

  // Register for all key events - This app will handle all key events
  RegisterForKeys( simpleBLEPeripheral_TaskID );


這個函數(shù)來自 OnBoard.c 源文件中
/*********************************************************************
* Keyboard Register function
*
* The keyboard handler is setup to send all keyboard changes to
* one task (if a task is registered).
*
* If a task registers, it will get all the keys. You can change this
* to register for individual keys.
*********************************************************************/
uint8 RegisterForKeys( uint8 task_id )
{
  // Allow only the first task
  if ( registeredKeysTaskID == NO_TASK_ID )
  {
    registeredKeysTaskID = task_id;
    return ( true );
  }
  else
    return ( false );
}


向一個全局變量 registeredKeysTaskID中賦值自己的任務(wù) ID,調(diào)用了這個函數(shù)就能成功注冊按鍵服務(wù),那這個全局變量在何時使用呢?
分析到這里,感覺有點迷糊了,我們可以從頂?shù)较路治?。任何一個程序都是從main函數(shù)開始的,這點我們要堅信。所以我們首先找到這個main函數(shù)
打開SimpleBLEPeripheral_Main.c文件可以看到/**************************************************************************************************
* @fn          main
*
* @brief       Start of application.
*
* @param       none
*
* @return      none
**************************************************************************************************
*/
int main(void)
{
  /* Initialize hardware */
  HAL_BOARD_INIT();   

  // Initialize board I/O
  InitBoard( OB_COLD );

  /* Initialze the HAL driver */
  HalDriverInit();

  /* Initialize NV system */
  osal_snv_init();

  /* Initialize LL */

  /* Initialize the operating system */
  osal_init_system();

  /* Enable interrupts */
  HAL_ENABLE_INTERRUPTS();

  // Final board initialization
  InitBoard( OB_READY );

  #if defined ( POWER_SAVING )
    osal_pwrmgr_device( PWRMGR_BATTERY );
  #endif

  /* Start OSAL */
  osal_start_system(); // No Return from here

  return 0;
}
我們打開 InitBoard( OB_READY );可以看到如下代碼
/*********************************************************************
* @fn      InitBoard()
* @brief   Initialize the CC2540DB Board Peripherals
* @param   level: COLD,WARM,READY
* @return  None
*/
void InitBoard( uint8 level )
{
  if ( level == OB_COLD )
  {
    // Interrupts off
    osal_int_disable( INTS_ALL );
    // Turn all LEDs off
    HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );
    // Check for Brown-Out reset
//    ChkReset();
  }
  else  // !OB_COLD
  {
    /* Initialize Key stuff */
    OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;
    //OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
    HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
  }
}

看到我上面標注的函數(shù)了吧?那個是一個按鍵回調(diào)服務(wù)注冊函數(shù),注冊了一個OnBoard_KeyCallback函數(shù)
HalKeyConfig 函數(shù)的實現(xiàn):
將上述的回調(diào)函數(shù)的地址復(fù)制給了函數(shù)指針變量。通過跟蹤發(fā)現(xiàn)該函數(shù)的指針變量在按鍵的輪詢函數(shù)中調(diào)用了,如下圖:
/**************************************************************************************************
* @fn      HalKeyPoll
*
* @brief   Called by hal_driver to poll the keys
*
* @param   None
*
* @return  None
**************************************************************************************************/
void HalKeyPoll (void)
{
  uint8 keys = 0;
  uint8 notify = 0;
#if defined (CC2540_MINIDK)
  if (!(HAL_KEY_SW_1_PORT & HAL_KEY_SW_1_BIT))    /* Key is active low */
  {
    keys |= HAL_KEY_SW_1;
  }
  if (!(HAL_KEY_SW_2_PORT & HAL_KEY_SW_2_BIT))    /* Key is active low */
  {
    keys |= HAL_KEY_SW_2;
  }
#else
  if (!(HAL_KEY_SW_6_PORT & HAL_KEY_SW_6_BIT))    /* Key is active low */
  {
    keys |= HAL_KEY_SW_6;
  }


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


  /* If interrupts are not enabled, previous key status and current key status
   * are compared to find out if a key has changed status.
   */
  if (!Hal_KeyIntEnable)
  {
    if (keys == halKeySavedKeys)
    {
      /* Exit - since no keys have changed */
      return;
    }
    else
    {
      notify = 1;
    }
  }
  else
  {
    /* Key interrupt handled here */
    if (keys)
    {
      notify = 1;
    }
  }


  /* Store the current keys for comparation next time */
  halKeySavedKeys = keys;


  /* Invoke Callback if new keys were depressed */
  if (notify && (pHalKeyProcessFunction))
  {
    (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);


  }
}

在這里底層的按鍵查詢函數(shù)調(diào)用一個函數(shù)指針,而非具體的函數(shù),這樣就將處理按鍵的接口留給了上層,上層應(yīng)用中,叧需解析的函數(shù)指針傳入的參數(shù) 1:keys 就知道是哪個按鍵被按下了。
我們再回到剛才的 OnBoard_KeyCallback 回調(diào)函數(shù)處,該回調(diào)函數(shù)代碼如下:
/*********************************************************************
* @fn      OnBoard_KeyCallback
*
* @brief   Callback service for keys
*
* @param   keys  - keys that were pressed
*          state - shifted
*
* @return  void
*********************************************************************/
void OnBoard_KeyCallback ( uint8 keys, uint8 state )
{
  uint8 shift;
  (void)state;

  // shift key (S1) is used to generate key interrupt
  // applications should not use S1 when key interrupt is enabled
  shift = (OnboardKeyIntEnable == HAL_KEY_INTERRUPT_ENABLE) ? false : ((keys & HAL_KEY_SW_6) ? true : false);

  if ( OnBoard_SendKeys( keys, shift ) != SUCCESS )  //就是這句話將按鍵消息上傳到應(yīng)用層去處理的
  {
    // Process SW1 here
    if ( keys & HAL_KEY_SW_1 )  // Switch 1
    {
    }
    // Process SW2 here
    if ( keys & HAL_KEY_SW_2 )  // Switch 2
    {
    }
    // 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
    {
    }
  }

  /* If any key is currently pressed down and interrupt
     is still enabled, disable interrupt and switch to polling */
  if( keys != 0 )
  {
    if( OnboardKeyIntEnable == HAL_KEY_INTERRUPT_ENABLE )
    {
      OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
      HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
    }
  }
  /* If no key is currently pressed down and interrupt
     is disabled, enable interrupt and turn off polling */
  else
  {
    if( OnboardKeyIntEnable == HAL_KEY_INTERRUPT_DISABLE )
    {
      OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;
      HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
    }
  }
}


進入按鍵消息發(fā)送函數(shù)中可以看到

/*********************************************************************
* @fn      OnBoard_SendKeys
*
* @brief   Send "Key Pressed" message to application.
*
* @param   keys  - keys that were pressed
*          state - shifted
*
* @return  status
*********************************************************************/
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 );
    }
    return ( SUCCESS );
  }
  else
    return ( FAILURE );
}


主要是將按鍵時間,狀態(tài)和按鍵值打包到信息中最后通過 osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );發(fā)送到注冊了按鍵服務(wù)的應(yīng)用層去 ,最終用戶按了哪個按鍵,如何響應(yīng)該按鍵在系統(tǒng)事件 SYS_EVENT_MSG 中處理。疑問又來了,為什么通過 osal_msg_send 收發(fā)的消息會出現(xiàn)在 SYS_EVENT_MSG 中呢?這個疑問,就是剛才我說過的osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr )會產(chǎn)生一個 a message  ready event in the destination tasks event list.
/*********************************************************************
* @fn      osal_msg_send
*
* @brief
*
*    This function is called by a task to send a command message to
*    another task or processing element.  The sending_task field must
*    refer to a valid task, since the task ID will be used
*    for the response message.  This function will also set a message
*    ready event in the destination tasks event list.
*
*
* @param   uint8 destination_task - Send msg to Task ID
* @param   uint8 *msg_ptr - pointer to new message buffer
*
* @return  SUCCESS, INVALID_TASK, INVALID_MSG_POINTER
*/
uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
{
  return ( osal_msg_enqueue_push( destination_task, msg_ptr, FALSE ) );
}


/*********************************************************************
* @fn      simpleBLEPeripheral_ProcessOSALMsg
*
* @brief   Process an incoming task message.
*
* @param   pMsg - message to process
*
* @return  none
*/
static void simpleBLEPeripheral_ProcessOSALMsg( osal_event_hdr_t *pMsg )
{
  switch ( pMsg->event )
  {
  #if defined( CC2540_MINIDK )
    case KEY_CHANGE:                  //按鍵處理事件,用戶產(chǎn)生的按鍵都是在這里處理    
      simpleBLEPeripheral_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );
      break;
  #endif // #if defined( CC2540_MINIDK )


  default:
    // do nothing
    break;
  }
}

在 SimpleBLEPeripheral 中,對按鍵的響應(yīng)如下:joystick right(SW2)收發(fā)廣播的開啟和關(guān)閉

/*********************************************************************
* @fn      simpleBLEPeripheral_HandleKeys
*
* @brief   Handles all key events for this device.
*
* @param   shift - true if in shift/alt.
* @param   keys - bit field for key events. Valid entries:
*                 HAL_KEY_SW_2
*                 HAL_KEY_SW_1
*
* @return  none
*/
static void simpleBLEPeripheral_HandleKeys( uint8 shift, uint8 keys )
{
  uint8 SK_Keys = 0;


  VOID shift;  // Intentionally unreferenced parameter


  if ( keys & HAL_KEY_SW_1 )
  {
    SK_Keys |= SK_KEY_LEFT;
  }


  if ( keys & HAL_KEY_SW_2 )
  {


    SK_Keys |= SK_KEY_RIGHT;


    // if device is not in a connection, pressing the right key should toggle
    // advertising on and off
    if( gapProfileState != GAPROLE_CONNECTED )
    {
      uint8 current_adv_enabled_status;
      uint8 new_adv_enabled_status;


      //Find the current GAP advertisement status
      GAPRole_GetParameter( GAPROLE_ADVERT_ENABLED, &current_adv_enabled_status ); //這個函數(shù)主要就是獲得廣播的開啟和關(guān)閉狀態(tài)


      if( current_adv_enabled_status == FALSE )
      {
        new_adv_enabled_status = TRUE;
      }
      else
      {
        new_adv_enabled_status = FALSE;
      }


      //change the GAP advertisement status to opposite of current status
      GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &new_adv_enabled_status );
    }


  }


  // Set the value of the keys state to the Simple Keys Profile;
  // This will send out a notification of the keys state if enabled
  SK_SetParameter( SK_KEY_ATTR, sizeof ( uint8 ), &SK_Keys );
}
#endif // #if defined( CC2540_MINIDK )

現(xiàn)在按鍵基本分析完了,其實CC2540學(xué)習(xí)板子是默認開啟廣播,但是Keyfob就需要按鍵手動開啟了。接下來我會繼續(xù)分析協(xié)議棧的其他功能,如有不足之外,望補充~~~~~~~~~~(花了我?guī)讉€小時寫出來,蛋疼。寫文章太費勁了)


轉(zhuǎn)載自:http://bbs./forum.PHP?mod=viewthread&tid=428987

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多