1. 程式人生 > >1 NXP的BLE協議棧軟體架構與應用層程式碼分析

1 NXP的BLE協議棧軟體架構與應用層程式碼分析

1 NXP的BLE協議棧軟體架構與應用層程式碼分析

本章介紹了BLE協議棧軟體架構,並重點分析了應用層的軟體程式碼。

1.1. BLE協議棧軟體架構

本文件學習KW40Z的BLE軟體開發採用流行的IAR嵌入式開發軟體。開啟frdm-kw40z-demo.eww工程專案檔案,對比BLE協議棧結構與NXP的BLE-Demo-Software工程目錄層次架構如圖。

從例程的工程檔案結構可以看出KW40Z的BLE軟體架構跟標準的BLE協議棧架構是一一對應的,包括如下層:應用層(App)、藍芽協議棧層(bluetooth,包括Host(在下圖中用一般性的通用的協議棧表示為物理層PHY,中介層MAC,網路層NWK)、以及NXP的通用連線的框架層(framework)、硬體SDK層(KSDK),作業系統虛擬層和具體的作業系統RTOS層。

wps592B.tmp

11 通用化NXP無線連線軟體架構示意圖

1.1.1 應用層

應用層主要是將具體的應用,例項化各個層次的模組,呼叫各模組的API實現最終的應用。包括有如下元件見圖。本章重點講述應用層

wps59C7.tmp

12  BLE應用軟體應用層結構

1.1.2 協議棧層

BLE協議棧主要根據BLE標準規範定義的各類協議棧元件,具體細分為Controller(PHY/LL)、HCI、Host(又細分為L2CAP/ATT/GATT/GAP/SM)、Profile等,上層應用通過呼叫協議棧的各層API實現各個協議功能的具體實現。

關於BLE協議棧層,將採用另外一章來講述。

wps59D8.tmp

13  BLE協議棧架構與KW40Z的BLE軟體工程目錄層次一一對應

1.1.3 通用連線框架層

為了保證在各個不同系列的無線MCU和各種不同無線協議棧中保持軟體程式碼的可移植性,設計了通用連線框架層,把無線協議以及硬體管理等各類操作封裝成通用的API,通過API來呼叫各類不同的元件。主要通過作業系統虛擬層來呼叫底層的作業系統或者裸奔(bare-metal)程式碼來實現框架中各個元件的管理。

框架層主要包括穿行通訊管理器、系統錯誤/系統重啟/系統定時器、隨機數生成器、快閃記憶體管理器、工作管理員、訊息管理器、非易失性儲存管理器等部件。

wps59D9.tmp

14 框架層各部件結構示意圖

關於框架層的實現,將採用另外一章來講述。

1.1.4 硬體SDK層

硬體底層操作的軟體程式碼簡稱為KSDK(Kinetic SDK),是Kinetic系列MCU家族的通用對晶片內部各模組進行操作的庫函式。在軟體專案層次中通常是採用另建一個工程編譯成庫之後,跟應用軟體進行連結組成最終的可執行程式碼。包含了作業系統虛擬層OSA和平臺模組層(platform),具體platform有包括了各類硬體模組的操作函式API等,如下圖。

wps59EA.tmp

15  KSDK個部件結構示意圖

關於KSDK中各個晶片模組的功能與API,將採用另外一章來講述。

1.1.5 RTOS層

RTOS主要為上層協議棧、框架、應用等元件提供具體的作業系統服務,實現了作業系統抽象層OSA要求的各類系統服務如任務建立和排程之星、訊息傳遞、中斷註冊等,實際可以採用FreeRTOS、uCOS-II、裸奔(bare-metal)程式碼等。

關於RTOS及其適配OSA層的實現,將採用另外一章來講述。

1.2. 應用層程式碼結構分析

應用層程式碼分析最好的方式就是從復位後的第一條程式碼開始跟蹤程式碼的執行過程,一遍一遍看如何初始化、呼叫API例項化軟體架構各元件,因此在IAR中設定Debug的取消run to main功能,並啟動除錯。

提示:當除錯時找不到某個.c檔案是,如果提示這個.c檔案是是編譯的KSDK庫裡面的,那麼可以重新rebuild KSDK庫,這樣把原來的庫中的c檔案路徑資訊替換為本機安裝KSDK的路徑資訊,這樣就可以找到.c檔案。

1.2.1 應用初始化

眾所周知,ARM系列晶片的第一條指令是Reset_Handler,應用初始化在Reset_Handler之後進行,一直到進入main函式。其流程如下,基本註釋都能看明白,主要實現了SystemInit裡面的時鐘初始化,然後將資料初始化比如完成一些變數的初始化賦值(從ROM複製到RAM)。

wps59EB.tmp

16 應用初始化程式碼流程圖

1.2.2 Main函式建立OSA啟動任務startup_task/main_task

wps59EC.tmp

17  main函式程式碼流程圖

Main函式很簡單:呼叫OSA服務建立名為“main”,函式為startup_task的任務並執行。

而startup_task函式僅僅呼叫main_task函式後進入死迴圈,由於main_task函式之後一直在等待EVENT並處理的死迴圈不返回,因此main_task之後即為整個main函式的終點。

1.2.3 main_task初始化所有元件

main_task則執行了呼叫硬體平臺初始化

        platformInitialized = 1;

        hardware_init();

框架初始化,包括記憶體MEM,定時器TIM、LED、安全庫SecLib、隨機數生成器RNG、鍵盤KBD、非易失性儲存NV、BLE中斷服務例程註冊BLE_SignalFromISRCallback、電源管理PWR。框架層的初始化和API詳見框架層章節。

/* Framework init */

        MEM_Init();

        TMR_Init();

        LED_Init();

        SecLib_Init();

        RNG_Init();

        RNG_GetRandomNo((uint32_t*)(&(pseudoRNGSeed[0])));

        RNG_GetRandomNo((uint32_t*)(&(pseudoRNGSeed[4])));

        RNG_GetRandomNo((uint32_t*)(&(pseudoRNGSeed[8])));

        RNG_GetRandomNo((uint32_t*)(&(pseudoRNGSeed[12])));

        RNG_GetRandomNo((uint32_t*)(&(pseudoRNGSeed[16])));

        RNG_SetPseudoRandomNoSeed(pseudoRNGSeed);

        KBD_Init(App_KeyboardCallBack);

#if mAppUseNvm_d       

/* Initialize NV module */

        NV_Init();

/* NV_STORAGE_END_ADDRESS from linker file is used as NV Start Address */

        gNvmStartAddress_c = (uint32_t)((uint8_t*)NV_STORAGE_END_ADDRESS);

#endif

        pfBLE_SignalFromISR = BLE_SignalFromISRCallback;

#if (cPWR_UsePowerDownMode)

        AppIdle_TaskInit();

        PWR_Init();

        PWR_DisallowDeviceToSleep();

#endif   

接著完成BLE的App、收發器Xcvr的初始化,同時建立App的事件mAppEvent,初始化BLE-Host協議棧傳遞給App的訊息佇列,最後將App_GenericCallback註冊到Ble協議棧。

/* Initialize peripheral drivers specific to the application */

        BleApp_Init();

/* BLE Radio Init */

        XcvrInit(BLE);

/* Create application event */

        status = OSA_EventCreate(&mAppEvent, kEventAutoClear);

if( kStatus_OSA_Success != status )

{

            panic(0,0,0,0);

return;

}

/* Prepare application input queue.*/

        MSG_InitQueue(&mHostAppInputQueue);

/* BLE Host Stack Init */

if (Ble_Initialize(App_GenericCallback) != gBleSuccess_c)

{

            panic(0,0,0,0);

return;

}

Demo-Software使用了LED、按鍵KBD、加速度、磁感測器、電位器和紅外遙控等硬體外設,由於Framework僅提供了LED、KBD的元件,未提供加速度、磁感測器等元件,因此必須在應用層上實現這些服務於App的服務任務,在main_task裡面實現該服務的事件和任務的建立。

/* Create service application event */

        status = OSA_EventCreate(&svcAppEvent, kEventAutoClear);

if( kStatus_OSA_Success != status )

{

            panic(0,0,0,0);

return;

}

/* Create service application task */

        status = OSA_TaskCreate((task_t)service_application_task,

"svc_app_task",

        gServiceApplicationTaskStackSize,

        service_application_task_stack,

        gServiceApplicationTaskPriority,

(task_param_t)NULL,

        FALSE,

&serviceApplicationTaskHandler);

1.3. App_Thread實現對BLE協議棧傳送過來的事件的處理

完成初始化後,進入App_Thread函式。該函式不停地對mAppEvent進行解析,如果出現gAppEvtMsgFromHostStack_c事件(該事件由BLE協議棧各模組GAP、GATT、SM等元件的回撥函式callback傳送),則對訊息佇列mHostAppInputQueue查詢是否有pending待處理的訊息,如果有則呼叫App_HandleHostMessageInput進行處理後釋放MSG_Free掉。。

/*! *********************************************************************************

* \brief  This function represents the Application task.

*         This task reuses the stack alocated for the MainThread.

*         This function is called to process all events for the task. Events

*         include timers, messages and any other user defined events.

* \param[in]  argument

*

********************************************************************************** */

void App_Thread (uint32_t param)

{

    event_flags_t event;

while(1)

{

        OSA_EventWait(&mAppEvent, 0x00FFFFFF, FALSE, OSA_WAIT_FOREVER, &event);

/* Dequeue the host to app message */

if (event & gAppEvtMsgFromHostStack_c)

{

/* Pointer for storing the messages from host. */

            appMsgFromHost_t *pMsgIn = NULL;

/* Check for existing messages in queue */

while(MSG_Pending(&mHostAppInputQueue))

{

                pMsgIn = MSG_DeQueue(&mHostAppInputQueue);

if (pMsgIn)

{

/* Process it */

                    App_HandleHostMessageInput(pMsgIn);

/* Messages must always be freed. */

                    MSG_Free(pMsgIn);

                    pMsgIn = NULL;

}

}

}

/* For BareMetal break the while(1) after 1 run */

if( gUseRtos_c == 0 )

{

break;

}

}

}

1.3.1 App_HandleHostMessageInput實現BLE協議棧的訊息處理

由於無線通訊的設定、傳輸都比較消耗時間,因此協議棧必須具有非同步特性,即完成API呼叫後只設置某些事件、訊息或者訊號量等執行緒間通訊後立即返回,而後在協議棧的任務中對這些執行緒間通訊進行解析並處理,完成處理後再返回事件、訊息或者訊號量給上層應用,上層應用通過callback回撥函式完成確認並進行相應的下一步操作。此部分訊息在示意圖為

wps5A0C.tmp

18  App_HandleHostMessageInput函式處理BLE協議棧的事件和訊息

1.4. service_application_task實現加速度、磁感測器、電位器和紅外遙控功能

在IAR的IDE中Find All Reference to svcAppEvent,可以看到有四個執行緒傳送不同的事件到svcAppEvent裡面。

/* User function to signal Accelerometer reading */

extern void App_AccelSignalAppThread(bool_t isSensitivityUpdateEvent, uint8_t newSensitivityValue)

{

if(isSensitivityUpdateEvent){

//Create update message

uint8_t* pMsgIn = NULL;

    pMsgIn = MSG_Alloc(sizeof(uint8_t));

if(pMsgIn == NULL)

return;

*pMsgIn = newSensitivityValue;

    MSG_Queue(&mAccelerometerMessage, pMsgIn);

}

  OSA_EventSet(&svcAppEvent, gAppEvtAccelerometerDataReady_c);

}

/* User function to signal E-Compass Heading calculation */

extern void App_CompassSignalAppThread (void){

  OSA_EventSet(&svcAppEvent, gAppEvtCalculateCompassHeading_c);

}

/* User function to signal Potentiometer update */

extern void App_PotentiometerSignalAppThread (void){

  OSA_EventSet(&svcAppEvent, gAppEvtPotentiometerUpdateTimeout_c);

}

/* User function to signal IR Controller task execution */

extern void App_IrControllerSignalAppThread (void){

  OSA_EventSet(&svcAppEvent, gAppEvtExecuteIrControllerTask_c);

}

這些執行緒分別是對應讀取/控制加速度、磁場、電位器和紅外控制的狀態或定時器變化,通過svcAppEvent這個事件傳遞給service_application_task,service_application_task實現了對svcAppEvent事件的處理並呼叫相應的硬體層API來完成最終的讀取/控制,並傳遞給使用者應用層程式碼來更新BLE的資料,最終通過BLE連線反饋給手機App。

wps5A1C.tmp

18  service_application_task函式處理各硬體控制的事件和訊息