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層。
1.1.1 應用層
應用層主要是將具體的應用,實例化各個層次的模塊,調用各模塊的API實現最終的應用。包括有如下組件見圖。本章重點講述應用層
1.1.2 協議棧層
BLE協議棧主要根據BLE標准規范定義的各類協議棧組件,具體細分為Controller(PHY/LL)、HCI、Host(又細分為L2CAP/ATT/GATT/GAP/SM)、Profile等,上層應用通過調用協議棧的各層API實現各個協議功能的具體實現。
關於BLE協議棧層,將采用另外一章來講述。
圖 13 BLE協議棧架構與KW40Z的BLE軟件工程目錄層次一一對應
1.1.3 通用連接框架層
為了保證在各個不同系列的無線MCU和各種不同無線協議棧中保持軟件代碼的可移植性,設計了通用連接框架層,把無線協議以及硬件管理等各類操作封裝成通用的API,通過API來調用各類不同的組件。主要通過操作系統虛擬層來調用底層的操作系統或者裸奔(bare-metal)代碼來實現框架中各個組件的管理。
框架層主要包括穿行通訊管理器、系統錯誤/系統重啟/系統定時器、隨機數生成器、閃存管理器、任務管理器、消息管理器、非易失性存儲管理器等部件。
關於框架層的實現,將采用另外一章來講述。
1.1.4 硬件SDK層
硬件底層操作的軟件代碼簡稱為KSDK(Kinetic SDK),是Kinetic系列MCU家族的通用對芯片內部各模塊進行操作的庫函數。在軟件項目層次中通常是采用另建一個工程編譯成庫之后,跟應用軟件進行鏈接組成最終的可執行代碼。包含了操作系統虛擬層OSA和平台模塊層(platform),具體platform有包括了各類硬件模塊的操作函數API等,如下圖。
關於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)。
1.2.2 Main函數創建OSA啟動任務startup_task/main_task
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回調函數完成確認並進行相應的下一步操作。此部分消息在示意圖為
圖 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。
圖 18 service_application_task函數處理各硬件控制的事件和消息