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层。 图 11 通用化NXP无线连接软件架构示意图
1.1.1 应用层应用层主要是将具体的应用,实例化各个层次的模块,调用各模块的API实现最终的应用。包括有如下组件见图。本章重点讲述应用层
图 12 BLE应用软件应用层结构
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)代码来实现框架中各个组件的管理。
框架层主要包括穿行通讯管理器、系统错误/系统重启/系统定时器、随机数生成器、闪存管理器、任务管理器、消息管理器、非易失性存储管理器等部件。
图 14 框架层各部件结构示意图
关于框架层的实现,将采用另外一章来讲述。
1.1.4 硬件SDK层硬件底层操作的软件代码简称为KSDK(Kinetic SDK),是Kinetic系列MCU家族的通用对芯片内部各模块进行操作的库函数。在软件项目层次中通常是采用另建一个工程编译成库之后,跟应用软件进行链接组成最终的可执行代码。包含了操作系统虚拟层OSA和平台模块层(platform),具体platform有包括了各类硬件模块的操作函数API等,如下图。
图 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)。
图 16 应用初始化代码流程图
1.2.2 Main函数创建OSA启动任务startup_task/main_task图 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回调函数完成确认并进行相应的下一步操作。此部分消息在示意图为
图 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函数处理各硬件控制的事件和消息