第十五節 BLE藍牙4.0協議棧啟動分析
TI的這款CC2540/CC2541器件可以單芯片實現BLE藍牙協議棧結構圖的所有組件,包括應用程序。從這章開始我們來剖析協議棧源碼,我們選用 SimpleBLEPeripheral工程開刀,這是一個從機的例程,基本的工作是對外廣播,等待主機來連接,讀寫展示的屬性。
首先打開工程文件,打開后可以看到整個工程的結構。
我們按照系統的啟動順序來一步一步走,我們都知道在C代碼中,一般啟動的首個函數為main,這個函數在 SimpleBLEPeripheral_Main.c中,打開文件,可以看到這個文件只有一個main函數和一個函數的申明,我們暫時不理會那個申明的函數,先看main都做了些什么工作:
Int main(void)
{
/* Initialize hardware */
HAL_BOARD_INIT(); // 硬件初始化
// Initialize board I/O
InitBoard( OB_COLD ); // 板級初始化
/* Initialze the HAL driver */
HalDriverInit(); // Hal驅動初始化
/* Initialize NV system */
osal_snv_init(); // Flash存儲SNV初始化
/* Initialize LL */
/* Initialize the operating system */
osal_init_system(); // OSAL初始化
/* 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 啟動OSAL
return 0;
}
通過代碼我們可以看到,系統啟動的過程,主要是做了一些初始化,如果開啟了低功耗,則還需要開啟低功耗管理。我們先不去理會初始化做了什么,但是我們知道在main函數的最后啟動了OSAL,那么我們就進去看看OSAL是如何運作的。
在IAR中如果需要跳轉到某個函數或變量的定義,可以在此函數名中右擊然后選擇Go To Definition……就可以調到相應的定義。
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop
#endif
{
osal_run_system();
}
}
這里看到我們進入了一個死循環,并且一直調用osal_run_system(),那我們再進入此函數。
void osal_run_system( void ){
uint8 idx = 0;
#ifndef HAL_BOARD_CC2538
osalTimeUpdate(); // 定時器更新
#endif
Hal_ProcessPoll(); // Hal層信息處理
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;
}
} while (++idx < tasksCnt); // 檢查每個人任務是否有事件
if (idx < tasksCnt) // 有事件發生
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // 進入臨界區
events = tasksEvents[idx];
tasksEvents[idx] = 0; // Clear the Events for this task. 清除事件標志
HAL_EXIT_CRITICAL_SECTION(intState); // 退出臨界區
activeTaskID = idx;
events = (tasksArr[idx])( idx, events ); // 執行事件處理函數
activeTaskID = TASK_NO_TASK;
HAL_ENTER_CRITICAL_SECTION(intState); // 進入臨界區
tasksEvents[idx] |= events; // Add back unprocessed events to the current task.
HAL_EXIT_CRITICAL_SECTION(intState); // 退出臨界區
}
#if defined( POWER_SAVING ) // 沒有事件發生,并且開啟了低功耗模式
else // Complete pass through all task events with no activity?
{ // 系統進入低功耗模式
osal_pwrmgr_powerconserve(); // Put the processor/system into sleep
}
#endif
/* Yield in case cooperative scheduling is being used. */
#if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0)
{
osal_task_yield();
}
#endif
}
在這里可以看到這個OSAL的核心,整個OSAL通過檢測每個任務是否有事件發生,如果有則執行相應的任務,處理相應的事件。如果沒有事件需要處理并且開啟了低功耗模式,則系統就會進入低功耗模式。
這里有一個很關鍵的地方,OSAL是如何知道哪個事件需要哪個任務來處理呢?
events = (tasksArr[idx])( idx, events ); // 執行事件處理函數
我們看這里有一個很關鍵的數組tasksArr,很顯然,這是一個函數指針數組,我們看看它的定義。
const pTaskEventHandlerFn tasksArr[] =
{
LL_ProcessEvent, // task 0
Hal_ProcessEvent, // task 1
HCI_ProcessEvent, // task 2
#if defined ( OSAL_CBTIMER_NUM_TASKS )
OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ), // task 3
#endif
L2CAP_ProcessEvent, // task 4
GAP_ProcessEvent, // task 5
GATT_ProcessEvent, // task 6
SM_ProcessEvent, // task 7
GAPRole_ProcessEvent, // task 8
GAPBondMgr_ProcessEvent, // task 9
GATTServApp_ProcessEvent, // task 10
SimpleBLEPeripheral_ProcessEvent // task 11
};
可以看到在這個數組的定義中,每個成員都是任務的執行函數,按照任務的優先級排序,并且在osalInitTasks中初始化的時候,我們可以看到每個任務都有一個對應的初始化函數,并且傳遞了一個taskID,此ID從0開始自增,這里有一點非常重要,初始化的順序和任務數組的定義順序是一樣的,這就保證了我們給任務發生消息或事件時能夠準確的傳遞到相應的任務處理函數。
void osalInitTasks( void )
{
uint8 taskID = 0;
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
/* LL Task */
LL_Init( taskID++ );
/* Hal Task */
Hal_Init( taskID++ );
/* HCI Task */
HCI_Init( taskID++ );
#if defined ( OSAL_CBTIMER_NUM_TASKS )
/* Callback Timer Tasks */
osal_CbTimerInit( taskID );
taskID += OSAL_CBTIMER_NUM_TASKS;
#endif
/* L2CAP Task */
L2CAP_Init( taskID++ );
/* GAP Task */
GAP_Init( taskID++ );
/* GATT Task */
GATT_Init( taskID++ );
/* SM Task */
SM_Init( taskID++ );
/* Profiles */
GAPRole_Init( taskID++ );
GAPBondMgr_Init( taskID++ );
GATTServApp_Init( taskID++ );
/* Application */
SimpleBLEPeripheral_Init( taskID );
}
應用層的初始化SimpleBLEPeripheral_Init,SimpleBLEPeripheral_Init( uint8task_id )主要對 GAP 和 GATT 進行配置,最后調用osal_set_event(simpleBLEPeripheral_TaskID, SBP_START_DEVICE_EVT )啟動設備。
設備啟動后應用層就能接收到這個設置的事件并進行處理,可以看到設備啟動中主要是啟動設備,注冊綁定管理,并且啟動了一個定時器,這個定時器是一個周期事件的第一次啟動。
周期事件中每次都會重啟這個定時器,并且處理周期事件。
在初始化的時候我們注冊了一個很重要的函數,設備狀態改變時的回調函數,這個函數在設備的狀態改變時會被底層的協議棧回調,我們可以從這個回調函數中看的設備的狀態的改變。
static void peripheralStateNotificationCB( gaprole_States_t newState);
從函數的定義可以看出,設備的狀態類型都在數據類型gaprole_States_t中定義了,我們看一下這個數據類型的定義:
typedef enum
{
GAPROLE_INIT = 0, //!< Waiting to be started
GAPROLE_STARTED, //!< Started but not advertising
GAPROLE_ADVERTISING, //!< Currently Advertising
GAPROLE_WAITING, //!< Device is started but not advertising, is in waiting period before advertising again
GAPROLE_WAITING_AFTER_TIMEOUT, //!< Device just timed out from a connection but is not yet advertising, is in waiting period before advertising again
GAPROLE_CONNECTED, //!< In a connection
GAPROLE_CONNECTED_ADV, //!< In a connection + advertising
GAPROLE_ERROR //!< Error occurred - invalid state
} gaprole_States_t;
看到這個定義就很明確了,設備的狀態就在這幾種狀態間切換。
本文導航
- 第 1 頁:由淺入深,藍牙4.0/BLE協議棧開發攻略大全(3)
- 第 2 頁:第十二節 Flash的讀寫
- 第 3 頁:第十三節 BLE協議棧簡介
- 第 4 頁:第十四節 OSAL工作原理
- 第 5 頁:第十五節 BLE藍牙4.0協議棧啟動分析
- TI公司(73141)
- 協議棧(33351)
- 藍牙BLE(23960)
評論
查看更多