TI BLE协议栈 按键流程分析
生活随笔
收集整理的這篇文章主要介紹了
TI BLE协议栈 按键流程分析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
之前在藍牙技術群看到好多網友不知道按鍵流程到底是什么情況,平時也沒時間,在群里也一兩句說不明白,也就說了下可以去看下zigbee按鍵流程過程,其實都是相通的,現在特意發帖分享下,希望能起到一個拋磚引玉的作用。
在介紹藍牙按鍵流程分析之前,我們需要了解一個概念,那就是就是OSAL。 什么是OSAL呢?可能大伙對于OS是比較了解的,學了計算機的搞過OS的也基本接觸過,簡單來說就是一個操作系統抽象層,可以理解為運行在 CC2540 上的操作系統,說操作系統還不能算,TI的OSAL只實現了任務切換和消息機制。并且把協議棧的代碼、硬件處理的代碼,用戶程序的代碼等分別放到了 OSAL 層的不同任務處理函數中去了,各任務函數之間通過消息機制、同一個任務之間通過事件的的方式來通信。
什么是 EVENT 事件?
OSAL 為每個任務函數分配了一個 16 位的事件變量,每一位代表一個事件,最高位為 0x8000
表示為系統事件 SYS_EVENT_MSG。其余的15 位留給用戶自定義需要的事件。通常事件由定時
器啟動,比如一秒后我要點亮 LED2,這就需要發送一個點亮 LED2 的事件,然后等待定時器1s后溢出,于是啟動點亮 LED2事件,事件會調用相應的hal 層API點亮LED2。
什么是 MSG 消息
MSG 是比 EVENT 事件更具體并且可以攜帶數據的一種通信方式,MSG 的標記是按數值,而不是按位。比如 0x01 和 0x02 是兩個不同的消息,但對于事件 0x03 則是 0x01 事件和 0x02 事件的組合。MSG 收發使用 osal_msg_send()和 osal_msg_receive(); 當調用 osal_msg_send()發送一個 msg 的同時會在 EVENT 列表中觸發一個 message ready event。 (請注意最后一句話,這句話點出了為什么按鍵時間的觸發為何會導致系統事件也接受到了)
現在以 SimpleBLEPeripheral 為例說明按鍵流程
在 SimpleBLEPeripheral 任務初始化函數中有這樣一條代碼:
??// Register for all key events - This app will handle all key events
??RegisterForKeys( simpleBLEPeripheral_TaskID );
這個函數來自 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中賦值自己的任務 ID,調用了這個函數就能成功注冊按鍵服務,那這個全局變量在何時使用呢?
分析到這里,感覺有點迷糊了,我們可以從頂到下分析。任何一個程序都是從main函數開始的,這點我們要堅信。所以我們首先找到這個main函數
打開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);
??}
}
看到我上面標注的函數了吧?那個是一個按鍵回調服務注冊函數,注冊了一個 OnBoard_KeyCallback函數
HalKeyConfig 函數的實現:
將上述的回調函數的地址復制給了函數指針變量。通過跟蹤發現該函數的指針變量在按鍵的輪詢函數中調用了,如下圖:
/**************************************************************************************************
* @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);
??}
}
在這里底層的按鍵查詢函數調用一個函數指針,而非具體的函數,這樣就將處理按鍵的接口留給了上層,上層應用中,叧需解析的函數指針傳入的參數 1:keys 就知道是哪個按鍵被按下了。
我們再回到剛才的 OnBoard_KeyCallback 回調函數處,該回調函數代碼如下:
/*********************************************************************
* @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? )??//就是這句話將按鍵消息上傳到應用層去處理的
??{
? ? // 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);
? ? }
??}
}
進入按鍵消息發送函數中可以看到
/*********************************************************************
* @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 );
}
主要是將按鍵時間,狀態和按鍵值打包到信息中最后通過?osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );發送到注冊了按鍵服務的應用層去?,最終用戶按了哪個按鍵,如何響應該按鍵在系統事件 SYS_EVENT_MSG 中處理。疑問又來了,為什么通過 osal_msg_send 收發的消息會出現在 SYS_EVENT_MSG 中呢?這個疑問,就是剛才我說過的osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr )會產生一個?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:? ?? ?? ?? ?? ?? ?//按鍵處理事件,用戶產生的按鍵都是在這里處理? ??
? ?? ?simpleBLEPeripheral_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );
? ?? ?break;
??#endif // #if defined( CC2540_MINIDK )
??default:
? ? // do nothing
? ? break;
??}
}
在 SimpleBLEPeripheral 中,對按鍵的響應如下:joystick right(SW2)收發廣播的開啟和關閉
/*********************************************************************
* @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, ¤t_adv_enabled_status ); //這個函數主要就是獲得廣播的開啟和關閉狀態
? ?? ?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 )
現在按鍵基本分析完了,其實CC2540學習板子是默認開啟廣播,但是Keyfob就需要按鍵手動開啟了。接下來我會繼續分析協議棧的其他功能,如有不足之外,望補充~~~~~~~~~~(花了我幾個小時寫出來,蛋疼。寫文章太費勁了)
轉載自:http://bbs.eeworld.com.cn/forum.php?mod=viewthread&tid=428987
總結
以上是生活随笔為你收集整理的TI BLE协议栈 按键流程分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 逻辑分析仪使用方法
- 下一篇: RTX操作系统库方式移植