内容发布更新时间 : 2024/12/24 1:18:36星期一 下面是文章的全部内容请认真阅读。
4.3 通用应用架构
这一章将描述一个应用任务的创建的更多的细节 4.3.1 应用初始化函数
第3.3章描述了一个任务是如何构建的。在任务被构建之后,SYS/BIOS内核调度进程就开始,在任务构建过程通过的函数会在任务就绪时运行(比如SimpleBLEperipheral_taskFxn)。该函数第一件应该做的事就是低啊用一个应用初始化函数。举例说明,在SimpleBLEPerpheral.c中:
static void SimpleBLEPeripheral_taskFxn(UArg a0, UArg a1) {
// Initialize application
SimpleBLEPeripheral_init(); // Application main loop for (;;) { …
这个初始化函数,SimpleBLEPeripheral_init(),配置了数个服务给任务同时也设置系列硬件和软件的配置设置和参数。一些共同的例子就是:初始化GATT客户端,注册各种Profile中的回调函数,设置GAPRole,设置绑定管理,设置GAP层,配置硬件模块如LCD,SPI等。在该指导中查看相关章节获取这些例子的更多信息。
注意:在应用初始化函数中,ICall_registerApp()必须在其他栈接口调用之前调用。 4.3.2 任务函数中的事件进程。
在初始化函数之后,像上面代码中展示的一样,任务函数会进入一个无限循环中,它将会保持阻塞并等待一个信号旗发送一个新的原因给进程:
ICall_Errno errno = ICall_wait(ICALL_TIMEOUT_FOREVER); if (errno == ICALL_ERRNO_SUCCESS) { ...
一旦一个事件或其他的刺激发生并处理,任务就会再次进入顶戴信号旗并保持阻塞状态知道其他的原因去执行。请看下面的逻辑图:
如上图所示,这里有多种理由可以导致信号旗通过并且使任务激活执行,他们包括: 4.3.2.1 内部任务消息
5 6 7 8 9
if (ICall_fetchServiceMsg(&src, &dest, (void **)&pMsg) == ICALL_ERRNO_SUCCESS) {
if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity)) {
10 // Process inter-task message
11 SimpleBLEPeripheral_processStackMsg((ICall_Hdr *)pMsg); 12 } 13 if (pMsg) 14 {
15 ICall_freeMsg(pMsg);
16 }
这些是其他任务(如BLE协议栈)通过ICall发送给应用的消息。在SimpleBLEPeripheral工程中没有这样的例子。其他可能的例子有: 1. 一个确定的发送自协议栈确认的空中指示
2. 一个发送自协议栈的、连接事件和广播结束的通知。
3. 一个回复给GATT客户端的操作。看5.3.3章有这方面的例子。 4.3.2.1.1 内部任务事件
在一些少数的案例中,BLE协议栈将会通过ICall在应用任务中设置一个事件。这样的例子就
是当HCI_EXT_AdvEventNoticeCmd()调用时。在这些案例中,新增的代码需添加到if语句中目的是为了与BLE协议栈事件和消息作出区别。这一点将会在本文档的任何需要的地方都会明确的提示。这样添加检查的例子就是GAPRole任务的任务函数:
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 &GAP_EVENT_SIGN_COUNTER_CHANGED) {
// Sign counter changed, save it to NV
VOID osal_snv_write(BLE_NVID_SIGNCOUNTER, sizeof(uint32_t), &gapRole_signCounter);
} } else {
// Process inter-task message
gapRole_processStackMsg((ICall_Hdr *)pMsg); } }
if (pMsg)
{
ICall_freeMsg(pMsg);
} }
注意上面的代码,如果事件来自于BLE协议栈pEvt->signature 将总是等于0xFFFF。
当为了一个内部事件选择了一个事件值时,这个值对于任务必须是唯一的并且必须是多于2的(因此只需设置一个位)。由于pEvt->event变量是以uint16_t初始化的,所以最大允许16个事件。那些已经用于BLE OSAL 全局事件的事件值是不能再用的。
/********************************************************************* * BLE OSAL GAP GLOBAL Events */
#define GAP_EVENT_SIGN_COUNTER_CHANGED 0x4000 //!< The device level sign counter changed
注意这些内部任务是一些列与4.2.3.2中提到的内部任务事件不同的事件。 4.3.2.2 发送到应用任务的RTOS队列中的消息
5. // If RTOS queue is not empty, process app message. 6. if (!Queue_empty(appMsgQueue)) 7. {
8. sbpEvt_t *pMsg = (sbpEvt_t *)Util_dequeueMsg(appMsgQueue); 9. if (pMsg)
10. {
// Process message.
SimpleBLEPeripheral_processAppMsg(pMsg); // Free the space from the message. ICall_free(pMsg); }
1. }
这些消息用SimpleBLEPeripheral_enqueueMsg()函数排序。因为它们被发送到队列中,当它们发送时会被处理。一个这方面的通用的例子就是在回调函数中接收到一个事件(阅读5.3.4.2.4章)。
4.3.2.3 通过内部事件值发送的事件
5. if (events &SBP_PERIODIC_EVT) 6. {
7. events &= ~SBP_PERIODIC_EVT; 8. Util_startClock(&periodicClock); 9. // Perform periodic application task 10. SimpleBLEPeripheral_performPeriodicTask(); 11. }
这些发送给应用任务的同步事件是通过设置应用任务的events的值的相关位实现的,每个位对应着一个定义好的事件,比如:
// Internal Events for RTOS application #define SBP_STATE_CHANGE_EVT 0x0001 #define SBP_CHAR_CHANGE_EVT 0x0002 #define SBP_PERIODIC_EVT 0x0004
函数在设置这些事件值的位时必须确保发送给信号旗来激活应用来执行。这方面的例子就是时钟处理函数用来处理时钟的超时。这部分的描述在3.4.2中。
注意当添加一个事件时,对于给定的任务其值必须是唯一的并且必须多于2(这样就可以只有一个位可以设置)。由于events变量是按照uint16_t初始化因此最多可以定义16个内部事件。 4.3.3 回调
应用代码同样也包含了各种给协议层、profile和RTOS模块的回调函数。为了确保线程的安全,在一个确切的回调函数中应该进行最小的处理,大量的处理应该在应用的上下文中完成。因此每个回调函数通常都对应两个函数(参考GAPRole状态改变回调函数): 1. 确切的回调函数:这个函数在调用函数的上下文中调用(如GAPRole任务)。为了在调
用上下文中进行最小的处理,所有这个函数的应该将一个事件排列到应用队列中进行处理。
static void SimpleBLEPeripheral_stateChangeCB(gaprole_States_t newState) {
SimpleBLEPeripheral_enqueueMsg(SBP_STATE_CHANGE_EVT, newState); }
2. 在应用上下文中进行的函数:当应用被回调函数的排序唤醒后,这个函数就会在事件通
过应用队列发送和处理之后被调用。
static void SimpleBLEPeripheral_processStateChangeEvt(gaprole_States_t newState) {