用户工具

站点工具


cc2640r2f:application_architecture

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

后一修订版
前一修订版
cc2640r2f:application_architecture [2017/08/31 16:30]
127.0.0.1 外部编辑
cc2640r2f:application_architecture [2021/06/22 23:14] (当前版本)
行 1: 行 1:
 <markdown> <markdown>
-# 应用程序 # + 
-从这个章节开始,我们将详细讲解CC2640R2F BLE5.0的应用程序框架,在之前我们希望已经按照我们学习线路图储备了<a href ="..\..\cc2640r2f_architecture\software_and_hardware\overview\overview.html">CC2640R2F平台的软硬件架构知识</a>明白应用工程区分App和Stack工程管理。这里我们主要是讲解基于TI-RTOS的App应用程序框架。+# 应用程序 # 
 + 
 +章节将详细讲解 CC2640R2F BLE5.0 的应用程序框架,我们希望已经按照学习线路图储备了<a href ="http://docs.leconiot.com/doku.php?id=cc2640r2f:cc2640r2f_architecture:software_and_hardware:overview:overview"> CC2640R2F平台的软硬件架构知识 </a>明白应用工程区分 App 和 Stack 工程管理。 
 +本章主要内容围绕基于 TI-RTOS 的 App 应用程序框架。
  
 ![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/image43.jpeg) ![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/image43.jpeg)
-这里介绍以 simple_peripheral Demo应用程序部分,包括以下内容:+ 
 +图1. 应用程序框架图 
 + 
 +以 simple_peripheral Demo 应用程序部分为例,包括以下内容:
  
 * Pre-main initialization * Pre-main initialization
行 11: 行 17:
 * Intertask Messages * Intertask Messages
  
-> **注意:** GAPRoleTask也是工程的一部分,但是我们将它放在协议栈部分进行讨论,功能与协议栈密切相关。+> **注意:**虽然 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_init()完成初始化,并且通过ICall_createRemoteTasks()完成协议栈任务创建 。+>**注意**:以上代码** ICALL **消息模块必须由 ICALL_init() 完成初始化,并且通过 ICall_createRemoteTasks() 完成协议栈任务创建。 
 ## ICALL ## ## ICALL ##
  
 ### 介绍 ### ### 介绍 ###
  
-在<a href="..\..\cc2640r2f_architecture\software_and_hardware\software\software_architecture.html">软件架构</a>章节我们讲解了由于历史兼容原因整个应用工程区分App和Stack两个工程管理,正是基于这样的两个工程设计,App和Stack之间的通信就需要重新考虑了,因为我们没有办常规API调用和全局变量方式完成消息传递。TI引入了ICALL消息机制完成App和Stack独立工程管理的彼此通信。以我们着重理解原理和代码实现,因其占了我们程序很大部分+在<a href="http://docs.leconiot.com/doku.php?id=cc2640r2f:cc2640r2f_architecture:software_and_hardware:software:software_architecture">软件架构</a>章节阐述了由于历史兼容原因会把整个应用工程区分 App 和 Stack 两个工程管理。因此采用常规 API 调用和全局变量方式完成消息传递,App 和 Stack 之间的通信就需要重新考虑了。TI 引入了 ICALL 消息机制完成 App 和 Stack 独立工程管理的相互通信,接来就着重理解原理和代码实现。
  
-Indirect Call Framework (**ICall 消息框架**)基于TI-RTOS提供服务(例如,同步线程、消息、事件)完成BLE协议栈和应用程序在两个工程的消息交互,保证应用程序和协议栈在统一的TI-RTOS环境中完成高效运行、相互通信和资源共享。+Indirect Call Framework (** ICall 消息框架**)基于 TI-RTOS 提供服务(例如,同步线程、消息、事件)完成 BLE 协议栈和应用程序在两个工程的消息交互的框架它能够保证应用程序和协议栈在统一的 TI-RTOS 环境中完成高效运行、相互通信和资源共享。
  
-ICall架构的核心组件是其消息调度(dispatcher),帮助程序在两个镜像/工程中完成BLE5-Stack协议栈以及库配置中应用程序交互。尽管大多数ICall交互在BLE5-Stack API(例如GAP,HCI等)中已经被抽象化(已经被封装为消息原语函数),但是我们必须理解在BLE-Stack和多线程RTOS环境中正常运行的基础架构。+ICall 架构的核心组件是其消息调度( dispatcher ),消息调度帮助程序在两个 镜像/工程 中完成 BLE5-Stack 协议栈以及库配置中应用程序交互。尽管大多数 ICall 交互在 BLE5-Stack API(例如 GAP,HCI 等)中已经被抽象化(已经被封装为消息原语函数),但是还是需要理解它们在 BLE-Stack 和多线程 RTOS 环境中正常运行的基础架构。
  
-**ICALL**源码在应用工程(例如我们这里的simple_peripheral)的 ICALL BLE/ICALL 文件夹路径。+** ICALL **源码在应用工程(例如本文的 simple_peripheral )的 ICALL BLE/ICALL 文件夹路径。
  
-![图32. ICall 应用-协议栈 抽象](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/image68.jpeg)+![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/image68.jpeg)
  
-### 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 分发时才能唤醒处理;而另外一些 API 却只有等待应用任通过 ICAlL(事件更新)异步返回结果。
  
-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中注册。+对于使用 BLE5 的客户端(例如这里的应用程序任务),客户端必须首先向ICall注册其任务 。该注册通常发生在应用程序的任务初始化功能中。下面的代码片段是 `simple_peripheral_init` 在 simple_peripheral.c 中注册。
  
 ```C ```C
 //ICALL客户端注册 //ICALL客户端注册
-ICall _registerApp (&selfEntity ,syncEvent +ICall _registerApp (&selfEntity ,&syncEvent)
 ``` ```
-完成客户端注册前需要传入结构体变量`selfEntity`和`syncEvent`,其值在函数返回时被初始化,以后服务端通过这两个变量来进行消息传递。`syncEvent`参数表示事件标识,`selfEntity`表示处理消息的目的任务,也是客户端实体以后通信的源地址每个注册ICALL的客户端都需要使用唯一的`syncEvent`和`selfEntity`。+完成客户端注册前需要传入结构体变量 `selfEntity` 和 `syncEvent` 其值在函数返回时被初始化,服务端通过这两个变量来进行消息传递。`syncEvent` 参数表示事件标识,`selfEntity` 表示处理消息的目的任务,也是客户端实体以后通信的源地址每个注册 ICALL 的客户端都需要使用唯一的 `syncEvent` 和`selfEntity` 。
  
-> **注意**: 在客户端注册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` 为用户选择要阻塞/挂起的事件标志。`timeout` 是以毫秒为单位的超时周期。如果在此超时时间范围内后尚未返回,该函数将返回。 +`handle` 是构造的 Event_Handle 事件句柄(标识符)。 
 +`andMask` 和 `orMask` 为用户选择要阻塞/挂起的事件标志。 
 +`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()调用后获得。 
  
-> **危险**:  不要从协议栈回调中调用ICall函数此操作可能导致ICall中止(使用ICall_abort())并中断系统。+以上的事件句柄 `handle` 由服务端 ICall _enrollService()和 ICall _registerApp()调用后获得。 
 + 
 +> **危险**:  不要从协议栈回调中调用 ICall 函数此操作可能导致 ICall 中止(使用 ICall_abort())并中断系统。 
 + 
 +### 示例 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 调度程序它在 BLE5-Stack 上下文中调度和执行;同时应用程序线程阻塞,直到接收到相应的命令状态消息;BLE5-Stack 完成执行命令,然后通过 ICall 将命令状态消息响应发送回应用程序线程。这种交换的示例图如下所示
  
-ICall _init()完成初始化 ICall模块实例,而 ICall_createRemoteTasks()使用已知地址的入口函数为协议栈创建任务。+![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/IcallBlcok.png)
  
-初始化后的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://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/IcallBlcok.png)+* 直接走读 App 代码,以下代码片段创建了协议栈任务:
  
-### BLE-Stack 工程是如何作为App中TI-RTOS一个任务运行的 ### 
-* 直接走读App代码,以下代码片段创建了协议栈任务。 
 ```C ```C
 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\source\ti\ble5stack\icall\src\icall.c  ICall_createRemoteTasks  Line 519 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\source\ti\ble5stack\icall\src\icall.c  ICall_createRemoteTasks  Line 519
行 197: 行 219:
     params.arg1 = (UArg) ICall_getInitParams(i);     params.arg1 = (UArg) ICall_getInitParams(i);
 ``` ```
-并且该任务的任务函数实体ICall_taskEntry是通过arg0  参数强制转换得到。+ 
 +* 该任务的任务函数实体 ICall_taskEntry 是通过 arg0  参数强制转换得到 
 ```C ```C
 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\source\ti\ble5stack\icall\src\icall.c ICall_taskEntry  Line 482 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\source\ti\ble5stack\icall\src\icall.c ICall_taskEntry  Line 482
行 206: 行 230:
 } }
 ``` ```
 +
 * 任务实体 * 任务实体
-对于arg0的参数地址,我们就要区分工程编译选项了。这点我们已经在<a href ="..\..\cc2640r2f_architecture\software_and_hardware\software\software_architecture.html">软件架构</a>章节详细讲解过。对于`FlashROM_StackLirary`这里直接链接的编译在静态库中的`startup_entry`函数。+ 
 +arg0 的参数地址要区分工程编译选项这点已经在<a href ="http://docs.leconiot.com/doku.php?id=cc2640r2f:cc2640r2f_architecture:software_and_hardware:software:software_architecture">软件架构</a>章节详细讲解过。`FlashROM_StackLirary` 链接的编译在静态库中的 `startup_entry` 函数 
 ```C ```C
 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\source\ti\ble5stack\icall\src\icall.c  ICall_createRemoteTasks Line 540 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\source\ti\ble5stack\icall\src\icall.c  ICall_createRemoteTasks Line 540
行 230: 行 257:
 #endif /* STACK_LIBRARY */ #endif /* STACK_LIBRARY */
 ``` ```
-而对于`FlashROM`编译选项这里的`arg0`一个绝对地址,该绝对地址通过边界工具`frontier.exe`计算协议栈工程编译生成的存储映射文件*.map所得并将计算结果保存在应用工程的`iar_boundary.xcl`文件。+ 
 + `FlashROM` 编译选项的 `arg0` 一个绝对地址,该绝对地址通过边界工具 `frontier.exe` 计算协议栈工程编译生成的存储映射文件 *.map 所得并将计算结果保存在应用工程的 `iar_boundary.xcl` 文件 
 ``` ```
 //Project->Options(Alt+F7)->Build Actions //Project->Options(Alt+F7)->Build Actions
 "$TOOLS_BLE$/frontier/frontier.exe" iar "$PROJ_DIR$/$CONFIG_NAME$/List/simple_peripheral_cc2640r2lp_stack.map" "$PROJ_DIR$/../config/iar_boundary.bdef" "$PROJ_DIR$/../config/iar_boundary.xcl" "$TOOLS_BLE$/frontier/frontier.exe" iar "$PROJ_DIR$/$CONFIG_NAME$/List/simple_peripheral_cc2640r2lp_stack.map" "$PROJ_DIR$/../config/iar_boundary.bdef" "$PROJ_DIR$/../config/iar_boundary.xcl"
 ``` ```
 +
 ```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,大概说下结论,对于协议栈工程是作为App工程的一个任务启动的,该任务创建区分协议栈是编译成独立镜像还是静态链接库方式,对于前置通过工具自动计算首地址作为协议栈的任务的入口地址,对于后者直接链接协议栈的入口地址作为协议栈任务的任务实体。+Okay,大概说下结论协议栈工程是作为 App 工程的一个任务启动的,该任务创建区分协议栈是编译成独立镜像还是静态链接库方式。独立镜像方式是通过工具自动计算首地址作为协议栈的任务的入口地址,静态链接库方式是直接链接协议栈的入口地址作为协议栈任务的任务实体。 
 + 
 +### 尝试走读一个 ICALL 的消息流程 ###
  
-### 尝试走读一个ICALL的消息流程 ### 
 * 服务端登记 * 服务端登记
-在协议栈任务中完成ICall服务端的登记,并且通过`ICALL_SERVICE_CLASS_BLE_MSG`作为服务端的源地址。+ 
 +在协议栈任务中完成 ICall 服务端的登记,`ICALL_SERVICE_CLASS_BLE_MSG` 作为服务端的源地址。 
 ```C ```C
 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\src\stack\osal_icall_ble.c  stack_main  Line 229 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\src\stack\osal_icall_ble.c  stack_main  Line 229
行 260: 行 294:
 { {
 ``` ```
 +
 * 客户端注册 * 客户端注册
-客户端根据不同任务进行注册,我们的这里的`SimpleBLEPeripheral_taskFxn`完成该任务的Icall客户端注册,并且返回`selfEntity`作为客户端Icall通信的源地址。+ 
 +客户端根据不同任务进行注册,`SimpleBLEPeripheral_taskFxn` 完成该任务的 Icall 客户端注册,并且返回 `selfEntity` 作为客户端 Icall 通信的源地址。 
 ```C ```C
 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\src\app\simple_peripheral.c  SimpleBLEPeripheral_init  Line 452 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\src\app\simple_peripheral.c  SimpleBLEPeripheral_init  Line 452
行 268: 行 305:
  
 ``` ```
 +
 * 发送消息 * 发送消息
-所有应用程序Icall消息发送都是封装在`icall_directAPI`函数中。+ 
 +所有应用程序 Icall 消息发送都是封装在 `icall_directAPI` 函数中。 
 ```C ```C
 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\src\app\simple_peripheral.c  SimpleBLEPeripheral_init  Line 497 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\src\app\simple_peripheral.c  SimpleBLEPeripheral_init  Line 497
行 280: 行 320:
  
 ``` ```
-而对于`icall_directAPI`,将所有变参封装到一个消息结构体,并且调用`ICall_sendServiceMsg`->`ICall_send`。+ 
 +而对于 `icall_directAPI` ,将所有变参封装到一个消息结构体并且调用 `ICall_sendServiceMsg` -> `ICall_send` 。 
 ```C ```C
 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\source\ti\ble5stack\icall\src\icall.c icall_directAPI Line3819 //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\source\ti\ble5stack\icall\src\icall.c icall_directAPI Line3819
行 296: 行 338:
  
 ``` ```
-最终`ICall_send`是直接将消息放入了目的Icall消息实体的消息队列中,并且触发事件通知该任务唤醒解析处理消息。+ 
 +最终 `ICall_send` 是直接将消息放入了目的 Icall 消息实体的消息队列中并且触发事件通知该任务唤醒解析处理消息。 
 ```C ```C
   //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\source\ti\ble5stack\icall\src\icall.c  Icall_Send Line 2661   //C:\ti\simplelink_cc2640r2_sdk_1_35_00_33\source\ti\ble5stack\icall\src\icall.c  Icall_Send Line 2661
行 302: 行 346:
   ICALL_SYNC_HANDLE_POST(ICall_entities[dest].task->syncHandle);   ICALL_SYNC_HANDLE_POST(ICall_entities[dest].task->syncHandle);
 ``` ```
 +
 ### 如何调试协议栈任务 ### ### 如何调试协议栈任务 ###
-对于协议栈任务,因为分布在另外一个工程,所以我们没有向原来那样直接加断点调试。上面我们详细讲解了协议栈是如何作为一个任务在应用工程中启动的,所以找到协议任务入口地址就是我们调试协议栈任务的关键。 + 
-在调试协议栈任务之前,我们建议将分别设置协议栈和应用工程优化等级Project->Opitons->C/C++ Compiler Optimizations->Level->None 为无,这对我们正常调试协议栈任务至关重要。+因为协议栈任务分布在另外一个工程,所以没有向原来那样直接加断点调试。已经协议栈是如何作为一个任务在应用工程中启动的,那么调试协议栈任务的关键是找到协议任务入口地址。 
 +在调试协议栈任务之前,建议将分别设置协议栈和应用工程优化等级 Project -> Opitons -> C/C++ Compiler Optimizations -> Level -> None 为无,这对正常调试协议栈任务至关重要。
  
 ![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/Opitimaizations.jpg) ![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/Opitimaizations.jpg)
  
-上面我们已经详细讲解过,对于`FlashROM`编译选项协议栈任务入口地址在`iar_boundary.xcl`地址给出,对于`FlashROM_StackLibrary`编译选项,我们可以通过编译生成的*.map文件查找`startup_entry` 符号从而找到入口地址。+图4 .设置优化等级 
 + 
 +`FlashROM` 编译选项协议栈任务入口地址在 `iar_boundary.xcl` 给出,对于 `FlashROM_StackLibrary` 编译选项,可以通过编译生成的 *.map 文件查找`startup_entry` 符号从而找到入口地址。
  
 ![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/FlashRomEntry.jpg) ![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/FlashRomEntry.jpg)
 ![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/LibEntry.jpg) ![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/LibEntry.jpg)
  
-拿到协议栈任务的口地址后,我们就可以在汇编窗口View->Disassembly 直接输入该地址,然后加上断点,等待协议栈任务创建后运行,从而跳转到协议栈工程进行调试。+图5 .找到入口地址 
 + 
 +拿到协议栈任务的口地址后,就可以在汇编窗口 View -> Disassembly 直接输入该地址,然后加上断点,等待协议栈任务创建后运行,从而跳转到协议栈工程进行调试。
  
 ![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/DisassemblyWindow.png) ![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/DisassemblyWindow.png)
  
-当然如果我们不知道协议栈任务的入口地址,直接通过创建协议栈任务的任务实体入口,直接跳转到`ICall_taskEntry`->`entryfn`然后按**F11**进入协议栈任务调试。+图6 .如何用协议栈工程进行调试 
 + 
 +如果不知道协议栈任务的入口地址,直接通过创建协议栈任务的任务实体入口,跳转到 `ICall_taskEntry` -> `entryfn` 然后按** F11 **进入协议栈任务调试。
  
 ![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/EntryFn.png) ![](http://www.leconiot.com/md_res/cc2640r2f/ble_stack_app/app/Images/EntryFn.png)
  
->**注意**:这里F11 Step Into可能会优化选项跳转失败,所以建议关闭优化选项或者直接到聚焦到汇编窗口 BX R2 F11 跳转。+图7 .如何进入协议栈任务调试 
 + 
 +>**注意**:这里 F11 Step Into 可能会优化选项跳转失败,所以建议关闭优化选项或者直接到聚焦到汇编窗口 BX R2 F11 跳转。
  
  
 ## Simple Peripheral Task ## ## Simple Peripheral Task ##
-简单外设任务作为应用程序任务是系统中最低优先级的任务该任务的代码是在Application IDE文件夹中的simple_peripheral 文件夹`simple_peripheral.c`中。+ 
 +简单外设任务是系统中最低优先级的应用程序任务该任务的代码是在 Application IDE 文件夹中的 simple_peripheral 文件夹 下的`simple_peripheral.c` 中。
  
 ### 应用程序初始化功能 ### ### 应用程序初始化功能 ###
  
-<a href="..\..\cc2640r2f_architecture\ti_rtos\tirtos_overivew.html">TI-RTOS概述</a>详细介绍如何了任务构建。构建任务并启动SYS / BIOS内核调度程序后,构造过程中传递的函数会在任务准备就绪时运行(例如 SimpleBLEPeripheral_taskFxn)。任务实体函数运行前这里会先运行任务初始化。+<a href="http://docs.leconiot.com/doku.php?id=cc2640r2f:cc2640r2f_architecture:ti_rtos:tirtos_overivew"> TI-RTOS 概述</a>详细介绍了任务构建。构建任务并启动 SYS / BIOS 内核调度程序后,构造过程中传递的函数会在任务准备就绪时运行(例如 SimpleBLEPeripheral_taskFxn )。任务实体函数运行前这里会先运行任务初始化。 
 + 
 +simple_peripheral 任务函数伪代码
  
-simple_peripheral任务函数伪代码 
 ```C ```C
 static  void  SimpleBLEPeripheral_taskFxn (UArg  a0 , UArg  a1 ) {  static  void  SimpleBLEPeripheral_taskFxn (UArg  a0 , UArg  a1 ) { 
行 343: 行 399:
 } }
 ``` ```
-这个初始化函数(simple_peripheral_init)为任务配置了几个服务,并设置了几个硬件和软件配置设置和参数。以下列表包含一些常见示例: 
  
-* 初始化GATT客户端+初始化函数( simple_peripheral_init )为任务配置了几个服务,并设置了几个硬件和软件配置设置和参数。 
 +以下列表包含一些常见示例: 
 + 
 +* 初始化 GATT 客户端
 * 设置各种配置文件的服务读写等回调函数 * 设置各种配置文件的服务读写等回调函数
-* 设置GAPRole+* 设置 GAPRole
 * 建立 Bond 管理器 * 建立 Bond 管理器
-* 初始化 GAP层 +* 初始化 GAP 层 
-* 配置LCD或SPI等硬件模块。+* 配置 LCD 或 SPI 等硬件模块。
  
->**注意**:在应用程序初始化函数中,调用任何协议栈API之前必须调用ICall _registerApp()完成注册。+>**注意**:在应用程序初始化函数中,调用任何协议栈 API 之前必须调用 ICall _registerApp() 完成注册。
  
  
 ### 任务功能中的事件处理 ### ### 任务功能中的事件处理 ###
-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:
 } }
 ``` ```
->**注意**:在当前代码中,如果事件来自BLE5-Stack ,则pEvt->signature总是等于0xFFFF。 
  
-当为一个事件选择一个事件值的时候,该值对于给定的任务必须是唯一的,并且必须是2的幂(只有1bit被设置为1)。因`pEvt->event`变量被初始化为 `uint16_t`类型,也就是最多运行允许16个事件。有一个不能使用的事件值是已经用于BLE5-Stack OSAL全局事件(bcomdef.h中所述)的事件值+>**注意**:在当前代码中,如果事件来自 BLE5-Stack ,则 pEvt->signature 总是等于 0xFFFF 。 
 + 
 +当为一个事件选择一个事件值的时候,该值对于给定的任务必须是唯一的,并且必须是 2 的幂(只有 1 bit 被设置为 1 )。这样做的原是 `pEvt->event` 变量被初始化为 `uint16_t` 类型,也就是最多运行允许 16 个事件。有一个不能使用的事件值是已经用于 BLE5-Stack OSAL 全局事件( bcomdef.h 中所述)的事件值。 
 + 
 +清单1. BLE OSAL事件在 bcomdef.h 中定义。
  
-清单49. BLE OSAL事件在bcomdef.h中定义。 
 ```C ```C
 /************************************************************ /************************************************************
行 425: 行 490:
  
 ``` ```
-> **注意**:这些任务间事件是与 `请求和处理协议栈事件` 中提到的内部事件不同的事件集。 
  
-## Intertask消息 ## +> **注意**:这些任务间的事件是与 `请求和处理协议栈事件` 中提到的内部事件不同的事件集。 
-这些消息通过ICall从一个任务(如BLE5-Stack Service/ICALL 服务端)传递给应用程序任务(ICALL 客户端)。+ 
 +## Intertask 消息 ## 
 + 
 +这些消息通过 ICall 从一个任务(如 BLE5-Stack Service/ICALL 服务端)传递给应用程序任务( ICALL 客户端)。
  
 正如以下情形: 正如以下情形:
  
-* 协议栈成功收到发送的无线数据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线程同步中描述了使用情况 有关事件模块的更多文档,请参见“ TI RTOS内核用户指南”。+ 
 +所有 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   UTIL_QUEUE_EVENT_ID // Event_Id_30 #define SBP_QUEUE_EVT   UTIL_QUEUE_EVENT_ID // Event_Id_30
行 465: 行 535:
 } }
 ``` ```
 +
 ### 请求和处理协议栈事件 ### ### 请求和处理协议栈事件 ###
-某些API可以选择在BLE5-Stack中发生特定事件时通知应用程序。启用此类事件通知的API将包含一个`taskEvent`参数。`taskEvent` 对于给定的ICall-aware任务,该值必须是唯一的 。应用程序可以通过检查是否`taskEvent`包含在数据结构`ICall_Stack_Event `的`uint16_t event_flag`变量中来处理所请求的协议栈事件。 
  
->**注意** :event_flag不与的TI-RTOS事件模块事件混淆+某些 API 可以选择在 BLE5-Stack 中发生特定事件时通知应用程序,启用此类事件通知的 API 将包含一个 `taskEvent` 参数。`taskEvent` 对于给定的ICall-aware 任务,值必须是唯一的。应用程序可以通过检查 `taskEvent` 是否包含在数据结构 `ICall_Stack_Event ` 的 `uint16_t event_flag` 变量中来处理所请求的协议栈事件。
  
 +>**注意** :event_flag 不与 TI-RTOS 事件模块事件混淆。
  
-下面的代码片段显示了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函数调用或协议栈API是很危险的,这样的函数调用可能导致中止或未定义的行为。+应用程序代码还包括对协议栈层、配置文件和 TI-RTOS 模块的各种回调函数。为了确保线程的安全性,回调要尽量做最少事情。大部分的处理应该发生在应用程序上下文中。每个回调定义了两个函数,一个是回调本身,另外一个就是在任务环境中被处理回调事件函数。回调不直接处理,通过事件方式到任务中。可以参考 GAPRole 状态改变回调,它是用来处理 GAPRole 状态变化的。 
 + 
 +>**危险**:在回调函数中执行阻塞 TI-RTOS 函数调用或协议栈 API 是很危险的,这样的函数调用可能导致中止或未定义的行为。
  
 simple_peripheral状态变化回调。 simple_peripheral状态变化回调。
 +
 ```C ```C
 static  void  SimpleBLEPeripheral_stateChangeCB (gaprole_States_t  newState ) {  static  void  SimpleBLEPeripheral_stateChangeCB (gaprole_States_t  newState ) { 
行 568: 行 646:
 } }
 ``` ```
-上面的代码片段显示了通过`SimpleBLEPeripheral_gapRoleCBs`和`GAPRole_StartDevice()`。回调只是在队列中放置一个消息来通知应用程序唤醒。一旦回调返回其父任务进入休眠状态。应用程序唤醒从消息队列取出消息处理同时调用以下代码片段。 
  
-simple_peripheral任务环境中的状态变化事件处理。+上面的代码片段是通过 `SimpleBLEPeripheral_gapRoleCBs `和 `GAPRole_StartDevice()` 回调的,回调只是在队列中放置一个消息来通知应用程序唤醒。一旦回调返回其父任务进入休眠状态,应用程序唤醒从消息队列取出消息处理同时调用以下代码片段。 
 + 
 +simple_peripheral 任务环境中的状态变化事件处理。 
 ```C ```C
 static  void  SimpleBLEPeripheral_processStateChangeEvt (gaprole_States_t  newState ) {  static  void  SimpleBLEPeripheral_processStateChangeEvt (gaprole_States_t  newState ) { 
行 576: 行 656:
 } }
 ``` ```
 +
 ## 加入我们 ## ## 加入我们 ##
 +
 文章所有代码、工具、文档开源。加入我们[**QQ群 591679055**](http://shang.qq.com/wpa/qunwpa?idkey=d94f12d37c3b37892af4b757c6dc34bea140f3f3128a8d68e556a3d728148e85)获取更多支持,共同研究CC2640R2F&BLE5.0。 文章所有代码、工具、文档开源。加入我们[**QQ群 591679055**](http://shang.qq.com/wpa/qunwpa?idkey=d94f12d37c3b37892af4b757c6dc34bea140f3f3128a8d68e556a3d728148e85)获取更多支持,共同研究CC2640R2F&BLE5.0。
 <div> <div>
行 585: 行 667:
 </div> </div>
 </markdown> </markdown>
- 
cc2640r2f/application_architecture.1504168239.txt.gz · 最后更改: 2021/06/22 23:14 (外部编辑)