这里会显示出您选择的修订版和当前版本之间的差别。
cc2640r2f:application_architecture [2017/08/31 16:30] 127.0.0.1 外部编辑 |
cc2640r2f:application_architecture [2021/06/22 23:14] |
||
---|---|---|---|
行 1: | 行 1: | ||
- | < | ||
- | # 应用程序 # | ||
- | 从这个章节开始,我们将详细讲解CC2640R2F BLE5.0的应用程序框架,在之前我们希望已经按照我们学习线路图储备了< | ||
- | |||
- | ![](http:// | ||
- | 这里介绍以 simple_peripheral Demo应用程序部分,包括以下内容: | ||
- | |||
- | * Pre-main initialization | ||
- | * ICall | ||
- | * Simple Peripheral Task | ||
- | * Intertask Messages | ||
- | |||
- | > **注意:** GAPRoleTask也是工程的一部分,但是我们将它放在协议栈部分进行讨论,其功能与协议栈密切相关。 | ||
- | |||
- | ## Pre-main initialization ## | ||
- | `main`函数包含在IDE Startup 文件夹的资源文件main.c中。作为程序的入口,其主要完成 全局中断禁止、外设驱动初始化、电源管理,TI-RTOS任务创建或构造,启用SYS / BIOS内核调度时完成全局中断使能。main函数不返回,将在整个项目生命周期内保留其资源; | ||
- | |||
- | | ||
- | ```C | ||
- | int main() | ||
- | { | ||
- | /* Register Application callback to trap asserts raised in the Stack */ | ||
- | RegisterAssertCback(AssertHandler); | ||
- | |||
- | PIN_init(BoardGpioInitTable); | ||
- | |||
- | #ifndef POWER_SAVING | ||
- | /* Set constraints for Standby, powerdown and idle mode */ | ||
- | Power_setConstraint(PowerCC26XX_SB_DISALLOW); | ||
- | Power_setConstraint(PowerCC26XX_IDLE_PD_DISALLOW); | ||
- | #endif // POWER_SAVING | ||
- | |||
- | /* Initialize ICall module */ | ||
- | ICall_init(); | ||
- | |||
- | /* Start tasks of external images - Priority 5 */ | ||
- | ICall_createRemoteTasks(); | ||
- | |||
- | /* Kick off profile - Priority 3 */ | ||
- | GAPRole_createTask(); | ||
- | |||
- | SimpleBLEPeripheral_createTask(); | ||
- | |||
- | /* enable interrupts and start SYS/BIOS */ | ||
- | BIOS_start(); | ||
- | |||
- | return 0; | ||
- | } | ||
- | ``` | ||
- | > | ||
- | ## ICALL ## | ||
- | |||
- | ### 介绍 ### | ||
- | |||
- | 在<a href=" | ||
- | |||
- | Indirect Call Framework (**ICall 消息框架**)基于TI-RTOS提供服务(例如,同步线程、消息、事件)完成BLE协议栈和应用程序在两个工程的消息交互,保证了应用程序和协议栈在统一的TI-RTOS环境中完成高效运行、相互通信和资源共享。 | ||
- | |||
- | ICall架构和的核心组件是其消息调度(dispatcher),其帮助程序在两个镜像/ | ||
- | |||
- | **ICALL**源码在应用工程(例如我们这里的simple_peripheral)的 ICALL BLE/ICALL 文件夹路径。 | ||
- | |||
- | ![图32. ICall 应用-协议栈 抽象](http:// | ||
- | |||
- | ### ICall BLE5协议栈服务端 ### | ||
- | |||
- | 如上图所示,**ICALL**实现包含一个客户端实体(例如,我们的应用程序)和一个服务器实体(即这里的BLE5.0协议栈)之间的通信。 | ||
- | |||
- | > **注意** :正确区分**ICALL**框架的CS架构和GATT服务器和客户端的结构,后者主要由BLE协议栈所定义实现。 | ||
- | |||
- | TI之所以这样设计,前面我们已经讲到: | ||
- | |||
- | * 实现应用程序和BLE协议栈的独立管理和固件更新; | ||
- | * 从传统平台(即CC254x的OSAL)移植到CC2640R2F的TI-RTOS,保持API一致性; | ||
- | |||
- | |||
- | **ICall** 作为 BLE5协议栈API的服务接口。当我们需要调用一些协议栈接口的时候,ICall模块会自动将命令分发(即调度)到BLE5协议栈,并将消息从BLE5协议栈结果回传到应用程序。 | ||
- | |||
- | |||
- | 因为ICall消息模块本身就作为是应用程序工程的一部分,应用任务可以通过函数调用方式直接访问ICall。由于BLE协议栈任务总是以最高优先级执行,但是应用程序在没有数据返回时缺处于阻塞状态,必然有些API在会在协议栈任务立即响应,应用任务只有在消息通过ICALL分发时才能唤醒处理。另外一些API却只有等待应用任务通过ICAlL(事件更新)异步返回结果。 | ||
- | |||
- | ### ICALL原语服务 ### | ||
- | |||
- | ICALl包含一系列的原语服务都被抽象成基于RTOS相关的函数接口。由于共享资源和线程之间的通信,应用程序必须使用以下ICALL原语服务功能: | ||
- | |||
- | * 消息传递和线程同步 | ||
- | * 堆分配与管理 | ||
- | |||
- | #### 消息传递和线程同步 #### | ||
- | **ICall**同协议栈的消息传递和线程同步都是是基于TI-RTOS多线程。 | ||
- | |||
- | **ICall**两个任务的消息传递发生在通过消息队列发送一个阻塞消息。发送方动态分分配一段内存,将消息的内容写入内存,然后将这段内存发送(即排队)到接收线程,然后再使用事件标志。接受任务在收到事件标志后唤醒,复制内存消息,并且处理,之后再并将这段内存块释放。 | ||
- | |||
- | 协议栈使用ICall通知和发送消息到应用程序。ICall传递这些服务消息,应用程序任务接收它然后处理。 | ||
- | |||
- | #### 堆分配与管理 #### | ||
- | |||
- | ICall为应用提供了全局堆管理的API用以动态内存分配。其堆的大小通过宏`HEAPMGR_SIZE` 配置。有关管理动态内存的更多详细信息,请参阅动态内存分配。BLE 协议栈的ICall使用此堆管理进行所有消息相关的内存管理,同样我们建议应用程序也使用这些ICall API来分配动态内存。 | ||
- | |||
- | ### ICALL初始化和注册 ### | ||
- | |||
- | 要实例化和初始化ICall服务,应用程序必须在启动TI-RTOS内核调度程序之前调用main()中的代码片段中的函数: | ||
- | |||
- | 使用ICall的必需代码。 | ||
- | ```C | ||
- | /* 初始化ICall模块 */ | ||
- | ICall _init (); | ||
- | |||
- | /* 启动协议栈任务 - 优先级 */ | ||
- | ICall _createRemoteTasks (); | ||
- | ``` | ||
- | |||
- | 调用ICall_init()初始化ICALL原语服务(例如,堆管理)和框架,调用 ICall _createRemoteTasks()创建但不启动BLE5-Stack任务。在使用ICall协议服务之前,服务器和客户端分别完成登记和注册。服务端在编译的时候就需要登记一个服务。登记函数使用一个全局的唯一标识符区分每个服务,也是作为后面通信地址。例如,BLE协议使用 `ICALL_SERVICE_CLASS_BLE`做些蓝牙协议栈ICALL的消息交互的标识。 | ||
- | |||
- | 对于服务端的登记,包含在`osal_icall_ble.c`文件 | ||
- | |||
- | ```C | ||
- | / *ICALL服务端登记* / | ||
- | ICall _enrollService (ICALL _SERVICE_CLASS_BLE ,NULL ,&entity ,&syncHandle ); | ||
- | ``` | ||
- | |||
- | 客户端在ICALL调度程序发送和/ | ||
- | |||
- | 对于使用BLE5API的客户端(例如应用程序任务),客户端必须首先向ICall注册其任务 。该注册通常发生在应用程序的任务初始化功能中。下面的代码片段是 `simple_peripheral_init` simple_peripheral.c中注册。 | ||
- | |||
- | ```C | ||
- | // | ||
- | ICall _registerApp (&selfEntity ,&syncEvent ); | ||
- | ``` | ||
- | 完成客户端注册前,需要传入结构体变量`selfEntity`和`syncEvent`, | ||
- | |||
- | > **注意**: 在客户端注册ICALL服务之前,在`ICallBLEApi.c`文件里面定义的ICALL相关API都不能调用的。 | ||
- | ### ICall线程同步 ### | ||
- | |||
- | ICALL使用TI-RTOS的事件用以线程同步而不是信号量。 | ||
- | |||
- | 为了让客户端或服务端在收到消息前都保持阻塞状态, ICall提供以下阻塞型API保持任务阻塞状态直到关联的信号量被Post: | ||
- | |||
- | ```C | ||
- | UInt Event_pend(Event_Handle handle, UInt andMask, UInt orMask, UInt32 timeout); | ||
- | ``` | ||
- | |||
- | `handle`表示是构造的Event_Handle的句柄(标识符)。`andMask`并 `orMask` 为用户选择要阻塞/ | ||
- | |||
- | `Event_pend` 函数表示阻塞当前任务等待某一事件标志位发生。 | ||
- | |||
- | 一共有32个事件标志位(int类型),这些标识位可以根据其特定用途进行定义。不过需要注意的是,ICall消息队列已经保留了特定事件标志位。 | ||
- | |||
- | 与TI-RTOS线程关联的事件由Event_post所需标志调用时发出/ | ||
- | |||
- | `Event_post`用以客户端/ | ||
- | |||
- | ```C | ||
- | void Event_post(Event_Handle handle,UInt eventMask); | ||
- | ``` | ||
- | 以上的事件句柄`handle`在服务端ICall _enrollService()和ICall _registerApp()调用后获得。 | ||
- | |||
- | > **危险**: | ||
- | |||
- | ### 示例ICall用法 ### | ||
- | |||
- | 下图显示了一个通过 ICall框架从应用程序发送到BLE5-Stack的示例命令,并将相应的返回值传回给应用程序。 | ||
- | |||
- | ICall _init()完成初始化 ICall模块实例,而 ICall_createRemoteTasks()使用已知地址的入口函数为协议栈创建任务。 | ||
- | |||
- | 初始化后的iCall,App作为客户端通过ICall_registerApp()完成注册。 | ||
- | |||
- | 在SYS / BIOS调度程序启动并且应用程序任务运行之后,应用程序发送一个ble_dispatch_JT.c 如GAP_GetParamValue()中定义的协议命令。 | ||
- | |||
- | 协议命令不在应用程序的线程中执行,而是封装在ICall消息中,并通过ICall框架发送到BLE5-Stack任务。该命令被发送到ICALL调度程序,它在BLE5-Stack上下文中调度和执行。同时应用程序线程阻塞,直到接收到相应的命令状态消息。BLE5-Stack完成执行命令,然后通过ICall将命令状态消息响应发送回应用程序线程。这种交换的示例图如下所示。 | ||
- | |||
- | ![图33. ICall消息传递示例](http:// | ||
- | |||
- | ### BLE-Stack 工程是如何作为App中TI-RTOS一个任务运行的 ### | ||
- | * 直接走读App代码,以下代码片段创建了协议栈任务。 | ||
- | ```C | ||
- | // | ||
- | |||
- | void ICall_createRemoteTasks(void { | ||
- | size_t i; | ||
- | UInt keytask; | ||
- | /* Cheap locking mechanism to lock tasks | ||
- | * which may attempt to access the service call dispatcher | ||
- | * till all services are registered. | ||
- | */ | ||
- | keytask = Task_disable(); | ||
- | |||
- | for (i = 0; i < ICALL_REMOTE_THREAD_COUNT; | ||
- | Task_Params params; | ||
- | Task_Handle task; | ||
- | ICall_CSState key; | ||
- | |||
- | Task_Params_init(& | ||
- | params.priority = ICall_threadPriorities[i]; | ||
- | params.stackSize = ICall_threadStackSizes[i]; | ||
- | params.arg0 = (UArg) icall_threadEntries[i]; | ||
- | params.arg1 = (UArg) ICall_getInitParams(i); | ||
- | ``` | ||
- | * 并且该任务的任务函数实体ICall_taskEntry是通过arg0 | ||
- | ```C | ||
- | // | ||
- | static Void ICall_taskEntry(UArg arg0, UArg arg1) { | ||
- | ICall_RemoteTaskEntry entryfn = (ICall_RemoteTaskEntry) arg0; | ||
- | |||
- | entryfn(& | ||
- | } | ||
- | ``` | ||
- | * 任务实体 | ||
- | 对于arg0的参数地址,我们就要区分工程编译选项了。这点我们已经在< | ||
- | ```C | ||
- | // | ||
- | params.arg0 = (UArg) icall_threadEntries[i]; | ||
- | |||
- | // | ||
- | static const ICall_RemoteTaskEntry icall_threadEntries[] = ICALL_ADDR_MAPS; | ||
- | |||
- | // | ||
- | #ifdef STACK_LIBRARY | ||
- | extern void startup_entry( const ICall_RemoteTaskArg *arg0, void *arg1 ); | ||
- | //extern ICall_RemoteTaskEntry startup_entry; | ||
- | #define ICALL_ADDR_MAPS \ | ||
- | { \ | ||
- | (ICall_RemoteTaskEntry) (startup_entry) \ | ||
- | } | ||
- | #else /* ! STACK_LIBRARY */ | ||
- | #define ICALL_ADDR_MAPS \ | ||
- | { \ | ||
- | (ICall_RemoteTaskEntry) (ICALL_STACK0_ADDR) \ | ||
- | } | ||
- | #endif /* STACK_LIBRARY */ | ||
- | ``` | ||
- | 而对于`FlashROM`编译选项这里的`arg0`为一个绝对地址,该绝对地址通过边界工具`frontier.exe`计算协议栈工程的编译生成的存储映射文件*.map所得。并将计算结果保存在应用工程的`iar_boundary.xcl`文件。 | ||
- | ``` | ||
- | // | ||
- | " | ||
- | ``` | ||
- | ```C | ||
- | /* | ||
- | ** Stack Frontier Generator 1.1.0 (2017-06-28 14: | ||
- | ** | ||
- | ** WARNING - Auto-generated file. Modifications could be lost! | ||
- | */ | ||
- | --config_def ICALL_RAM0_START=0x20003fe8 | ||
- | --config_def ICALL_STACK0_START=0x00016a00 | ||
- | --config_def ICALL_STACK0_ADDR=0x00016a01 | ||
- | |||
- | ``` | ||
- | |||
- | Okay, | ||
- | |||
- | ### 尝试走读一个ICALL的消息流程 ### | ||
- | * 服务端登记 | ||
- | 在协议栈任务中完成ICall服务端的登记,并且通过`ICALL_SERVICE_CLASS_BLE_MSG`作为服务端的源地址。 | ||
- | ```C | ||
- | // | ||
- | if (ICall_enrollService(ICALL_SERVICE_CLASS_BLE_MSG, | ||
- | (ICall_ServiceFunc) osal_service_entry, | ||
- | & | ||
- | & | ||
- | { | ||
- | ``` | ||
- | * 客户端注册 | ||
- | 客户端根据不同任务进行注册,我们的这里的`SimpleBLEPeripheral_taskFxn`完成该任务的Icall客户端注册,并且返回`selfEntity`作为改客户端Icall通信的源地址。 | ||
- | ```C | ||
- | // | ||
- | |||
- | ICall_registerApp(& | ||
- | |||
- | ``` | ||
- | * 发送消息 | ||
- | 所有应用程序Icall消息发送都是封装在`icall_directAPI`函数中。 | ||
- | ```C | ||
- | // | ||
- | |||
- | // Setup the GAP | ||
- | GAP_SetParamValue(TGAP_CONN_PAUSE_PERIPHERAL, | ||
- | |||
- | // | ||
- | #define GAP_SetParamValue(...) | ||
- | |||
- | ``` | ||
- | 而对于`icall_directAPI`,将所有变参封装到一个的消息结构体, | ||
- | ```C | ||
- | // | ||
- | icallLiteMsg_t liteMsg; | ||
- | liteMsg.hdr.len = sizeof(icallLiteMsg_t); | ||
- | liteMsg.hdr.next = NULL; | ||
- | liteMsg.hdr.dest_id = ICALL_UNDEF_DEST_ID; | ||
- | liteMsg.msg.directAPI | ||
- | liteMsg.msg.pointerStack = (uint32_t*)(*((uint32_t*)(& | ||
- | ICall_sendServiceMsg(ICall_getEntityId(), | ||
- | | ||
- | |||
- | // | ||
- | return (ICall_send(src, | ||
- | |||
- | ``` | ||
- | 最终`ICall_send`是直接将消息放入了目的Icall消息实体的消息队列中, | ||
- | ```C | ||
- | // | ||
- | ICall_msgEnqueue(& | ||
- | ICALL_SYNC_HANDLE_POST(ICall_entities[dest].task-> | ||
- | ``` | ||
- | ### 如何调试协议栈任务 ### | ||
- | 对于协议栈任务,因为分布在另外一个工程,所以我们没有向原来那样直接加断点调试。上面我们详细讲解了协议栈是如何作为一个任务在应用工程中启动的,所以找到协议任务入口地址就是我们调试协议栈任务的关键。 | ||
- | 在调试协议栈任务之前,我们建议将分别设置协议栈和应用工程优化等级Project-> | ||
- | |||
- | ![](http:// | ||
- | |||
- | 上面我们已经详细讲解过,对于`FlashROM`编译选项,协议栈任务入口地址在`iar_boundary.xcl`地址给出,对于`FlashROM_StackLibrary`编译选项,我们可以通过编译生成的*.map文件查找`startup_entry` 符号从而找到入口地址。 | ||
- | |||
- | ![](http:// | ||
- | ![](http:// | ||
- | |||
- | 拿到协议栈任务的人口地址后,我们就可以在汇编窗口View-> | ||
- | |||
- | ![](http:// | ||
- | |||
- | 当然如果我们不知道协议栈任务的入口地址,直接通过创建协议栈任务的任务实体入口,直接跳转到`ICall_taskEntry`-> | ||
- | |||
- | ![](http:// | ||
- | |||
- | > | ||
- | |||
- | |||
- | ## Simple Peripheral Task ## | ||
- | 简单外设任务作为应用程序任务是系统中最低优先级的任务。该任务的代码是在Application IDE文件夹中的simple_peripheral 文件夹`simple_peripheral.c`中。 | ||
- | |||
- | ### 应用程序初始化功能 ### | ||
- | |||
- | <a href=" | ||
- | |||
- | simple_peripheral任务函数伪代码 | ||
- | ```C | ||
- | static | ||
- | // | ||
- | SimpleBLEPeripheral_init (); | ||
- | |||
- | // Application main loop | ||
- | for (;;) { | ||
- | |||
- | } | ||
- | } | ||
- | ``` | ||
- | 这个初始化函数(simple_peripheral_init)为任务配置了几个服务,并设置了几个硬件和软件配置设置和参数。以下列表包含一些常见示例: | ||
- | |||
- | * 初始化GATT客户端 | ||
- | * 设置各种配置文件的服务读写等回调函数 | ||
- | * 设置GAPRole | ||
- | * 建立 Bond 管理器 | ||
- | * 初始化 GAP层 | ||
- | * 配置LCD或SPI等硬件模块。 | ||
- | |||
- | > | ||
- | |||
- | |||
- | ### 任务功能中的事件处理 ### | ||
- | simple_peripheral实现以事件驱动的任务功能。任务函数进入一个无限循环,使其不间断地为一个独立的任务处理,并且始终不会运行到完成。在这个无限循环中,任务保持阻塞并等待,直到事件标志更新后进入事件处理函数。: | ||
- | |||
- | ICall任务保持阻塞并等待,直到发信号通知进行处理。 | ||
- | ```C | ||
- | // Waits for an event to be posted associated with the calling thread. | ||
- | // Note that an event associated with a thread is posted when a | ||
- | // message is queued to the message receive queue of the thread | ||
- | events = Event_pend(syncEvent, | ||
- | ICALL_TIMEOUT_FOREVER); | ||
- | ``` | ||
- | 当事件发生并被处理后,任务又等待事件标志并且保保持阻塞状态,直到有另一个事件发生。 | ||
- | |||
- | ### 任务事件 ### | ||
- | 当BLE5-Stack通过ICAll消息模块在应用程序任务中设置事件时,任务事件发生。一个比较好的例子就是调用HCI_EXT_ConnEventNoticeCmd()来指示协议栈`connection event`结束。表示该事件结束的任务事件也将显示在simple_peripheral的任务函数中: | ||
- | |||
- | SBP任务检查任务事件。 | ||
- | ```C | ||
- | if (events) | ||
- | { | ||
- | ICall_EntityID dest; | ||
- | ICall_ServiceEnum src; | ||
- | ICall_HciExtEvt *pMsg = NULL; | ||
- | |||
- | if (ICall_fetchServiceMsg(& | ||
- | (void **)& | ||
- | { | ||
- | uint8 safeToDealloc = TRUE; | ||
- | |||
- | if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity)) | ||
- | { | ||
- | ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg; | ||
- | |||
- | // Check for BLE stack events first | ||
- | if (pEvt-> | ||
- | { | ||
- | if (pEvt-> | ||
- | { | ||
- | // Try to retransmit pending ATT Response (if any) | ||
- | SimpleBLEPeripheral_sendAttRsp(); | ||
- | } | ||
- | } | ||
- | else | ||
- | { | ||
- | // Process inter-task message | ||
- | safeToDealloc = SimpleBLEPeripheral_processStackMsg((ICall_Hdr *)pMsg); | ||
- | } | ||
- | } | ||
- | |||
- | if (pMsg && safeToDealloc) | ||
- | { | ||
- | ICall_freeMsg(pMsg); | ||
- | } | ||
- | } | ||
- | |||
- | // Additional Event Processing | ||
- | } | ||
- | ``` | ||
- | > | ||
- | |||
- | 当为一个事件选择一个事件值的时候,该值对于给定的任务必须是唯一的,并且必须是2的幂(只有1bit被设置为1)。因为`pEvt-> | ||
- | |||
- | 清单49. BLE OSAL事件在bcomdef.h中定义。 | ||
- | ```C | ||
- | / | ||
- | * BLE OSAL GAP GLOBAL Events | ||
- | */ | ||
- | #define GAP_EVENT_SIGN_COUNTER_CHANGED 0x4000 //!< The device level sign counter changed | ||
- | |||
- | ``` | ||
- | > **注意**:这些任务间事件是与 `请求和处理协议栈事件` 中提到的内部事件不同的事件集。 | ||
- | |||
- | ## Intertask消息 ## | ||
- | 这些消息通过ICall从一个任务(如BLE5-Stack Service/ | ||
- | |||
- | 正如以下情形: | ||
- | |||
- | * 协议栈成功收到发送的无线数据ACK,需要发送确认从协议栈到应用。 | ||
- | * 与HCI命令相对应的事件。 | ||
- | * GATT客户端操作的响应(请参阅直接使用GATT层) | ||
- | |||
- | Task事件来自simple_peripheral的主要任务循环的一个例子。 | ||
- | |||
- | ### 使用TI-RTOS事件模块 ### | ||
- | 所有BLE5-Stack 1.00.00项目使用TI-RTOS事件模块获取ICall堆栈消息事件。ICall线程同步中描述了使用情况 ,有关事件模块的更多文档,请参见“ TI RTOS内核用户指南”。 | ||
- | |||
- | ### 处理队列的应用程序消息 ### | ||
- | 使用Util_enqueueMsg() 函数将应用程序消息放入队列以先进先出的顺序进行处理。当UTIL_QUEUE_EVENT_ID发布事件时,应用程序应该从消息队列取出消息处理后并释放。 | ||
- | |||
- | 下面的代码片段显示了simple_peripheral如何处理应用程序消息。 | ||
- | ```C | ||
- | #define SBP_QUEUE_EVT | ||
- | |||
- | // If TI-RTOS queue is not empty, process app message. | ||
- | if (events & SBP_QUEUE_EVT) | ||
- | { | ||
- | while (!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); | ||
- | } | ||
- | } | ||
- | } | ||
- | ``` | ||
- | ### 请求和处理协议栈事件 ### | ||
- | 某些API可以选择在BLE5-Stack中发生特定事件时通知应用程序。启用此类事件通知的API将包含一个`taskEvent`参数。`taskEvent` 对于给定的ICall-aware任务,该值必须是唯一的 。应用程序可以通过检查是否`taskEvent`包含在数据结构`ICall_Stack_Event `的`uint16_t event_flag`变量中来处理所请求的协议栈事件。 | ||
- | |||
- | > | ||
- | |||
- | |||
- | 下面的代码片段显示了simple_peripheral如何请求协议栈事件标志。 | ||
- | |||
- | 连接间隔结束时请求通知的应用程序。 | ||
- | ```C | ||
- | // Application specific event ID for HCI Connection Event End Events | ||
- | #define SBP_HCI_CONN_EVT_END_EVT | ||
- | |||
- | static uint8_t SimpleBLEPeripheral_processGATTMsg(gattMsgEvent_t *pMsg) | ||
- | { | ||
- | // See if GATT server was unable to transmit an ATT response | ||
- | if (pMsg-> | ||
- | { | ||
- | // No HCI buffer was available. Let's try to retransmit the response | ||
- | // on the next connection event. | ||
- | if (HCI_EXT_ConnEventNoticeCmd(pMsg-> | ||
- | | ||
- | { | ||
- | // First free any pending response | ||
- | SimpleBLEPeripheral_freeAttRsp(FAILURE); | ||
- | |||
- | // Hold on to the response message for retransmission | ||
- | pAttRsp = pMsg; | ||
- | |||
- | //... | ||
- | } | ||
- | //... | ||
- | } | ||
- | //... | ||
- | } | ||
- | ``` | ||
- | 下面的代码片段显示了simple_peripheral如何处理协议栈事件标志。 | ||
- | |||
- | 处理请求BLE5-Stack事件 | ||
- | ```C | ||
- | // Application specific event ID for HCI Connection Event End Events | ||
- | #define SBP_HCI_CONN_EVT_END_EVT | ||
- | |||
- | static void SimpleBLEPeripheral_taskFxn(UArg a0, UArg a1) | ||
- | { | ||
- | |||
- | // Application main loop | ||
- | for (;;) | ||
- | { | ||
- | uint32_t events; | ||
- | |||
- | // Waits for an event to be posted associated with the calling thread. | ||
- | // Note that an event associated with a thread is posted when a | ||
- | // message is queued to the message receive queue of the thread | ||
- | events = Event_pend(syncEvent, | ||
- | ICALL_TIMEOUT_FOREVER); | ||
- | |||
- | if (events) | ||
- | { | ||
- | ICall_EntityID dest; | ||
- | ICall_ServiceEnum src; | ||
- | ICall_HciExtEvt *pMsg = NULL; | ||
- | |||
- | if (ICall_fetchServiceMsg(& | ||
- | (void **)& | ||
- | { | ||
- | uint8 safeToDealloc = TRUE; | ||
- | |||
- | if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity)) | ||
- | { | ||
- | ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg; | ||
- | |||
- | // Check for BLE stack events first | ||
- | if (pEvt-> | ||
- | { | ||
- | if (pEvt-> | ||
- | { | ||
- | // Try to retransmit pending ATT Response (if any) | ||
- | SimpleBLEPeripheral_sendAttRsp(); | ||
- | } | ||
- | } | ||
- | else | ||
- | { | ||
- | // Process inter-task message | ||
- | safeToDealloc = SimpleBLEPeripheral_processStackMsg((ICall_Hdr *)pMsg); | ||
- | } | ||
- | } | ||
- | } | ||
- | } | ||
- | } | ||
- | } | ||
- | ``` | ||
- | ### 回调 ### | ||
- | 应用程序代码还包括对协议栈层,配置文件和TI-RTOS模块的各种回调函数。为了确保线程的安全性,回调要尽量做最少事情,大部分的处理应该发生在应用程序上下文中。每个回调定义了两个函数。一个是回调本身,另外一个就是在任务环境中被处理回调事件函数。回调不直接处理,通过事件方式到任务中。可以参考 GAPRole 状态改变回调,其用以处理GAPRole状态变化。 | ||
- | |||
- | > | ||
- | |||
- | simple_peripheral状态变化回调。 | ||
- | ```C | ||
- | static | ||
- | SimpleBLEPeripheral_enqueueMsg (SBP_STATE_CHANGE_EVT , newState ); | ||
- | } | ||
- | ``` | ||
- | 上面的代码片段显示了通过`SimpleBLEPeripheral_gapRoleCBs`和`GAPRole_StartDevice()`。回调只是在队列中放置一个消息来通知应用程序唤醒。一旦回调返回其父任务进入休眠状态。应用程序唤醒从消息队列取出消息处理同时调用以下代码片段。 | ||
- | |||
- | simple_peripheral任务环境中的状态变化事件处理。 | ||
- | ```C | ||
- | static | ||
- | // ... | ||
- | } | ||
- | ``` | ||
- | ## 加入我们 ## | ||
- | 文章所有代码、工具、文档开源。加入我们[**QQ群 591679055**](http:// | ||
- | <div> | ||
- | <p align=" | ||
- | <a target=" | ||
- | © Copyright 2017, 成都乐控畅联科技有限公司. | ||
- | </p> | ||
- | </ | ||
- | </ | ||