这里会显示出您选择的修订版和当前版本之间的差别。
后一修订版 | 前一修订版 | ||
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的应用程序框架,在之前我们希望已经按照我们学习线路图储备了< | + | # 应用程序 # |
+ | |||
+ | 本章节将详细讲解 CC2640R2F BLE5.0 的应用程序框架,我们希望您已经按照学习线路图储备了< | ||
+ | 本章主要内容是围绕基于 TI-RTOS 的 App 应用程序框架。 | ||
![](http:// | ![](http:// | ||
- | 这里介绍以 simple_peripheral Demo应用程序部分,包括以下内容: | + | |
+ | 图1. 应用程序框架图 | ||
+ | |||
+ | 以 simple_peripheral Demo 应用程序部分为例,包括以下内容: | ||
* Pre-main initialization | * Pre-main initialization | ||
行 11: | 行 17: | ||
* Intertask Messages | * Intertask Messages | ||
- | > **注意:** GAPRoleTask也是工程的一部分,但是我们将它放在协议栈部分进行讨论,其功能与协议栈密切相关。 | + | > **注意:**虽然 |
## Pre-main initialization ## | ## Pre-main initialization ## | ||
- | `main`函数包含在IDE Startup 文件夹的资源文件main.c中。作为程序的入口,其主要完成 全局中断禁止、外设驱动初始化、电源管理,TI-RTOS任务创建或构造,启用SYS / BIOS内核调度时完成全局中断使能。main函数不返回,将在整个项目生命周期内保留其资源; | ||
- | 基本main.c功能。 | + | `main` 函数包含在 IDE Startup 文件夹的资源文件 main.c 中。作为程序的入口,主要完成全局中断禁止、外设驱动初始化、电源管理、TI-RTOS 任务创建或构造、在启用 SYS / BIOS 内核调度时完成全局中断使能。main 函数不返回,将在整个项目生命周期内保留其资源。 |
+ | |||
+ | 基本 main.c 功能。 | ||
```C | ```C | ||
int main() | int main() | ||
行 48: | 行 56: | ||
} | } | ||
``` | ``` | ||
- | > | + | > |
## ICALL ## | ## ICALL ## | ||
### 介绍 ### | ### 介绍 ### | ||
- | 在<a href=" | + | 在<a href="http://docs.leconiot.com/doku.php? |
- | Indirect Call Framework (**ICall 消息框架**)基于TI-RTOS提供服务(例如,同步线程、消息、事件)完成BLE协议栈和应用程序在两个工程的消息交互,保证了应用程序和协议栈在统一的TI-RTOS环境中完成高效运行、相互通信和资源共享。 | + | Indirect Call Framework (** ICall 消息框架**)是基于 TI-RTOS 提供服务(例如,同步线程、消息、事件)并完成 BLE 协议栈和应用程序在两个工程的消息交互的框架,它能够保证应用程序和协议栈在统一的 TI-RTOS 环境中完成高效运行、相互通信和资源共享。 |
- | ICall架构和的核心组件是其消息调度(dispatcher),其帮助程序在两个镜像/ | + | ICall 架构的核心组件是其消息调度( dispatcher ),消息调度帮助程序在两个 镜像/ |
- | **ICALL**源码在应用工程(例如我们这里的simple_peripheral)的 ICALL BLE/ICALL 文件夹路径。 | + | ** ICALL **源码在应用工程(例如本文的 simple_peripheral )的 ICALL BLE/ICALL 文件夹路径。 |
- | ![图32. ICall 应用-协议栈 抽象](http:// | + | ![](http:// |
- | ### ICall BLE5协议栈服务端 ### | + | 图2. ICall 应用-协议栈 |
- | 如上图所示,**ICALL**实现包含一个客户端实体(例如,我们的应用程序)和一个服务器实体(即这里的BLE5.0协议栈)之间的通信。 | + | ### ICall BLE5 协议栈服务端 ### |
- | > **注意** :正确区分**ICALL**框架的CS架构和GATT服务器和客户端的结构,后者主要由BLE协议栈所定义实现。 | + | 如图 2 所示,** ICALL **实现包含一个客户端实体(例如我们的应用程序)和一个服务器实体(即这里的 BLE5.0 |
- | TI之所以这样设计,前面我们已经讲到: | + | > **注意** :正确区分** ICALL **框架的 CS 架构以及 GATT 服务器和客户端的架构,后者主要由 BLE 协议栈所定义实现。 |
- | * 实现应用程序和BLE协议栈的独立管理和固件更新; | + | TI 之所以这样设计是因为: |
- | * 从传统平台(即CC254x的OSAL)移植到CC2640R2F的TI-RTOS,保持API一致性; | + | |
+ | * 实现应用程序和 BLE 协议栈的独立管理和固件更新 | ||
+ | * 从传统平台(即 CC254x 的 OSAL )移植到 CC2640R2F 的 TI-RTOS ,保持 API 一致性 | ||
- | **ICall** 作为 BLE5协议栈API的服务接口。当我们需要调用一些协议栈接口的时候,ICall模块会自动将命令分发(即调度)到BLE5协议栈,并将消息从BLE5协议栈结果回传到应用程序。 | ||
+ | ** ICall ** 作为 BLE5 协议栈 API 的服务接口。当调用一些协议栈接口时,ICall 模块会自动将命令分发(即调度)到 BLE5 协议栈,并将消息结果从 BLE5 协议栈传回应用程序。 | ||
- | 因为ICall消息模块本身就作为是应用程序工程的一部分,应用任务可以通过函数调用方式直接访问ICall。由于BLE协议栈任务总是以最高优先级执行,但是应用程序在没有数据返回时缺处于阻塞状态,必然有些API在会在协议栈任务立即响应,应用任务只有在消息通过ICALL分发时才能唤醒处理。另外一些API却只有等待应用任务通过ICAlL(事件更新)异步返回结果。 | ||
- | ### ICALL原语服务 ### | + | ICall 消息模块本身就作为是应用程序工程的一部分,应用任务可以通过函数调用方式直接访问 ICall 。由于 BLE 协议栈任务总是以最高优先级执行,应用程序在没有数据返回时处于阻塞状态,必然有些 API 会在协议栈任务立即响应,应用任务只有在消息通过 |
- | ICALl包含一系列的原语服务都被抽象成基于RTOS相关的函数接口。由于共享资源和线程之间的通信,应用程序必须使用以下ICALL原语服务功能: | + | ### ICALL 原语服务 ### |
+ | |||
+ | ICALl 一系列的原语服务都被抽象成基于 RTOS 相关的函数接口。由于共享资源和多线程之间的通信,应用程序必须使用以下 ICALL 原语服务功能: | ||
* 消息传递和线程同步 | * 消息传递和线程同步 | ||
行 88: | 行 99: | ||
#### 消息传递和线程同步 #### | #### 消息传递和线程同步 #### | ||
- | **ICall**同协议栈的消息传递和线程同步都是是基于TI-RTOS多线程。 | ||
- | **ICall**两个任务的消息传递发生在通过消息队列发送一个阻塞消息。发送方动态分分配一段内存,将消息的内容写入内存,然后将这段内存发送(即排队)到接收线程,然后再使用事件标志。接受任务在收到事件标志后唤醒,复制内存消息,并且处理,之后再并将这段内存块释放。 | + | ** ICall **同协议栈的消息传递和线程同步都是是基于 TI-RTOS 多线程。 |
- | 协议栈使用ICall通知和发送消息到应用程序。ICall传递这些服务消息,应用程序任务接收它然后处理。 | + | ** ICall **通过一个任务在消息队列给另外一个任务发送一个阻塞消息时实现消息传递。发送方动态分配一段内存,将消息的内容写入内存,将这段内存发送(即排队)到接收线程,然后再使用事件标志。接受任务在收到事件标志后唤醒,复制内存消息并处理,之后再将这段内存块释放。 |
+ | |||
+ | 协议栈使用 ICall 通知和发送消息到应用程序,ICall 传递这些服务消息,应用程序任务接收它然后处理。 | ||
#### 堆分配与管理 #### | #### 堆分配与管理 #### | ||
- | ICall为应用提供了全局堆管理的API用以动态内存分配。其堆的大小通过宏`HEAPMGR_SIZE` 配置。有关管理动态内存的更多详细信息,请参阅动态内存分配。BLE 协议栈的ICall使用此堆管理进行所有消息相关的内存管理,同样我们建议应用程序也使用这些ICall API来分配动态内存。 | + | ICall 为应用提供了全局堆管理的 API 用以动态内存分配,其堆的大小通过宏 `HEAPMGR_SIZE` 配置。有关管理动态内存的更多详细信息,请参阅动态内存分配。BLE 协议栈的 ICall 使用堆管理进行所有消息相关的内存管理,建议应用程序也使用这些 ICall API 来分配动态内存。 |
- | ### ICALL初始化和注册 ### | + | ### ICALL 初始化和注册 ### |
- | 要实例化和初始化ICall服务,应用程序必须在启动TI-RTOS内核调度程序之前调用main()中的代码片段中的函数: | + | 要实例和初始化 ICall 服务,应用程序必须在启动 TI-RTOS 内核调度程序之前调用 main()代码片段中的函数: |
+ | |||
+ | 使用 ICall 的必需代码。 | ||
- | 使用ICall的必需代码。 | ||
```C | ```C | ||
- | /* 初始化ICall模块 */ | + | /* 初始化 ICall 模块 */ |
- | ICall _init (); | + | ICall_init(); |
- | /* 启动协议栈任务 | + | /* 将协议栈作作为任务创建 |
- | ICall _createRemoteTasks | + | ICall _createRemoteTasks |
``` | ``` | ||
- | 调用ICall_init()初始化ICALL原语服务(例如,堆管理)和框架,调用 ICall _createRemoteTasks()创建但不启动BLE5-Stack任务。在使用ICall协议服务之前,服务器和客户端分别完成登记和注册。服务端在编译的时候就需要登记一个服务。登记函数使用一个全局的唯一标识符区分每个服务,也是作为后面通信地址。例如,BLE协议使用 `ICALL_SERVICE_CLASS_BLE`做些蓝牙协议栈ICALL的消息交互的标识。 | + | 调用 ICall_init()初始化 ICALL 原语服务(例如,初始化堆管理)和框架,调用 ICall _createRemoteTasks()创建但不启动 BLE5-Stack 任务。在使用ICall 协议服务之前,服务器和客户端分别完成登记和注册。服务端在编译的时候就需要登记一个服务,登记函数使用一个全局的唯一标识符区分每个服务并作为通信地址。例如,BLE协议使用 `ICALL_SERVICE_CLASS_BLE` 做些蓝牙协议栈 ICALL的消息交互的标识。 |
- | 对于服务端的登记,包含在`osal_icall_ble.c`文件 | + | 服务端的登记在 `osal_icall_ble.c` 文件: |
```C | ```C | ||
- | / *ICALL服务端登记* / | + | / * ICALL 服务端登记* / |
ICall _enrollService (ICALL _SERVICE_CLASS_BLE ,NULL ,&entity ,&syncHandle ); | ICall _enrollService (ICALL _SERVICE_CLASS_BLE ,NULL ,&entity ,&syncHandle ); | ||
``` | ``` | ||
- | 客户端在ICALL调度程序发送和/ | + | 客户端在 ICALL 调度程序发送和/ |
- | 对于使用BLE5API的客户端(例如应用程序任务),客户端必须首先向ICall注册其任务 。该注册通常发生在应用程序的任务初始化功能中。下面的代码片段是 `simple_peripheral_init` simple_peripheral.c中注册。 | + | 对于使用 |
```C | ```C | ||
// | // | ||
- | ICall _registerApp | + | ICall _registerApp |
``` | ``` | ||
- | 完成客户端注册前,需要传入结构体变量`selfEntity`和`syncEvent`,其值在函数返回时被初始化,以后服务端通过这两个变量来进行消息传递。`syncEvent`参数表示事件标识,`selfEntity`表示处理消息的目的任务,也是客户端实体以后通信的源地址,每个注册ICALL的客户端都需要使用唯一的`syncEvent`和`selfEntity`。 | + | 完成客户端注册前需要传入结构体变量 `selfEntity` 和 `syncEvent` |
- | > **注意**: 在客户端注册ICALL服务之前,在`ICallBLEApi.c`文件里面定义的ICALL相关API都不能调用的。 | + | > **注意**: 在客户端注册 ICALL 服务之前,在 `ICallBLEApi.c` 文件里面定义的 ICALL 相关 API 都不能调用。 |
- | ### ICall线程同步 ### | + | |
- | ICALL使用TI-RTOS的事件用以线程同步而不是信号量。 | + | ### ICall 线程同步 |
- | 为了让客户端或服务端在收到消息前都保持阻塞状态, ICall提供以下阻塞型API保持任务阻塞状态直到关联的信号量被Post: | + | ICALL 使用 TI-RTOS 的事件用以线程同步而不是信号量。 |
+ | |||
+ | 为了让客户端或服务端在收到消息前都保持阻塞状态, ICall 提供以下阻塞型 API 保持任务阻塞状态直到关联的信号量被 Post : | ||
```C | ```C | ||
行 141: | 行 155: | ||
``` | ``` | ||
- | `handle`表示是构造的Event_Handle的句柄(标识符)。`andMask`并 `orMask` 为用户选择要阻塞/ | + | `handle` 是构造的 Event_Handle |
+ | `andMask` | ||
+ | `timeout` 是以毫秒为单位的超时周期。如果在此超时时间范围内后事件尚未被Post,该函数将返回。 | ||
`Event_pend` 函数表示阻塞当前任务等待某一事件标志位发生。 | `Event_pend` 函数表示阻塞当前任务等待某一事件标志位发生。 | ||
- | 一共有32个事件标志位(int类型),这些标识位可以根据其特定用途进行定义。不过需要注意的是,ICall消息队列已经保留了特定事件标志位。 | + | 一共有 32 个事件标志位( int 类型),这些标识位可以根据其特定用途进行定义。不过需要注意的是,ICall 消息队列已经保留了特定事件标志位。 |
- | 与TI-RTOS线程关联的事件由Event_post所需标志调用时发出/ | + | 与 TI-RTOS 线程关联的事件由 Event_post 所需标志调用时发出/ |
- | `Event_post`用以客户端/ | + | `Event_post` 用以客户端/ |
```C | ```C | ||
void Event_post(Event_Handle handle,UInt eventMask); | void Event_post(Event_Handle handle,UInt eventMask); | ||
``` | ``` | ||
- | 以上的事件句柄`handle`在服务端ICall _enrollService()和ICall _registerApp()调用后获得。 | ||
- | > **危险**: | + | 以上的事件句柄 `handle` 由服务端 ICall _enrollService()和 ICall _registerApp()调用后获得。 |
+ | |||
+ | > **危险**: | ||
+ | |||
+ | ### 示例 ICall 用法 ### | ||
+ | |||
+ | 图 3 显示了一个通过 ICall 框架从应用程序发送到 BLE5-Stack 的示例命令,并将相应的返回值传回给应用程序。 | ||
+ | |||
+ | ICall _init()完成初始化 ICall 模块实例,ICall_createRemoteTasks()使用已知地址的入口函数为协议栈创建任务。 | ||
+ | |||
+ | 初始化后的 ICall ,App 作为客户端通过 ICall_registerApp()完成注册。 | ||
- | ### 示例ICall用法 ### | + | 在 SYS / BIOS 调度程序启动并且应用程序任务运行之后,应用程序发送一个 ble_dispatch_JT.c 如 GAP_GetParamValue()中定义的协议命令。 |
- | 下图显示了一个通过 ICall框架从应用程序发送到BLE5-Stack的示例命令,并将相应的返回值传回给应用程序。 | + | 协议命令不在应用程序的线程中执行,而是封装在ICall消息中执行,是由 ICall 框架发送到 BLE5-Stack |
- | ICall _init()完成初始化 ICall模块实例,而 ICall_createRemoteTasks()使用已知地址的入口函数为协议栈创建任务。 | + | ![](http:// |
- | 初始化后的iCall,App作为客户端通过ICall_registerApp()完成注册。 | + | 图3. ICall 消息传递示例 |
- | 在SYS / BIOS调度程序启动并且应用程序任务运行之后,应用程序发送一个ble_dispatch_JT.c 如GAP_GetParamValue()中定义的协议命令。 | + | 有了以上理论知识储备,我们尝试解决以下问题: |
- | 协议命令不在应用程序的线程中执行,而是封装在ICall消息中,并通过ICall框架发送到BLE5-Stack任务。该命令被发送到ICALL调度程序,它在BLE5-Stack上下文中调度和执行。同时应用程序线程阻塞,直到接收到相应的命令状态消息。BLE5-Stack完成执行命令,然后通过ICall将命令状态消息响应发送回应用程序线程。这种交换的示例图如下所示。 | + | ### BLE-Stack 工程是如何作为 App 中 TI-RTOS 的一个任务运行的 |
- | ![图33. ICall消息传递示例](http:// | + | * 直接走读 App 代码,以下代码片段创建了协议栈任务: |
- | ### BLE-Stack 工程是如何作为App中TI-RTOS一个任务运行的 ### | ||
- | * 直接走读App代码,以下代码片段创建了协议栈任务。 | ||
```C | ```C | ||
// | // | ||
行 197: | 行 219: | ||
params.arg1 = (UArg) ICall_getInitParams(i); | params.arg1 = (UArg) ICall_getInitParams(i); | ||
``` | ``` | ||
- | * 并且该任务的任务函数实体ICall_taskEntry是通过arg0 | + | |
+ | * 该任务的任务函数实体 ICall_taskEntry 是通过 arg0 参数强制转换得到的。 | ||
```C | ```C | ||
// | // | ||
行 206: | 行 230: | ||
} | } | ||
``` | ``` | ||
+ | |||
* 任务实体 | * 任务实体 | ||
- | 对于arg0的参数地址,我们就要区分工程编译选项了。这点我们已经在< | + | |
+ | arg0 的参数地址要区分工程编译选项,这点已经在< | ||
```C | ```C | ||
// | // | ||
行 230: | 行 257: | ||
#endif /* STACK_LIBRARY */ | #endif /* STACK_LIBRARY */ | ||
``` | ``` | ||
- | 而对于`FlashROM`编译选项这里的`arg0`为一个绝对地址,该绝对地址通过边界工具`frontier.exe`计算协议栈工程的编译生成的存储映射文件*.map所得。并将计算结果保存在应用工程的`iar_boundary.xcl`文件。 | + | |
+ | `FlashROM` 编译选项的 `arg0` | ||
``` | ``` | ||
// | // | ||
" | " | ||
``` | ``` | ||
+ | |||
```C | ```C | ||
/* | /* | ||
行 241: | 行 271: | ||
** WARNING - Auto-generated file. Modifications could be lost! | ** WARNING - Auto-generated file. Modifications could be lost! | ||
*/ | */ | ||
+ | |||
--config_def ICALL_RAM0_START=0x20003fe8 | --config_def ICALL_RAM0_START=0x20003fe8 | ||
--config_def ICALL_STACK0_START=0x00016a00 | --config_def ICALL_STACK0_START=0x00016a00 | ||
行 247: | 行 278: | ||
``` | ``` | ||
- | Okay, | + | Okay, |
+ | |||
+ | ### 尝试走读一个 ICALL 的消息流程 ### | ||
- | ### 尝试走读一个ICALL的消息流程 ### | ||
* 服务端登记 | * 服务端登记 | ||
- | 在协议栈任务中完成ICall服务端的登记,并且通过`ICALL_SERVICE_CLASS_BLE_MSG`作为服务端的源地址。 | + | |
+ | 在协议栈任务中完成 ICall 服务端的登记,`ICALL_SERVICE_CLASS_BLE_MSG` 作为服务端的源地址。 | ||
```C | ```C | ||
// | // | ||
行 260: | 行 294: | ||
{ | { | ||
``` | ``` | ||
+ | |||
* 客户端注册 | * 客户端注册 | ||
- | 客户端根据不同任务进行注册,我们的这里的`SimpleBLEPeripheral_taskFxn`完成该任务的Icall客户端注册,并且返回`selfEntity`作为改客户端Icall通信的源地址。 | + | |
+ | 客户端根据不同任务进行注册,`SimpleBLEPeripheral_taskFxn` 完成该任务的 Icall 客户端注册,并且返回 `selfEntity` 作为客户端 Icall 通信的源地址。 | ||
```C | ```C | ||
// | // | ||
行 268: | 行 305: | ||
``` | ``` | ||
+ | |||
* 发送消息 | * 发送消息 | ||
- | 所有应用程序Icall消息发送都是封装在`icall_directAPI`函数中。 | + | |
+ | 所有应用程序 Icall 消息发送都是封装在 `icall_directAPI` 函数中。 | ||
```C | ```C | ||
// | // | ||
行 280: | 行 320: | ||
``` | ``` | ||
- | 而对于`icall_directAPI`,将所有变参封装到一个的消息结构体,并且调用`ICall_sendServiceMsg`-> | + | |
+ | 而对于 `icall_directAPI` ,将所有变参封装到一个消息结构体,并且调用 `ICall_sendServiceMsg` -> `ICall_send` 。 | ||
```C | ```C | ||
// | // | ||
行 296: | 行 338: | ||
``` | ``` | ||
- | 最终`ICall_send`是直接将消息放入了目的Icall消息实体的消息队列中,并且触发事件,通知该任务唤醒解析处理消息。 | + | |
+ | 最终 `ICall_send` 是直接将消息放入了目的 Icall 消息实体的消息队列中,并且触发事件通知该任务唤醒解析处理消息。 | ||
```C | ```C | ||
// | // | ||
行 302: | 行 346: | ||
ICALL_SYNC_HANDLE_POST(ICall_entities[dest].task-> | ICALL_SYNC_HANDLE_POST(ICall_entities[dest].task-> | ||
``` | ``` | ||
+ | |||
### 如何调试协议栈任务 ### | ### 如何调试协议栈任务 ### | ||
- | 对于协议栈任务,因为分布在另外一个工程,所以我们没有向原来那样直接加断点调试。上面我们详细讲解了协议栈是如何作为一个任务在应用工程中启动的,所以找到协议任务入口地址就是我们调试协议栈任务的关键。 | + | |
- | 在调试协议栈任务之前,我们建议将分别设置协议栈和应用工程优化等级Project-> | + | 因为协议栈任务分布在另外一个工程,所以没有向原来那样直接加断点调试。已经了解协议栈是如何作为一个任务在应用工程中启动的,那么调试协议栈任务的关键是找到协议任务入口地址。 |
+ | 在调试协议栈任务之前,建议将分别设置协议栈和应用工程优化等级 Project -> Opitons -> C/C++ Compiler Optimizations -> Level -> None 为无,这对正常调试协议栈任务至关重要。 | ||
![](http:// | ![](http:// | ||
- | 上面我们已经详细讲解过,对于`FlashROM`编译选项,协议栈任务入口地址在`iar_boundary.xcl`地址给出,对于`FlashROM_StackLibrary`编译选项,我们可以通过编译生成的*.map文件查找`startup_entry` 符号从而找到入口地址。 | + | 图4 .设置优化等级 |
+ | |||
+ | `FlashROM` 编译选项的协议栈任务入口地址在 `iar_boundary.xcl` | ||
![](http:// | ![](http:// | ||
![](http:// | ![](http:// | ||
- | 拿到协议栈任务的人口地址后,我们就可以在汇编窗口View-> | + | 图5 .找到入口地址 |
+ | |||
+ | 拿到协议栈任务的入口地址后,就可以在汇编窗口 View -> Disassembly 直接输入该地址,然后加上断点,等待协议栈任务创建后运行,从而跳转到协议栈工程进行调试。 | ||
![](http:// | ![](http:// | ||
- | 当然如果我们不知道协议栈任务的入口地址,直接通过创建协议栈任务的任务实体入口,直接跳转到`ICall_taskEntry`-> | + | 图6 .如何用协议栈工程进行调试 |
+ | |||
+ | 如果不知道协议栈任务的入口地址,则直接通过创建协议栈任务的任务实体入口,跳转到 `ICall_taskEntry` -> `entryfn` | ||
![](http:// | ![](http:// | ||
- | > | + | 图7 .如何进入协议栈任务调试 |
+ | |||
+ | > | ||
## Simple Peripheral Task ## | ## Simple Peripheral Task ## | ||
- | 简单外设任务作为应用程序任务是系统中最低优先级的任务。该任务的代码是在Application IDE文件夹中的simple_peripheral 文件夹`simple_peripheral.c`中。 | + | |
+ | 简单外设任务是系统中最低优先级的应用程序任务,该任务的代码是在 Application IDE 文件夹中的 simple_peripheral 文件夹 | ||
### 应用程序初始化功能 ### | ### 应用程序初始化功能 ### | ||
- | <a href=" | + | <a href="http://docs.leconiot.com/doku.php? |
+ | |||
+ | simple_peripheral 任务函数伪代码 | ||
- | simple_peripheral任务函数伪代码 | ||
```C | ```C | ||
static | static | ||
行 343: | 行 399: | ||
} | } | ||
``` | ``` | ||
- | 这个初始化函数(simple_peripheral_init)为任务配置了几个服务,并设置了几个硬件和软件配置设置和参数。以下列表包含一些常见示例: | ||
- | * 初始化GATT客户端 | + | 初始化函数( simple_peripheral_init )为任务配置了几个服务,并设置了几个硬件和软件配置设置和参数。 |
+ | 以下列表包含一些常见示例: | ||
+ | |||
+ | * 初始化 GATT 客户端 | ||
* 设置各种配置文件的服务读写等回调函数 | * 设置各种配置文件的服务读写等回调函数 | ||
- | * 设置GAPRole | + | * 设置 GAPRole |
* 建立 Bond 管理器 | * 建立 Bond 管理器 | ||
- | * 初始化 GAP层 | + | * 初始化 GAP 层 |
- | * 配置LCD或SPI等硬件模块。 | + | * 配置 LCD 或 SPI 等硬件模块。 |
- | > | + | > |
### 任务功能中的事件处理 ### | ### 任务功能中的事件处理 ### | ||
- | simple_peripheral实现以事件驱动的任务功能。任务函数进入一个无限循环,使其不间断地为一个独立的任务处理,并且始终不会运行到完成。在这个无限循环中,任务保持阻塞并等待,直到事件标志更新后进入事件处理函数。: | + | |
+ | simple_peripheral 实现以事件驱动任务的功能。任务函数进入一个无限循环,使其不间断地处理一个独立的任务,并且始终不会运行到完成。在无限循环过程中,任务保持阻塞并等待,直到事件标志更新后进入事件处理函数。 | ||
ICall任务保持阻塞并等待,直到发信号通知进行处理。 | ICall任务保持阻塞并等待,直到发信号通知进行处理。 | ||
+ | |||
```C | ```C | ||
// Waits for an event to be posted associated with the calling thread. | // Waits for an event to be posted associated with the calling thread. | ||
行 366: | 行 426: | ||
ICALL_TIMEOUT_FOREVER); | ICALL_TIMEOUT_FOREVER); | ||
``` | ``` | ||
- | 当事件发生并被处理后,任务又等待事件标志并且保保持阻塞状态,直到有另一个事件发生。 | + | |
+ | 当事件发生并被处理后,任务又等待事件标志并且保持阻塞状态,直到有另一个事件发生。 | ||
### 任务事件 ### | ### 任务事件 ### | ||
- | 当BLE5-Stack通过ICAll消息模块在应用程序任务中设置事件时,任务事件发生。一个比较好的例子就是调用HCI_EXT_ConnEventNoticeCmd()来指示协议栈`connection event`结束。表示该事件结束的任务事件也将显示在simple_peripheral的任务函数中: | ||
- | SBP任务检查任务事件。 | + | 当 BLE5-Stack 通过 ICAll 消息模块在应用程序任务中设置事件时,任务事件发生。一个比较好的例子就是调用 HCI_EXT_ConnEventNoticeCmd()来指示协议栈 `connection event` 结束。表示该事件结束的任务事件也将显示在 simple_peripheral 的任务函数中。 |
+ | |||
+ | SBP 任务检查任务事件: | ||
```C | ```C | ||
if (events) | if (events) | ||
行 413: | 行 476: | ||
} | } | ||
``` | ``` | ||
- | > | ||
- | 当为一个事件选择一个事件值的时候,该值对于给定的任务必须是唯一的,并且必须是2的幂(只有1bit被设置为1)。因为`pEvt-> | + | > |
+ | |||
+ | 当为一个事件选择一个事件值的时候,该值对于给定的任务必须是唯一的,并且必须是 2 的幂(只有 | ||
+ | |||
+ | 清单1. BLE OSAL事件在 bcomdef.h 中定义。 | ||
- | 清单49. BLE OSAL事件在bcomdef.h中定义。 | ||
```C | ```C | ||
/ | / | ||
行 425: | 行 490: | ||
``` | ``` | ||
- | > **注意**:这些任务间事件是与 `请求和处理协议栈事件` 中提到的内部事件不同的事件集。 | ||
- | ## Intertask消息 ## | + | > **注意**:这些任务间的事件是与 `请求和处理协议栈事件` 中提到的内部事件不同的事件集。 |
- | 这些消息通过ICall从一个任务(如BLE5-Stack Service/ | + | |
+ | ## Intertask 消息 ## | ||
+ | |||
+ | 这些消息通过 ICall 从一个任务(如 BLE5-Stack Service/ | ||
正如以下情形: | 正如以下情形: | ||
- | * 协议栈成功收到发送的无线数据ACK,需要发送确认从协议栈到应用。 | + | * 协议栈成功收到发送的无线数据 ACK ,需要发送确认从协议栈到应用。 |
- | * 与HCI命令相对应的事件。 | + | * 与 HCI 命令相对应的事件。 |
- | * GATT客户端操作的响应(请参阅直接使用GATT层) | + | * GATT 客户端操作的响应(请参阅直接使用 GATT 层) |
- | Task事件来自simple_peripheral的主要任务循环的一个例子。 | + | Task 事件是来自 simple_peripheral 的主要任务循环的一个例子。 |
- | ### 使用TI-RTOS事件模块 ### | + | ### 使用 TI-RTOS 事件模块 ### |
- | 所有BLE5-Stack 1.00.00项目使用TI-RTOS事件模块获取ICall堆栈消息事件。ICall线程同步中描述了使用情况 | + | |
+ | 所有 BLE5-Stack 1.00.00 项目使用 TI-RTOS 事件模块获取 ICall 堆栈消息事件,ICall 线程同步中描述了使用情况。有关事件模块的更多文档,请参见“ TI RTOS 内核用户指南”。 | ||
### 处理队列的应用程序消息 ### | ### 处理队列的应用程序消息 ### | ||
- | 使用Util_enqueueMsg() 函数将应用程序消息放入队列以先进先出的顺序进行处理。当UTIL_QUEUE_EVENT_ID发布事件时,应用程序应该从消息队列取出消息处理后并释放。 | ||
- | 下面的代码片段显示了simple_peripheral如何处理应用程序消息。 | + | 使用 Util_enqueueMsg() 函数将应用程序消息放入队列以先进先出的顺序进行处理。当 UTIL_QUEUE_EVENT_ID 发布事件时,应用程序应该从消息队列取出消息处理后释放。 |
+ | |||
+ | 下面的代码片段显示了 simple_peripheral 如何处理应用程序消息。 | ||
```C | ```C | ||
#define SBP_QUEUE_EVT | #define SBP_QUEUE_EVT | ||
行 465: | 行 535: | ||
} | } | ||
``` | ``` | ||
+ | |||
### 请求和处理协议栈事件 ### | ### 请求和处理协议栈事件 ### | ||
- | 某些API可以选择在BLE5-Stack中发生特定事件时通知应用程序。启用此类事件通知的API将包含一个`taskEvent`参数。`taskEvent` 对于给定的ICall-aware任务,该值必须是唯一的 。应用程序可以通过检查是否`taskEvent`包含在数据结构`ICall_Stack_Event `的`uint16_t event_flag`变量中来处理所请求的协议栈事件。 | ||
- | > | + | 某些 API 可以选择在 BLE5-Stack 中发生特定事件时通知应用程序,启用此类事件通知的 API 将包含一个 `taskEvent` 参数。`taskEvent` 对于给定的ICall-aware 任务,值必须是唯一的。应用程序可以通过检查 `taskEvent` 是否包含在数据结构 `ICall_Stack_Event ` 的 `uint16_t event_flag` 变量中来处理所请求的协议栈事件。 |
+ | > | ||
- | 下面的代码片段显示了simple_peripheral如何请求协议栈事件标志。 | ||
- | 连接间隔结束时请求通知的应用程序。 | + | 下面的代码片段显示了 simple_peripheral 如何请求协议栈事件标志。 |
+ | |||
+ | 连接间隔结束时请求通知的应用程序: | ||
```C | ```C | ||
// Application specific event ID for HCI Connection Event End Events | // Application specific event ID for HCI Connection Event End Events | ||
行 501: | 行 574: | ||
} | } | ||
``` | ``` | ||
- | 下面的代码片段显示了simple_peripheral如何处理协议栈事件标志。 | ||
- | 处理请求BLE5-Stack事件 | + | 下面的代码片段显示了 simple_peripheral 如何处理协议栈事件标志。 |
+ | |||
+ | 处理请求 BLE5-Stack 事件: | ||
```C | ```C | ||
// Application specific event ID for HCI Connection Event End Events | // Application specific event ID for HCI Connection Event End Events | ||
行 557: | 行 632: | ||
} | } | ||
``` | ``` | ||
+ | |||
### 回调 ### | ### 回调 ### | ||
- | 应用程序代码还包括对协议栈层,配置文件和TI-RTOS模块的各种回调函数。为了确保线程的安全性,回调要尽量做最少事情,大部分的处理应该发生在应用程序上下文中。每个回调定义了两个函数。一个是回调本身,另外一个就是在任务环境中被处理回调事件函数。回调不直接处理,通过事件方式到任务中。可以参考 GAPRole 状态改变回调,其用以处理GAPRole状态变化。 | ||
- | > | + | 应用程序代码还包括对协议栈层、配置文件和 TI-RTOS 模块的各种回调函数。为了确保线程的安全性,回调要尽量做最少事情。大部分的处理应该发生在应用程序上下文中。每个回调定义了两个函数,一个是回调本身,另外一个就是在任务环境中被处理回调事件函数。回调不直接处理,通过事件方式到任务中。可以参考 GAPRole 状态改变回调,它是用来处理 GAPRole 状态变化的。 |
+ | |||
+ | > | ||
simple_peripheral状态变化回调。 | simple_peripheral状态变化回调。 | ||
+ | |||
```C | ```C | ||
static | static | ||
行 568: | 行 646: | ||
} | } | ||
``` | ``` | ||
- | 上面的代码片段显示了通过`SimpleBLEPeripheral_gapRoleCBs`和`GAPRole_StartDevice()`。回调只是在队列中放置一个消息来通知应用程序唤醒。一旦回调返回其父任务进入休眠状态。应用程序唤醒从消息队列取出消息处理同时调用以下代码片段。 | ||
- | simple_peripheral任务环境中的状态变化事件处理。 | + | 上面的代码片段是通过 `SimpleBLEPeripheral_gapRoleCBs `和 `GAPRole_StartDevice()` 回调的,回调只是在队列中放置一个消息来通知应用程序唤醒。一旦回调返回其父任务进入休眠状态,应用程序唤醒从消息队列取出消息处理同时调用以下代码片段。 |
+ | |||
+ | simple_peripheral 任务环境中的状态变化事件处理。 | ||
```C | ```C | ||
static | static | ||
行 576: | 行 656: | ||
} | } | ||
``` | ``` | ||
+ | |||
## 加入我们 ## | ## 加入我们 ## | ||
+ | |||
文章所有代码、工具、文档开源。加入我们[**QQ群 591679055**](http:// | 文章所有代码、工具、文档开源。加入我们[**QQ群 591679055**](http:// | ||
<div> | <div> | ||
行 585: | 行 667: | ||
</ | </ | ||
</ | </ | ||
- |