BLE应用基础架构
1 程序启动
初始化GATT客户端
各种配置文件中注册回调
设置GAPRole
设置Bond Manager
设置GAP层配置硬件模块,如LCD、SPI
初始化函数之后,任务函数进入无限循环,以便它作为独立任务连续处理,并且不会运行到完成。
2 任务函数中的事件处理
在前面的代码片段中显示的初始化函数之后,任务函数进入无限循环,以便它作为独立任务连续处理,并且不会运行到完成。在这个无限循环中,任务保持阻塞状态,并等待信号量发出新的处理原因:
当事件或其他刺激发生并被处理时,任务等待信号量并保持阻塞状态,直到有其他原因需要处理。
2.1 任务事件
当蓝牙低功耗协议栈通过ICall在应用程序任务中设置事件时被设置。
简单外设的任务函数中显示了一个表示连接事件结束的任务事件的示例:
if (ICall_fetchServiceMsg(&src, &dest, (void **)&pMsg) == ICALL_ERRNO_SUCCESS)
{
if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
{
ICall_Event *pEvt = (ICall_Event *)pMsg;
// Check for BLE stack events first
if (pEvt->signature == 0xffff)
{
if (pEvt->event_flag & SBP_CONN_EVT_END_EVT)
{
// Try to retransmit pending ATT response (if any)
SimpleBLEPeripheral_sendATTRsp();
}
... }
if (pMsg) {
ICall_freeMsg(pMsg);
}
}
2.2 任务间消息
这些消息通过ICall从另一个任务(例如蓝牙低能耗协议栈)传递到应用程序任务。一些可能的示例如下:
•从协议栈发送确认,以确认成功的OTA指示
•与HCI命令相对应的事件
•对GATT客户操作的响应
下面是simple_peripheral的主任务循环中的一个示例。
if (ICall_fetchServiceMsg(&src, &dest,
(void **)&pMsg) == ICALL_ERRNO_SUCCESS)
{
uint8 safeToDealloc = TRUE;
if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
{
ICall_Event *pEvt = (ICall_Event *)pMsg;
...
else
{
// Process inter-task message
safeToDealloc = SimpleBLEPeripheral_processStackMsg((ICall_Hdr *)pMsg);
}
}
if (pMsg && safeToDealloc)
{
ICall_freeMsg(pMsg);
}
}
2.3 程序任务队列中的消息
这些消息已使用simple_peripheral_enqueueMsg()函数排队。因为这些消息被发布到队列中,所以按照它们发生的顺序进行处理。这方面的一个常见示例是回调函数中接收的事件。
// If RTOS queue is not empty, process app message.
if (!Queue_empty(appMsgQueue))
{
sbpEvt_t *pMsg = (sbpEvt_t *)Util_dequeueMsg(appMsgQueue);
if (pMsg)
{
// Process message.
SimpleBLEPeripheral_processAppMsg(pMsg);
// Free the space from the message.
ICall_free(pMsg);
}
}
2.4 通过内部事件变量引起的事件
通过在应用程序任务的事件变量中设置适当的位,将这些异步事件发送给应用程序任务进行处理,其中每个位对应于定义的事件。
// Internal Events for RTOS application
#define SBP_STATE_CHANGE_EVT 0x0001
#define SBP_CHAR_CHANGE_EVT 0x0002
#define SBP_PERIODIC_EVT 0x0004
在事件变量中设置此位的函数还必须发送到信号量,以唤醒应用程序进行处理。此过程的一个示例是处理时钟超时的时钟处理程序。以下是从simple_peripheral的主任务函数处理周期性事件的示例:
if (events & SBP_PERIODIC_EVT)
{
events &= ~SBP_PERIODIC_EVT;
Util_startClock(&periodicClock);
// Perform periodic application task
SimpleBLEPeripheral_performPeriodicTask();
}
2.5 使用TI-RTOS事件模块发出的事件
要开始使用事件,应用程序须包含RTOS中的事件模块。
#include <ti/sysbios/knl/Event.h>
与上一节采用的位域方法类似,应用程序可以定义要向RTOS注册的自定义事件;使用预定义的RTOS事件而不是手动分配位字段。对于ARM设备(如CC2640),最多可使用32个事件。下面的代码片段显示了simple_ap中定义的事件。
#define AP_NONE Event_Id_NONE // No Event
#define AP_EVT_PUI Event_Id_00 // Power-Up Indication
#define AP_EVT_ADV_ENB Event_Id_01 // Advertisement Enable
与信号量方法类似,应用程序可以依赖于事件,允许在事件发生之前阻止任务的执行。请参阅下面simple_ap中的代码片段。
flags = Event_pend(event, Event_Id_NONE, orFlags + andFlags + SNP_ALL_EVENTS, timeout);
使用事件模块的andFlags和orFlags,用户可以将挂起调用配置为等待特定的事件组合发生。当某个事件发生时,应用程序可以发布该事件,例如下面的simple_ap示例。
Event_post(apEvent, AP_EVT_BUTTON_LEFT);
3 回调函数
应用程序代码还包括对协议堆栈层、配置文件和RTOS模块的各种回调。为了确保线程安全,必须在实际回调中最小化处理,并且大部分处理应该发生在应用程序上下文中。每个回调定义了两个函数(考虑GAPRole状态更改回调):
实际回调:在调用任务或模块(例如GAPRole任务)的上下文中调用此函数。为了最小化调用上下文中的处理,此函数应将事件排入应用程序队列进行处理。
static void SimpleBLEPeripheral_stateChangeCB(gaprole_States_t newState)
{
SimpleBLEPeripheral_enqueueMsg(SBP_STATE_CHANGE_EVT, newState);
}
要在应用程序上下文中处理的函数:当应用程序由于回调的排队而唤醒时,当事件从应用程序队列中弹出并处理时,将调用此函数。
static void SimpleBLEPeripheral_processStateChangeEvt(gaprole_States_t newState)
{
... }