这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
cc2640r2f:tirtos_overivew [2017/09/01 18:33] long |
cc2640r2f:tirtos_overivew [2019/08/29 19:50] jaylee ↷ 页面cc2640r2f:cc2640r2f_architecture:ti_rtos:tirtos_overivew被移动至cc2640r2f:tirtos_overivew |
||
---|---|---|---|
行 1: | 行 1: | ||
< | < | ||
- | # TI-RTOS概述 # | + | |
- | TI-RTOS是CC2640R2F设备上低功耗蓝牙项目的运行环境。TI-RTOS内核是传统SYS/ | + | # TI-RTOS 概述 # |
+ | |||
+ | TI-RTOS 是 CC2640R2F 设备上低功耗蓝牙 | ||
## 线程模块 ## | ## 线程模块 ## | ||
- | TI-RTOS内核管理线程执行的四个不同的任务级别,如图21所示。线程模块列表如下图所示,按照优先级降序排列。 | + | |
+ | TI-RTOS 内核管理线程执行的四个不同的任务级别,按照优先级降序排列。 | ||
* 硬件中断 | * 硬件中断 | ||
行 10: | 行 13: | ||
* 任务 | * 任务 | ||
* 后台空闲功能的空闲任务 | * 后台空闲功能的空闲任务 | ||
+ | |||
+ | 内核管理线程如下图所示: | ||
![](http:// | ![](http:// | ||
- | 图21. TI-RTOS执行线程 | ||
- | 这一节将要介绍四个执行线程以及整个TI-RTOS中用于消息传递和同步的各种结构。在大多数情况下,TI-RTOS函数在util.c(Util)中已被抽象为更高级别的函数。较低级的TI-RTOS函数在**TI-RTOS Kernel API Guide**中有描述,你可以在[TI-RTOS内核用户指南](http:// | + | |
+ | 这一节将要介绍四个执行线程以及整个 TI-RTOS 中用于消息传递和同步的各种结构。在大多数情况下,TI-RTOS 函数在 util.c(Util)中已被抽象为更高级别的函数。较低级的 TI-RTOS 函数在** TI-RTOS Kernel API Guide **中有描述,你可以在[ TI-RTOS 内核用户指南](http:// | ||
### 硬件中断(Hwi) ### | ### 硬件中断(Hwi) ### | ||
- | Hwi线程(也称为中断服务程序或ISR)是TI-RTOS应用程序中具有最高优先级的线程。Hwi线程用于执行有严格时间限制的关键任务。它们被触发以响应在实时环境中发生的外部异步事件(中断)。Hwi线程总是运行至完成,但是如果有其他的Hwi中断使能,它也可以暂时地被其他中断触发的Hwi线程抢占,这就是所谓的中断嵌套。有关中断嵌套,向导和功能的具体信息,可以在[CC26XX技术参考手册](http:// | + | Hwi 线程(也称为中断服务程序或 ISR )是 TI-RTOS 应用程序中具有最高优先级的线程。Hwi 线程用于执行有严格时间限制的关键任务,它们被触发以响应在实时环境中发生的外部异步事件(中断)。Hwi 线程总是运行至完成,但是如果有其他 Hwi 中断使能,它也可以暂时地被其他中断触发的 Hwi 线程抢占,这就是所谓的中断嵌套。有关中断嵌套、向导和功能的具体信息,可以在[CC26XX技术参考手册](http:// |
- | 一般来说中断服务程序运行时间较短,不影响硬实时系统的要求。另外,由于Hwis总是运行至完成,所以在在其上下文中不会调用阻塞API。 | + | 一般来说中断服务程序运行时间较短,不影响硬实时系统的要求。另外,由于 |
- | 中断的TI-RTOS驱动程序将初始化分配的外设所需的中断。有关详细信息,请参阅[外设驱动](http:// | + | 中断的 TI-RTOS 驱动程序将初始化分配外设所需的中断。有关详细信息,请参阅[外设驱动](http:// |
- | >注意: | + | >**注意**: SDK 提供了使用 GPIO 和 Hwis 的示例。虽然 SDK 包含一个 |
- | > | + | |
- | CC2640R2F的Hwi模块还支持**零延迟中断**。这些中断不通过TI-RTOS Hwi调度程序,因此比标准中断更灵敏,但是该功能禁止其中断服务程序直接调用任何TI-RTOS内核API。ISR要保护自己的上下文防止它干扰内核的调度程序。 | + | CC2640R2F 的 Hwi 模块还支持**零延迟中断**,这些中断不通过 TI-RTOS Hwi 调度程序,因此比标准中断更灵敏。但是该功能禁止其中断服务程序直接调用任何 TI-RTOS 内核 API 。ISR 要保护自己的上下文防止它干扰内核的调度程序。 |
- | 为了能让低功耗蓝牙协议栈满足RF严格的时序要求,所有应用程序定义的Hwis都要以最低优先级执行。TI向系统添加新的Hwis时,建议不要修改默认的Hwi优先级。为了不破坏低功耗蓝牙协议栈中TI-RTOS的严格时序,应用程序中定义了临界段代码。执行临界段代码时中断会被关闭,在临界段代码执行完毕之后才会重新启用中断。 | + | 为了能让低功耗蓝牙协议栈满足 RF 严格的时序要求,所有应用程序定义的 Hwis(硬件中断线程)都要以最低优先级执行。TI 向系统添加新的 Hwis 时,建议不要修改默认的 Hwi 优先级。为了不破坏低功耗蓝牙协议栈中 TI-RTOS 的严格时序,应用程序中定义了临界段代码。执行临界段代码时中断会被关闭,在临界段代码执行完毕之后才会重新启用中断。 |
### 软件中断(Swi) ### | ### 软件中断(Swi) ### | ||
- | 软件中断线程(Swis)是在Hwi线程和任务线程之间提供的一个额外的优先级。与Hwis由硬件中断触发不同,Swis是通过在程序编写过程中调用某些Swi模块的API来触发中断。由于时间限制,Swis中断服务程序不能作为任务来运行,其截止时间不如硬件中断服务程序那么严格。Swi也总是运行至完成,它允许Hwis将不太重要的中断处理放到较低优先级的线程来处理,从而最小化CPU在中断服务程序中花费的时间。Swis需要足够的空间来保存每个Swi中断优先级的上下文,它的每个线程都使用单独的堆栈。 | + | 软件中断线程( Swis )是在 Hwi 线程和任务线程之间提供的一个额外的优先级。与 Hwis 由硬件中断触发不同,Swis 是通过在程序编写过程中调用某些 Swi 模块的 API 来触发中断。由于时间限制,Swis 中断服务程序不能作为任务来运行,其截止时间不如硬件中断服务程序那么严格。Swi 也总是运行至完成,它允许 Hwis将不太重要的中断处理放到较低优先级的线程来处理,从而最小化 CPU 在中断服务程序中花费的时间。 |
+ | |||
+ | Swis 需要足够的空间来保存每个 Swi 中断优先级的上下文,它的每个线程都使用单独的堆栈。 | ||
+ | |||
+ | 与 Hwis 类似,Swis 需要保持简短,它不应该包含任何阻塞 API 的调用。这保证了诸如无线协议栈等高优先级任务能根据需要执行。在 Swis 中建议发布某些 TI-RTOS 同步对象,然后把具体处理放在任务上下文中。 | ||
+ | 下图说明了这种用例。 | ||
- | 与Hwis类似,Swis需要保持简短,它不应该包含任何阻塞API的调用。这保证了诸如无线协议栈等高优先级任务能根据需要执行。在Swis中建议发布某些TI-RTOS同步对象,然后把具体处理放在任务上下文中。图22说明了这种用例。 | ||
![](http:// | ![](http:// | ||
- | 图22. 抢占情景 | ||
- | Swi上下文中常常会由时钟模块调用,对于Swi服务函数,不调用阻塞API,保证较短的执行时间是很重要的。 | + | Swi 上下文中常常会由时钟模块调用,Swi 服务函数不调用阻塞 API,保证较短的执行时间是很重要的。 |
### 任务 ### | ### 任务 ### | ||
- | 任务线程的优先级高于空闲任务线程,低于软件中断。任务与软件中断的不同之处在于,任务可以在执行期间等待(阻塞),直到有必要的资源可用。每个任务线程都有一个独立的堆栈。TI-RTOS提供了可用于任务间通信和同步的多种机制。这些包括信号量,事件,消息队列和邮箱。 | + | 任务线程的优先级高于空闲任务线程,低于软件中断。任务与软件中断的不同之处是:任务可以在执行期间等待(阻塞),直到有必要的资源可用。每个任务线程都有一个独立的堆栈。TI-RTOS 提供了可用于任务间通信和同步的多种机制。这些包括信号量、事件、消息队列和邮箱。 |
有关详细信息,可以在本文后面的**任务**中查看。 | 有关详细信息,可以在本文后面的**任务**中查看。 | ||
### 空闲任务 ### | ### 空闲任务 ### | ||
- | 空闲任务线程在TI-RTOS应用程序中优先级最低,它会执行一个空闲循环。在主函数返回之后,TI-RTOS应用程序会调用每个TI-RTOS模块的启动程序,然后进入空闲循环。每个线程在被再次调用之前都必须等待所有其他线程执行完成。空闲循环在没有更高优先级的任务需要执行的时候会一直执行。只有没有严格截止期限的函数才能在空闲循环中执行。 | + | 空闲任务线程在 TI-RTOS 应用程序中优先级最低,它会执行一个空闲循环。在主函数返回之后,TI-RTOS 应用程序会调用每个 TI-RTOS 模块的启动程序,然后进入空闲循环。每个线程在被再次调用之前都必须等待所有其他线程执行完成。空闲循环在没有更高优先级的任务需要执行的时候会一直执行。只有没有严格截止期限的函数才能在空闲循环中执行。 |
- | 对于CC2640R2F设备,空闲任务支持电源策略管理器设置为允许的最低功率节省模式。 | + | 对于 CC2640R2F 设备,空闲任务支持电源策略管理器设置为允许的最低功率节省模式。 |
## 内核配置 ## | ## 内核配置 ## | ||
- | TI-RTOS应用程序可以使用工程中的`.cfg`文件来配置TI-RTOS内核。在**IAR**和**CCS**工程中,配置文件在应用程序项目工作区中的`TOOLS`文件夹下。 | + | TI-RTOS 应用程序可以使用工程中的 `.cfg` 文件来配置 TI-RTOS 内核。在** IAR **和** CCS **工程中,配置文件在应用程序项目工作区中的 `TOOLS` 文件夹下。 |
- | 该配置通过选择性地包括或使用可用于内核的RTSC模块来实现。`.cfg`中通过调用`xdc.useModule()`函数来设置[TI-RTOS内核用户指南](http://www.leconiot.com/md_res/cc2640r2f/cc2640r2f_architecture/ti_rtos/files/ | + | 该配置通过选择性地包括或使用可用于内核的 RTSC 模块来实现。`.cfg` 中通过调用 `xdc.useModule()` 函数来设置[ TI-RTOS 内核用户指南](file:///C:/ |
> | > | ||
- | > | + | > BLE5-Stack 中的项目(如 `simple_peripheral` )通常会包含一个 app_ble.cfg 配置文件。 |
- | 可以在`.cfg`文件中配置的一些选项(但不限于这些): | + | 可以在 `.cfg` 文件中配置的一些选项(但不限于这些): |
* 启动选项 | * 启动选项 | ||
- | * Hwi,Swi和任务优先级的数量 | + | * Hwi、Swi 和任务优先级的数量 |
* 异常和错误处理 | * 异常和错误处理 | ||
- | * 系统刻度的持续时间(TI-RTOS内核中最基本的时间单位)。 | + | * 系统刻度的持续时间( TI-RTOS 内核中最基本的时间单位)。 |
* 定义应用程序的入口点和中断向量 | * 定义应用程序的入口点和中断向量 | ||
- | * TI-RTOS堆和堆栈(不要与其他堆管理器混淆!) | + | * TI-RTOS 堆和堆栈(不要与其他堆管理器混淆!) |
- | * 包括预编译的内核和TI-RTOS驱动程序库 | + | * 包括预编译的内核和 TI-RTOS 驱动程序库 |
- | * 系统配置(for `System_printf()`) | + | * 系统配置(for `System_printf()` ) |
- | 一旦`.cfg`文件发生变动时,您需要重新运行XDCTools的`configuro`工具。在IAR和CCS提供的示例中这一步作为预构建步骤已经为您提供。 | + | 一旦 `.cfg` 文件发生变动时,您需要重新运行 XDCTools 的 `configuro` 工具。在 IAR 和 CCS 提供的示例中这一步作为预构建步骤已经为您提供。 |
>注意: | >注意: | ||
- | > | + | > `.cfg` 的名称并不重要,但是项目只能包含一个 `.cfg` 文件。 |
- | 对于CC2640R2F,TI-RTOS内核存储在ROM中。通常为了节省Flash的访问足迹,`.cfg`也会包含在内核的ROM模块中,如清单1所示。 | + | CC2640R2F |
+ | |||
+ | 清单1. 如何把 TI-RTOS 内核包含到 ROM 中 | ||
- | 清单1. 如何把TI-RTOS内核包含到ROM中 | ||
```C | ```C | ||
/ * ================ ROM configuration================ * / | / * ================ ROM configuration================ * / | ||
行 95: | 行 103: | ||
``` | ``` | ||
- | ROM中的TI-RTOS内核针对性能进行了优化。如果您的应用程序需要额外的工具(通常用于调试),则必须将TI-RTOS内核包含在Flash中,这将增加Flash消耗。下面显示的是在`ROM`中使用TI-RTOS内核的简要列表。 | + | ROM 中的 TI-RTOS 内核针对性能进行了优化。如果您的应用程序需要额外的工具(通常用于调试),则必须将 TI-RTOS 内核包含在 Flash 中,这将增加 Flash 消耗。 |
- | * `BIOS.assertsEnabled`必须设置为`false` | + | 下面显示的是在 `ROM` 中使用 TI-RTOS 内核的简要列表。 |
- | * `BIOS.logsEnabled`必须设置为`false` | + | |
- | * `BIOS.taskEnabled`必须设置为`true` | + | * `BIOS.assertsEnabled` 必须设置为 `false` |
- | * `BIOS.swiEnabled`必须设置为`true` | + | * `BIOS.logsEnabled` 必须设置为 `false` |
- | * `BIOS.runtimeCreatesEnabled` 必须设置为`true` | + | * `BIOS.taskEnabled` 必须设置为 `true` |
- | * BIOS必须使用该`ti.sysbios.gates.GateMutex`模块 | + | * `BIOS.swiEnabled` 必须设置为 `true` |
- | * `Clock.tickSource`必须设置为`Clock.TickSource_TIMER` | + | * `BIOS.runtimeCreatesEnabled` 必须设置为 `true` |
- | * `Semaphore.supportsPriority`一定是`false` | + | * BIOS 必须使用该 `ti.sysbios.gates.GateMutex` 模块 |
- | * Swi,Task和Hwi hooks不容许 | + | * `Clock.tickSource` 必须设置为 `Clock.TickSource_TIMER` |
- | * Swi,Task和Hwi name instances不容许 | + | * `Semaphore.supportsPriority` 一定是 `false` |
+ | * Swi、Task 和 Hwi hooks 不容许 | ||
+ | * Swi、Task 和 Hwi name instances 不容许 | ||
* 任务堆栈检查被禁用 | * 任务堆栈检查被禁用 | ||
- | * `Hwi.disablePriority`必须设置为`0x20` | + | * `Hwi.disablePriority` 必须设置为 `0x20` |
- | * `Hwi.dispatcherAutoNestingSupport`必须设置为`true` | + | * `Hwi.dispatcherAutoNestingSupport` 必须设置为 `true` |
- | * 默认的Heap instance必须设置为`ti.sysbios.heaps.HeapMem`管理者 | + | * 默认的 Heap instance 必须设置为 `ti.sysbios.heaps.HeapMem` 管理者 |
- | 有关上述列表外的其他文档,可以在[TI-RTOS内核用户指南](http://www.leconiot.com/md_res/cc2640r2f/cc2640r2f_architecture/ti_rtos/files/ | + | 有关上述列表外的其他文档,可以在[ TI-RTOS 内核用户指南](file:///C:/ |
## 创造与构建 ## | ## 创造与构建 ## | ||
- | 大多数TI-RTOS模块通常都有`_create()`和`_construct()`APIs用来初始化最初的例程。这两个API之间运行时的主要差异是内存分配和错误处理。 | + | |
- | 在初始化之前,**创建**API会从默认的TI-RTOS堆执行内存分配。因此,应用程序必须在继续之前检查有效句柄的返回值。 | + | 大多数 TI-RTOS 模块通常都有 `_create()` 和 `_construct()` APIs 用来初始化最初的例程,这两个 API 之间运行时的主要差异是内存分配和错误处理。 |
+ | 在初始化之前,**创建** API 会从默认的 TI-RTOS 堆执行内存分配。因此,应用程序必须在继续之前检查有效句柄的返回值。 | ||
清单2. 创建一个信号量 | 清单2. 创建一个信号量 | ||
+ | |||
``` | ``` | ||
1 Semaphore_Handle sem; | 1 Semaphore_Handle sem; | ||
行 131: | 行 143: | ||
10 | 10 | ||
``` | ``` | ||
- | **构造**API提供了一个用于存储实例变量的数据结构。由于内存已被预先分配给实例,构建后不再需要进行错误检查。 | + | |
+ | **构造** API 提供了一个用于存储实例变量的数据结构。由于内存已被预先分配给实例,构建后不再需要进行错误检查。 | ||
清单3. 构造一个信号量 | 清单3. 构造一个信号量 | ||
+ | |||
```C | ```C | ||
1 Semaphore_Handle | 1 Semaphore_Handle | ||
行 147: | 行 161: | ||
## 线程同步 ## | ## 线程同步 ## | ||
- | TI-RTOS内核提供了几个诸如信号量,事件和队列用于同步任务的模块。以下部分讨论这些常见的TI-RTOS同步模块。 | + | |
+ | TI-RTOS 内核提供了几个诸如信号量、事件和队列用于同步任务的模块。以下部分讨论这些常见的 TI-RTOS 同步模块。 | ||
### 信号量 ### | ### 信号量 ### | ||
- | 信号量通常用于TI-RTOS应用中的任务同步和互斥。图23.显示了信号量的功能。信号量可以分为计数信号量或二进制信号量。程序通过`Semaphore_post()`来释放信号量,计数信号量会记录跟踪信号量发布的次数。当一组资源在任务之间共享时,信号量很有用。在使用这些资源之前,任务会调用`Semaphore_pend()`来查看资源是否可用,只有共享资源被释放出来之后处于等待状态的任务得到该资源才能执行。二进制信号量只能有两种状态:可用(count = 1)和不可用(count = 0)。二进制信号量可用于在任务之间共享一个资源,或者用于基本信令机制,可以多次发布信号量。二进制信号不跟踪计数, 他们只跟踪信号量是否已经发布。 | + | 信号量通常用于 TI-RTOS 应用中的任务同步和互斥。下图显示了信号量的功能。 |
![](http:// | ![](http:// | ||
- | 图23. | + | |
+ | 信号量可以分为计数信号量或二进制信号量。程序通过 `Semaphore_post()` 来释放信号量,计数信号量会记录跟踪信号量发布的次数。当一组资源在任务之间共享时,信号量很有用。在使用这些资源之前,任务会调用 `Semaphore_pend()` 来查看资源是否可用,只有共享资源被释放出来之后处于等待状态的任务得到该资源才能执行。 | ||
+ | |||
+ | 二进制信号量只能有两种状态:可用(count = 1)和不可用(count = 0)。二进制信号量可用于在任务之间共享一个资源,或者用于基本信令机制,可以多次发布信号量。二进制信号不跟踪计数, 他们只跟踪信号量是否已经发布。 | ||
#### 初始化信号量 #### | #### 初始化信号量 #### | ||
- | 以下代码描述了如何在TI-RTOS中初始化信号量。信号量可以创建和构造,如本文上面的**创建与构造**中所述。 | + | 以下代码描述了如何在 TI-RTOS 中初始化信号量。信号量可以创建和构造,如本文上面的**创建与构造**中所述。 |
有关如何创建信号量,请参见清单2。 | 有关如何创建信号量,请参见清单2。 | ||
有关如何构造信号量,请参见清单3。 | 有关如何构造信号量,请参见清单3。 | ||
行 163: | 行 183: | ||
#### 等待信号量 #### | #### 等待信号量 #### | ||
- | `Semaphore_pend()`是一个阻塞函数调用。它只能在任务中调用。当任务调用此阻塞函数后将会等待信号量的释放(post),这时就绪的低优先级任务可以执行。调用`Semaphore_pend()`如果计数器为0,任务将阻塞,否则计数器会递减1,任务执行。在另一个线程调用`Semaphore_post()`释放信号量或者提供的系统滴答时钟超时之前,任务都会保持阻塞状态。通过读取其返回值`Semaphore_pend()`可以区分信号量是否发布或超时。 | + | `Semaphore_pend()` 是一个阻塞函数调用,它只能在任务中调用。当任务调用此阻塞函数后将会等待信号量的释放( post ),这时就绪的低优先级任务可以执行。调用 `Semaphore_pend()` 如果计数器为 0 ,任务将阻塞,否则计数器会递减 1 ,任务执行。 |
+ | |||
+ | 在另一个线程调用 `Semaphore_post()` 释放信号量或者提供的系统滴答时钟超时之前,任务都会保持阻塞状态。通过读取其返回值 `Semaphore_pend()` 可以区分信号量是否发布或超时。 | ||
清单4. 等待一个信号量 | 清单4. 等待一个信号量 | ||
+ | |||
```C | ```C | ||
1 bool isSuccessful; | 1 bool isSuccessful; | ||
行 184: | 行 207: | ||
>注意: | >注意: | ||
- | > | + | > |
- | > | + | >置为 10 微秒。 |
- | > | + | > |
#### 发布信号量 #### | #### 发布信号量 #### | ||
- | 信号量的发布是通过调用`Semaphore_post()`完成的。在此发布的信号量上挂起的任务将从阻塞状态转换到就绪状态。如果此时正好没有更高优先级的线程准备运行,得到该信号量的任务将会运行。如果信号量上没有挂起任务,调用Semaphore_post()将时信号量计数器加1。二进制信号量的最大计数为1。 | + | 信号量的发布是通过调用 `Semaphore_post()` 完成的。在发布信号量上挂起的任务将从阻塞状态转换到就绪状态。如果此时正好没有更高优先级的线程准备运行,得到该信号量的任务将会运行。如果信号量上没有挂起任务,调用 Semaphore_post() 将时信号量计数器加 1 ,二进制信号量的最大计数为 1 。 |
清单5. 发布信号量 | 清单5. 发布信号量 | ||
+ | |||
```C | ```C | ||
1 Semaphore_post (sem ); | 1 Semaphore_post (sem ); | ||
行 198: | 行 223: | ||
### 事件 ### | ### 事件 ### | ||
- | 信号量提供了线程之间的基本同步。有些情况下,信号量本身就足够了解什么进程需要触发。然而有些同步的特定原因也需要跨线程传递。为了实现这一点,可以使用TI-RTOS 事件(Event)模块。事件类似于信号量,每个事件实际上都包含一个信号量。使用事件的附加优点在于可以以线程安全的方式向任务通知特定事件。 | + | 信号量提供了线程之间的基本同步。有些情况下,信号量本身就足够了解什么进程需要触发。然而有些同步的特定原因也需要跨线程传递。为了实现这一点,可以使用TI-RTOS 事件(Event)模块。 |
+ | |||
+ | 事件类似于信号量,每个事件实际上都包含一个信号量。使用事件的附加优点在于可以以线程安全的方式向任务通知特定事件。 | ||
#### 初始化事件 #### | #### 初始化事件 #### | ||
- | 创建和构建事件同样遵循本文上面的**创建与构建**中说明的准则。如清单6所示,是一个关于如何构造Event实例的例子。 | + | 创建和构建事件同样遵循本文上面的**创建与构建**中说明的准则。如清单6所示,是一个关于如何构造 Event 实例的例子。 |
清单6. 构造事件 | 清单6. 构造事件 | ||
+ | |||
``` | ``` | ||
1 Event_Handle event; | 1 Event_Handle event; | ||
行 219: | 行 247: | ||
#### 事件等待 #### | #### 事件等待 #### | ||
- | 类似于`Semaphore_pend()`,任务线程会在调用`Event_pend()`时阻塞, 直到事件通过一个`Event_post()`发布或者等待超时。清单7展示了一个任务等待下面显示的3个示例事件ID中的任意一个发布的代码片段。 `BIOS_WAIT_FOREVER`参数设置表示不会等待超时,只要时间没发布会永远等待下去。因此, Event_pend()将在返回的位掩码值中发布一个或多个事件。 | + | 类似于 `Semaphore_pend()` ,任务线程会在调用 `Event_pend()` 时阻塞, 直到事件通过一个 `Event_post()` 发布或者等待超时。清单 7 展示了一个任务等待下面显示的 3 个示例事件 ID 中的任意一个发布的代码片段。 `BIOS_WAIT_FOREVER` 参数设置表示不会等待超时,只要时间没发布会永远等待下去。因此, Event_pend() 将在返回的位掩码值中发布一个或多个事件。 |
- | `Event_pend()`返回的每个事件会以线程安全的方式在事件实例中自动清除。因此,对于发布的事件只需保留本地副本。有关使用`Event_pend()`的详细介绍 ,可以在[TI-RTOS内核用户指南](http://www.leconiot.com/md_res/cc2640r2f/cc2640r2f_architecture/ti_rtos/files/ | + | `Event_pend()` 返回的每个事件会以线程安全的方式在事件实例中自动清除,对于发布的事件只需保留本地副本。有关使用 `Event_pend()` 的详细介绍 ,可以在[ TI-RTOS 内核用户指南](file:///C:/ |
清单7. 事件挂起 | 清单7. 事件挂起 | ||
+ | |||
```C | ```C | ||
1 #define START_ADVERTISING_EVT | 1 #define START_ADVERTISING_EVT | ||
行 264: | 行 293: | ||
#### 事件发布 #### | #### 事件发布 #### | ||
- | 事件可以从一些TI-RTOS内核任务中发布(Post),通过调用`Event_post()`就可以发布事件实体和事件ID。清单8.显示了高优先级线程(如Swi)如何发布特定事件。 | + | 事件可以从一些 TI-RTOS 内核任务中发布(Post),通过调用 `Event_post()` 就可以发布事件实体和事件 ID 。清单8.显示了高优先级线程(如 Swi )如何发布特定事件。 |
清单8. 发布事件 | 清单8. 发布事件 | ||
+ | |||
```C | ```C | ||
1 #define START_ADVERTISING_EVT | 1 #define START_ADVERTISING_EVT | ||
行 281: | 行 311: | ||
### 队列 ### | ### 队列 ### | ||
- | TI-RTOS队列是一个基于先入先出(FIFO),线程安全的单向消息传递模块。队列通常用于高优先级线程将消息传递给较低优先级的线程以进行延迟处理;因此允许低优先级任务阻塞直到需要运行。 | + | TI-RTOS 队列是一个基于先入先出(FIFO)、线程安全的单向消息传递模块。队列通常用于高优先级线程将消息传递给较低优先级的线程以进行延迟处理,因此允许低优先级任务阻塞直到需要运行。 |
+ | |||
+ | 在下图中,队列被配置为从任务 A 到任务 B 的单向通信。任务 A 将消息**放入**到队列中,任务 B 从队列**获取**消息。 | ||
- | 在图24中,队列被配置为从任务A到任务B的单向通信。任务A将消息**放入**到队列中,任务B从队列**获取**消息。 | ||
![](http:// | ![](http:// | ||
- | 图24. 队列消息传递过程 | ||
- | 在BLE5-Stack中,TI-RTOS队列功能在`util.c`中已经被抽象为接口函数,在[TI-RTOS内核用户指南](http://www.leconiot.com/md_res/cc2640r2f/cc2640r2f_architecture/ti_rtos/files/ | + | BLE5-Stack 中 TI-RTOS 队列功能在 `util.c` 中已经被抽象为接口函数,在[ TI-RTOS 内核用户指南](file:///C:/ |
+ | |||
+ | 在 CC2640R2F 软件中,ICall 使用来自各自模块的队列和事件实现应用程序和协议栈任务之间的消息传递。高优先级任务,Hwi 或 Swi 将消息发布到队列中传递给应用程序任务。当没有其他高优先级线程运行时,应用程序任务将在其上下文中处理此消息。 | ||
- | 在CC2640R2F软件中,ICall使用来自各自模块的队列和事件在应用程序和协议栈任务之间传递消息。高优先级任务,Hwi或Swi将消息发布到队列中传递给应用程序任务。然后,当没有其他高优先级线程运行时,应用程序任务将在其上下文中处理此消息。 | + | `util` |
- | `util`模块包含一组抽象的TI-RTOS队列功能,如下所示: | ||
* Util_constructQueue()创建一个队列。 | * Util_constructQueue()创建一个队列。 | ||
* Util_enqueueMsg()将项目放入队列。 | * Util_enqueueMsg()将项目放入队列。 | ||
行 298: | 行 329: | ||
#### 功能实例 #### | #### 功能实例 #### | ||
- | 图25和图26说明了队列如何将按键消息从Hwi排入队列(到Swi中的板卡按键模块),然后在任务上下文中发布后处理。这个实例来自于BLE5-Stack中的simple_central工程。 | + | 下图说明了队列如何将按键消息从 Hwi 排入队列(到 Swi 中的板卡按键模块),然后在任务上下文中发布后处理。这个实例来自于 BLE5-Stack 中的 simple_central 工程。 |
![](http:// | ![](http:// | ||
- | 图25. 消息加入队列的时序图 | ||
- | 在中断使能的情况下,引脚中断可能会在Hwi上下文中异步发生 。为了使中断尽可能短,与中断相关的工作推迟到任务来处理。在BLE5-Stack中的simple_central示例中,引脚中断通过板卡按键模块进行抽象。该模块通过Swi注册回调通知函数 。在这种情况下,`SimpleBLECentral_keyChangeHandler`是注册的回调函数。 | ||
- | **图25中的步骤1**:展示了当键按下的时候回调`SimpleBLECentral_keyChangeHandler`。该事件被放入应用程序的队列中等待处理。 | + | 在中断使能的情况下,引脚中断可能会在 Hwi 上下文中异步发生。为了使中断尽可能短,与中断相关的工作推迟到任务来处理。在 BLE5-Stack 中的 simple_central 示例中,引脚中断通过板卡按键模块进行抽象。该模块通过 Swi 注册回调通知函数 。在这种情况下, `SimpleBLECentral_keyChangeHandler` 是注册的回调函数。 |
+ | |||
+ | **上图中的步骤 1 **:展示了当键按下的时候回调 `SimpleBLECentral_keyChangeHandler` 。该事件被放入应用程序的队列中等待处理。 | ||
清单9. 定义SimpleBLECentral_keyChangeHandler() | 清单9. 定义SimpleBLECentral_keyChangeHandler() | ||
+ | |||
```C | ```C | ||
1 void SimpleBLECentral_keyChangeHandler(uint8 keys) | 1 void SimpleBLECentral_keyChangeHandler(uint8 keys) | ||
行 311: | 行 344: | ||
3 SimpleBLECentral_enqueueMsg(SBC_KEY_CHANGE_EVT, | 3 SimpleBLECentral_enqueueMsg(SBC_KEY_CHANGE_EVT, | ||
4 } | 4 } | ||
- | ``` | + | ``` |
- | **图25中的步骤2**:显示了按键消息是如何被压入`simple_central`任务的队列中的。首先通过`ICall_malloc()`分配内存,以便消息可以放入队列中。一旦消息添加进队列,Util_enqueueMsg()将发布一个`UTIL_QUEUE_EVENT_ID`事件来通知应用程序进行处理。 | + | |
+ | **上图中的步骤 2 **:显示了按键消息是如何被压入 `simple_central` 任务的队列中的。首先通过 `ICall_malloc()` 分配内存,以便消息可以放入队列中。一旦消息添加进队列,Util_enqueueMsg()将发布一个 `UTIL_QUEUE_EVENT_ID` 事件来通知应用程序进行处理。 | ||
清单10. 定义SimpleBLECentral_enqueueMsg() | 清单10. 定义SimpleBLECentral_enqueueMsg() | ||
+ | |||
```C | ```C | ||
1 static uint8_t SimpleBLECentral_enqueueMsg(uint8_t event, uint8_t state, uint8_t *pData) | 1 static uint8_t SimpleBLECentral_enqueueMsg(uint8_t event, uint8_t state, uint8_t *pData) | ||
行 333: | 行 368: | ||
14 } | 14 } | ||
``` | ``` | ||
+ | |||
![](http:// | ![](http:// | ||
- | 图26. 消息出队的时序图 | + | |
- | **图26中的步骤3**: | + | **上图的步骤 3 **: `simple_central` 应用程序一直在检查是否有消息被放置在队列中需要进行处理,当 `UTIL_QUEUE_EVENT_ID` 事件发布之后,它也就被解除阻塞,开始处理消息。 |
清单11. 处理应用程序消息 | 清单11. 处理应用程序消息 | ||
+ | |||
```C | ```C | ||
1 // If TI-RTOS queue is not empty, process app message | 1 // If TI-RTOS queue is not empty, process app message | ||
行 355: | 行 392: | ||
``` | ``` | ||
- | **图26中的步骤4**:`simple_central`应用取出队列中的消息并对其进行处理。 | + | **上图中的步骤 4 **:`simple_central` 应用取出队列中的消息并对其进行处理。 |
清单12. 处理按键中断消息 | 清单12. 处理按键中断消息 | ||
+ | |||
```C | ```C | ||
1 static void SimpleBLECentral_processAppMsg(sbcEvt_t *pMsg) | 1 static void SimpleBLECentral_processAppMsg(sbcEvt_t *pMsg) | ||
行 371: | 行 409: | ||
``` | ``` | ||
- | **图26中的步骤5**:`simple_central`应用程序处理完消息后可以释放在**步骤2**中所分配的内存。 | + | **上图的步骤5**:`simple_central` 应用程序处理完消息后可以释放在**步骤 2 **中所分配的内存。 |
## 任务 ## | ## 任务 ## | ||
- | TI-RTOS任务(或称为线程)就是一段简单的程序,通常是一段死循环。实际上,将处理器从一个任务切换到另一个任务有助于实现并发。每个任务总是处于以下运行状态之一: | + | |
+ | TI-RTOS 任务(或称为线程)就是一段简单的程序,通常是一段死循环。实际上,将处理器从一个任务切换到另一个任务有助于实现并发。每个任务总是处于以下运行状态之一: | ||
* **运行**:任务当前正在运行 | * **运行**:任务当前正在运行 | ||
* **就绪**:任务准备好等待执行 | * **就绪**:任务准备好等待执行 | ||
* **阻塞**:任务被暂停执行 | * **阻塞**:任务被暂停执行 | ||
* **终止**:任务终止执行 | * **终止**:任务终止执行 | ||
- | * **无效**:任务处于无效列表中(还不受TI-RTOS管理) | + | * **无效**:任务处于无效列表中(还不受 TI-RTOS 管理) |
- | 有且只有一个任务总是在CPU中运行,当然它有可能只是空闲任务( 见图21.)。当前运行的任务可以被某些唤醒的高优先级任务以及其他模块(如Semaphores)的功能阻止执行。当前任务也可以自行终止执行。在任一情况下,处理器都切换到准备运行的最高优先级任务。有关这些功能的更多信息,请参见**TI-RTOS内核用户指南**中TI.sysbios.knl软件包中的任务模块。 | + | 有且只有一个任务总是在 CPU 中运行,当然它有可能只是空闲任务。当前运行的任务可以被某些唤醒的高优先级任务以及其他模块(如 Semaphores )的功能阻止执行,也可以自行终止执行。在任一情况下,处理器都切换到准备运行的最高优先级任务。有关这些功能的更多信息,请参见** TI-RTOS 内核用户指南**中 TI.sysbios.knl 软件包中的任务模块。 |
- | 每个任务都会分配相应的优先级,多个任务可以具有相同的优先级。任务是从最高优先级向最低优先级执行的; 相同优先级的任务按照到达顺序进行执行。当前运行的任务的优先级永远不会低于任何就绪任务的优先级。当有更高优先级的任务就绪时,正在运行的任务才会被抢占并重新安排执行。 | + | 每个任务都会分配相应的优先级,多个任务可以具有相同的优先级。任务是从最高优先级向最低优先级执行的,相同优先级的任务按照到达顺序进行执行。当前运行的任务的优先级永远不会低于任何就绪任务的优先级。当有更高优先级的任务就绪时,正在运行的任务才会被抢占并重新安排执行。 |
- | 在`simple_peripheral`应用中,低功耗蓝牙协议栈任务被给予最高优先级(5),应用任务被给予最低优先级(1)。 | + | 在 `simple_peripheral` 应用中,低功耗蓝牙协议栈任务被给予最高优先级(5),应用任务被给予最低优先级(1)。 |
### 初始化任务 ### | ### 初始化任务 ### | ||
- | 初始化任务时,会给它分配独立的运行堆栈,用于存储局部变量以及进一步嵌套函数调用。在单个程序中执行的所有任务共享一组通用的全局变量,根据C语言标准规范中的函数访问范围进行访问。分配的运行堆栈就是任务的上下文。以下是正在构建的应用程序任务的示例。 | + | 初始化任务时,会给它分配独立的运行堆栈,用于存储局部变量以及进一步嵌套函数调用。在单个程序中执行的所有任务共享一组通用的全局变量,根据 C 语言标准规范中的函数访问范围进行访问。分配的运行堆栈就是任务的上下文。 |
+ | |||
+ | 以下是正在构建的应用程序任务的示例。 | ||
+ | |||
+ | 清单13. TI-RTOS 任务 | ||
- | 清单13. TI-RTOS任务 | ||
```C | ```C | ||
1 #include < | 1 #include < | ||
行 439: | 行 482: | ||
``` | ``` | ||
- | 在启动TI-RTOS内核的调度程序`BIOS_start()`之前,在main()中需要完成所有任务的创建。任务在调度程序启动后按其分配的优先级执行。 | + | 在启动 TI-RTOS 内核调度程序 `BIOS_start()` 之前,main()中需要完成所有任务的创建。任务在调度程序启动后按其分配的优先级执行。 |
- | TI建议尽量使用现有的任务应用程序进行特定应用的处理。在工程中添加额外的任务时,必须在TI-RTOS配置文件(`.cfg`)中定义的TI-RTOS优先级范围内为该任务分配优先级。 | + | TI 建议尽量使用现有的任务应用程序进行特定应用的处理。在工程中添加额外的任务时,必须在 TI-RTOS 配置文件( `.cfg` )中定义的 TI-RTOS 优先级范围内为该任务分配优先级。 |
> | > | ||
- | > | + | > |
- | > | + | > `Clock.tickPeriod = 6;` |
不要添加具有等于或高于低功耗蓝牙协议栈任务优先级的任务。有关系统任务层次结构的详细信息,可以在**标准工程任务层次结构**中查看。 | 不要添加具有等于或高于低功耗蓝牙协议栈任务优先级的任务。有关系统任务层次结构的详细信息,可以在**标准工程任务层次结构**中查看。 | ||
- | 确保任务的堆栈大小至少为512字节的预定义内存。必须分配足够大的堆栈空间来保证程序的正常运行以及任务抢占上下文的存储。任务抢占上下文是当一个任务由于中断产生或者被较高优先级任务抢占而被保存的上下文。使用IDE的TI-RTOS分析工具,可以分析任务以确定任务堆栈使用情况的峰值。 | + | 确保任务的堆栈大小至少为 512 字节的预定义内存,必须分配足够大的堆栈空间来保证程序的正常运行以及任务抢占上下文的存储。任务抢占上下文是当一个任务由于中断产生或者被较高优先级任务抢占而被保存的上下文。使用 IDE 的 TI-RTOS 分析工具,可以分析任务以确定任务堆栈使用情况的峰值。 |
> | > | ||
- | > | + | > |
### 任务功能 ### | ### 任务功能 ### | ||
- | 当任务被初始化时,任务函数的函数指针会传递给`Task_construct`函数。当任务第一次有机会运行时,这个函数就是由TI-RTOS管理运行的函数了。清单13.显示了此任务函数的一般拓扑关系。 | + | 当任务被初始化时,任务函数的函数指针会传递给 `Task_construct` 函数。当任务第一次有机会运行时,这个函数就是由 TI-RTOS 管理运行的函数了。清单13.显示了此任务函数的一般拓扑关系。 |
- | 就典型的使用情况而言,任务大部分时间都通过调用称为`_pend()`的API(例如`Semaphore_pend()`)而处于阻塞状态。通常,高优先级线程(例如Hwis或Swis)使用`_post()`API(例如`Semaphore_post()`)来解除某些任务的阻塞。 | + | 就典型的使用情况而言,任务大部分时间都通过调用称为 `_pend()` 的 API(例如`Semaphore_pend()`)而处于阻塞状态。通常高优先级线程(例如 Hwis 或Swis )使用 `_post()` API(例如 `Semaphore_post()` )来解除某些任务的阻塞。 |
## 时钟 ## | ## 时钟 ## | ||
- | 时钟实例是可以在一定数量的系统时钟节拍之后运行的函数。时钟实例可以是单次或周期性的。这些实例可以配置为在创建后立即开始或者在一段延迟后启动,并可以随时停止。所有的时钟实例当它们在Swi的上下文中时钟溢出就会被执行。以下示例显示了TI-RTOS配置文件(`.cfg`)中设置的TI-RTOS时钟周期的最小分辨率。 | + | |
+ | 时钟实例是可以在一定数量的系统时钟节拍之后运行的函数,时钟实例可以是单次或周期性的。这些实例可以配置为在创建后立即开始,或者在一段延迟后启动,并可以随时停止。所有的时钟实例当它们在 Swi 的上下文中时钟溢出就会被执行。以下示例显示了 TI-RTOS 配置文件( `.cfg` )中设置的 TI-RTOS 时钟周期的最小分辨率。 | ||
> | > | ||
- | > | + | > |
> | > | ||
- | 每个系统时钟节拍会启动一个时钟对象来为节拍计数,然后再和一系列的时钟进行比较以确定相关函数是否该运行。对于需要更高分辨率的定时器,TI建议使用16位硬件定时器通道或传感器控制器来做。有关这些功能的更多信息,请参见[TI-RTOS内核用户指南](file:/// | + | 每个系统时钟节拍会启动一个时钟对象来为节拍计数,然后再和一系列的时钟进行比较以确定相关函数是否该运行。对于需要更高分辨率的定时器,TI 建议使用 16 位硬件定时器通道或传感器控制器来做。有关这些功能的更多信息,请参见[ TI-RTOS 内核用户指南](file:/// |
+ | |||
+ | 您可以直接在应用程序中使用内核的 Clock API ,`Util` 模块中提供了一组抽象的 TI-RTOS 时钟功能,如下所示: | ||
- | 您可以直接在应用程序中使用内核的Clock API,`Util`模块中提供了一组抽象的TI-RTOS时钟功能,如下所示: | + | * `Util_constructClock()` 创建一个 Clock 对象。 |
- | * `Util_constructClock()`创建一个Clock对象。 | + | * `Util_startClock()` 启动一个现有的 Clock 对象。 |
- | * `Util_startClock()`启动一个现有的Clock对象。 | + | * `Util_restartClock()` 停止,重新启动一个现有的 Clock 对象。 |
- | * `Util_restartClock()`停止,重新启动一个现有的Clock对象。 | + | * `Util_isActive()` 检查 Clock 对象是否正在运行。 |
- | * `Util_isActive()`检查Clock对象是否正在运行。 | + | * `Util_stopClock()` 停止一个现有的 Clock 对象。 |
- | * `Util_stopClock()`停止一个现有的Clock对象。 | + | * `Util_rescheduleClock()` 重新配置一个现有的Clock 对象。 |
- | * `Util_rescheduleClock()`重新配置一个现有的Clock对象。 | + | |
### 功能实例 ### | ### 功能实例 ### | ||
- | 以下示例来自BLE5-Stack中的simple_peripheral项目。 | + | 以下示例来自 BLE5-Stack 中的 simple_peripheral 项目。 |
![](http:// | ![](http:// | ||
- | 图27. 触发时钟对象 | ||
- | **步骤1**:在图27中,通过`Util_constructClock()`来构建时钟对象。在示例进入连接状态后,它将通过`Util_startClock()`启动Clock对象。 | ||
- | 清单14. 在`simple_peripheral`中的构建`periodicClock`Clock对象 | + | **步骤1**:在上图中,通过 `Util_constructClock()` 来构建时钟对象。在示例进入连接状态后,它将通过 `Util_startClock()` 启动 Clock 对象。 |
+ | |||
+ | 清单14. 在 `simple_peripheral` 中的构建 `periodicClock` Clock 对象 | ||
```C | ```C | ||
// Clock instances for internal periodic events. | // Clock instances for internal periodic events. | ||
行 508: | 行 555: | ||
> | > | ||
- | **步骤3**: | + | **步骤3**: |
+ | |||
+ | 清单16.维护 SBP_PERIODIC_EVT 事件 | ||
- | 清单16.维护SBP_PERIODIC_EVT事件 | ||
```C | ```C | ||
if (events & SBP_PERIODIC_EVT) | if (events & SBP_PERIODIC_EVT) | ||
行 522: | 行 570: | ||
## 驱动 ## | ## 驱动 ## | ||
- | TI-RTOS提供了一套可以添加到应用程序的CC26xx外设驱动程序。驱动程序为应用程序提供了与CC26xx板载外设接口以及外部设备通信的机制。这些驱动程序在`DriverLib`中抽象了对寄存器的访问。 | ||
- | 有关BLE5-Stack中每个TI-RTOS驱动程序的重要文档及有关具体位置,请参阅`BLE5-Stack release notes`。本节仅概述了驱动程序如何适应软件生态系统。有关可用的功能和驱动程序API的说明,请参阅`TI-RTOS API Reference`。 | + | TI-RTOS 提供了一套可以添加到应用程序的 CC26xx 外设驱动程序。驱动程序为应用程序提供了与 CC26xx 板载外设接口以及外部设备通信的机制,这些驱动程序在 `DriverLib` 中抽象了对寄存器的访问。 |
+ | |||
+ | 有关 BLE5-Stack 中每个 TI-RTOS 驱动程序的重要文档及有关具体位置,请参阅 `BLE5-Stack release notes` 。本节仅概述了驱动程序如何适应软件生态系统。有关可用的功能和驱动程序 API 的说明,请参阅 `TI-RTOS API Reference` 。 | ||
### 添加驱动程序 ### | ### 添加驱动程序 ### | ||
- | 某些驱动程序会作为源文件被添加到工程项目工作区中Drivers文件夹下的相应文件夹中,如图28所示。 | + | 某些驱动程序会作为源文件被添加到工程项目工作区中 Drivers 文件夹下的相应文件夹中,如图所示。 |
![](http:// | ![](http:// | ||
- | 图28. 驱动程序文件夹 | ||
- | 驱动的源文件可以在`$TI_RTOS_DRIVERS_BASE$\ti\drivers`的相应文件夹中找到。(`$TI_RTOS_DRIVERS_BASE$`指的是安装位置,可以在IAR Tools\Configure Custom Argument Variables菜单中查看。对于CCS,相应的路径变量在Project Options\ Resource\Linked Resources选项卡中定义) | + | 驱动的源文件可以在 `$TI_RTOS_DRIVERS_BASE$\ti\drivers` 的相应文件夹中找到。( `$TI_RTOS_DRIVERS_BASE$` 指的是安装位置,可以在 IAR Tools\Configure Custom Argument Variables 菜单中查看。CCS 相应的路径变量在 Project Options\ Resource\Linked Resources 选项卡中定义) |
- | 例如,ECC和TRNG驱动程序是BLE5-Stack而不是TI-RTOS的一部分,它们分别位于 | + | 例如,ECC 和 TRNG 驱动程序是 BLE5-Stack 而不是 TI-RTOS 的一部分,它们分别位于 |
- | `< | + | |
- | `< | + | |
要向工程中添加驱动程序,需要将相应驱动程序的头文件包含在引用驱动程序API的应用程序文件中。 | 要向工程中添加驱动程序,需要将相应驱动程序的头文件包含在引用驱动程序API的应用程序文件中。 | ||
- | 例如,要添加用于读取或控制输出I/ | + | 例如,要添加用于读取或控制输出 I/O 引脚的 PIN 驱动程序,请添加以下内容: |
`#include < | `#include < | ||
- | 还需要将以下TI-RTOS驱动程序文件添加到`Drivers\PIN`文件夹下的项目中: | + | 还需要将以下 TI-RTOS 驱动程序文件添加到 `Drivers\PIN` 文件夹下的项目中: |
* PINCC26XX.c | * PINCC26XX.c | ||
* PINCC26XX.h | * PINCC26XX.h | ||
行 550: | 行 601: | ||
### 板文件 ### | ### 板文件 ### | ||
- | 板文件为将通过设置相应的参数将固定的驱动配置修改为特定的板配置,例如配置PIN驱动程序的GPIO表或者定义将哪些引脚分配给I2C,SPI或UART。 | + | 板文件为将通过设置相应的参数将固定的驱动配置修改为特定的板配置,例如配置 PIN 驱动程序的 GPIO 表或者定义将哪些引脚分配给 I2C、SPI 或 UART。 |
- | 有关板文件的更多信息,以及如何在TI EMs和LPs或本地硬件端口之间切换,请参见**TI提供的板文件部分**部分。 | + | 有关板文件的更多信息,以及如何在 TI EMs 和 LPs 或本地硬件端口之间切换,请参见** TI 提供的板文件部分**部分。 |
### 可用驱动程序 ### | ### 可用驱动程序 ### | ||
- | 本节给大家介绍一些可用的驱动程序,并提供一些基本示例演示如何将驱动程序添加到`simple_peripheral`项目中。有关每个驱动程序的详细信息,可在**TI-RTOS API Reference**中查看。 | + | 本节给大家介绍一些可用的驱动程序,并提供一些基本示例演示如何将驱动程序添加到 `simple_peripheral` 项目中。有关每个驱动程序的详细信息,可在** TI-RTOS API Reference **中查看。 |
#### 引脚 #### | #### 引脚 #### | ||
- | PIN驱动程序用于控制GPIO的I/ | + | PIN 驱动程序用于控制 GPIO 的 I/O 引脚或着连接在上面的硬件外围设备。如** TI 提供的板文件部分**所述,引脚在 main()中必须首先初始化为安全状态(在板文件中配置)。此初始化后,任何模块都可以使用 PIN 驱动程序配置一组引脚供使用。 |
- | |信号名称|针号|CC2640R2F LaunchPad映射| | + | 以下是在 simple_peripheral 任务中将一个引脚配置成作为中断使用,另一个配置成作为输出使用的示例,在中断发生时交换它们的角色。IOID_x 引脚号对应于[ CC26XX 技术参考手册](http:// |
+ | |||
+ | 下表列出了使用的引脚及其在 `CC2640R2F LaunchPad` 上的映射,这些已在板文件中定义了。 | ||
+ | |||
+ | |信号名称|针号|CC2640R2F LaunchPad 映射| | ||
|: | |: | ||
|CC2640R2_LAUNCHXL_PIN_RLED|IOID_6|DIO6(红色)| | |CC2640R2_LAUNCHXL_PIN_RLED|IOID_6|DIO6(红色)| | ||
|CC2640R2_LAUNCHXL_PIN_BTN1|IOID_13|DIO13(BTN_1)| | |CC2640R2_LAUNCHXL_PIN_BTN1|IOID_13|DIO13(BTN_1)| | ||
- | `simple_peripheral.c`代码需要做以下修改。 | + | `simple_peripheral.c` 代码需要做以下修改。 |
+ | |||
+ | 1. 包括 PIN 驱动程序文件: | ||
- | 1. 包括PIN驱动程序文件: | ||
`# | `# | ||
- | 2. 声明引脚配置表和引脚状态并申明simple_peripheral任务使用的变量: | + | 2. 声明引脚配置表和引脚状态并申明 simple_peripheral 任务使用的变量: |
清单17. 引脚配置表 | 清单17. 引脚配置表 | ||
+ | |||
```C | ```C | ||
static PIN_Config SBP_configTable[] = | static PIN_Config SBP_configTable[] = | ||
行 581: | 行 639: | ||
PIN_TERMINATE | PIN_TERMINATE | ||
}; | }; | ||
- | |||
static PIN_State sbpPins; | static PIN_State sbpPins; | ||
static PIN_Handle hSbpPins; | static PIN_Handle hSbpPins; | ||
static uint8_t LED_value = 0; | static uint8_t LED_value = 0; | ||
``` | ``` | ||
- | 3. 声明在Hwi上下文中执行ISR: | + | |
- | 清单18. 声明ISR | + | 3. 声明在 Hwi 上下文中执行 ISR: |
+ | |||
+ | 清单18. 声明 ISR | ||
```C | ```C | ||
static void buttonHwiFxn(PIN_Handle hPin, PIN_Id pinId) | static void buttonHwiFxn(PIN_Handle hPin, PIN_Id pinId) | ||
行 595: | 行 655: | ||
``` | ``` | ||
- | 4. 在SimpleBLEPeripheral_processAppMsg中,添加一个case来处理上面的事件,并定义事件: | + | 4. 在 SimpleBLEPeripheral_processAppMsg 中,添加一个 case 来处理上面的事件,并定义事件: |
- | 清单19. ISR事件处理 | + | |
+ | 清单19. ISR 事件处理 | ||
```C | ```C | ||
#define SBP_BTN_EVT | #define SBP_BTN_EVT | ||
- | |||
static void SimpleBLEPeripheral_processAppMsg(sbcEvt_t *pMsg) | static void SimpleBLEPeripheral_processAppMsg(sbcEvt_t *pMsg) | ||
{ | { | ||
行 619: | 行 680: | ||
} | } | ||
``` | ``` | ||
- | 5. 打开引脚使用,并在simple_peripheral_init()中配置中断: | + | |
+ | 5. 打开引脚使用,并在 simple_peripheral_init()中配置中断: | ||
清单20. 打开引脚并配置中断 | 清单20. 打开引脚并配置中断 | ||
+ | |||
```C | ```C | ||
// Open pin structure for use | // Open pin structure for use | ||
行 631: | 行 695: | ||
PIN_setConfig(hSbpPins, | PIN_setConfig(hSbpPins, | ||
``` | ``` | ||
+ | |||
6. 编译 | 6. 编译 | ||
7. 下载 | 7. 下载 | ||
行 636: | 行 701: | ||
> | > | ||
- | > | + | >按下 CC2640R2F LaunchPad 上的 BTN-1 按钮可切换红色 LED,这里没有做去抖动处理。 |
#### GPIO #### | #### GPIO #### | ||
- | GPIO模块允许您通过简单易用的API来管理通用I/ | + | GPIO 模块允许您通过简单易用的 API 来管理通用 I/O 引脚。GPIO 引脚的行为通常是静态配置的,但也可以在运行时进行重新配置。 |
- | 由于其简单性,GPIO驱动程序不遵循其他TI-RTOS驱动程序类型,其中驱动程序的API具有单独的特定于设备的实现。在GPIOxxx_Config结构中,这种差异最为明显,它不需要您指定特定的函数表或对象, | + | 由于其简单性,GPIO 驱动程序不遵循其他 TI-RTOS 驱动程序类型,其中驱动程序的 API 具有单独的特定于设备的实现。在 GPIOxxx_Config 结构中,这种差异最为明显,它不需要您指定特定的函数表或对象, |
+ | |||
+ | 以下是一个如何配置 GPIO 引脚,并利用注册中断回调函数来控制 LED 灯的打开关闭的示例。 | ||
+ | |||
+ | 1. 在 `simple_peripheral.c` 中包含 GPIO 驱动程序文件: | ||
- | 以下是一个如何配置GPIO引脚,并利用注册中断回调函数来控制LED灯的打开关闭的示例。 | ||
- | 1. 在`simple_peripheral.c`中包含GPIO驱动程序文件: | ||
`# | `# | ||
- | 在Board.c中做一下添加: | + | 在 Board.c 中做一下添加: |
+ | |||
+ | 2. 添加一组 `GPIO_PinConfig` 元素,用于定义应用程序使用的每个引脚的初始配置。引脚类型(即 INPUT/ | ||
+ | |||
+ | 清单21. 设置 GPIO 引脚配置数组 | ||
- | 2. 添加一组`GPIO_PinConfig`元素,用于定义应用程序使用的每个引脚的初始配置。引脚类型(即INPUT/ | ||
- | 清单21. 设置GPIO引脚配置数组 | ||
```C | ```C | ||
// | // | ||
行 670: | 行 739: | ||
}; | }; | ||
``` | ``` | ||
- | 3. 添加一组`GPIO_CallbackFxn`元素,用于存储配置有中断的GPIO引脚的回调函数指针。这些数组元素的索引对应于`GPIO_PinConfig`数组中定义的引脚。这些函数指针可以通过引用数组元素中的回调函数名来静态定义,也可以通过动态地将数组元素设置为NULL并在运行时使用`GPIO_setCallback()`来插入回调条目。不用于中断的引脚可以从回调数组中省略,以减少内存使用(如果它们位于`GPIO_PinConfig`数组的末尾)。回调函数语法应符合以下条件: | + | |
+ | 3. 添加一组 `GPIO_CallbackFxn` 元素,用于存储配置有中断的 GPIO 引脚的回调函数指针。这些数组元素的索引对应于 `GPIO_PinConfig` 数组中定义的引脚。这些函数指针可以通过引用数组元素中的回调函数名来静态定义,也可以通过动态地将数组元素设置为 NULL 并在运行时使用 `GPIO_setCallback()` 来插入回调条目。不用于中断的引脚可以从回调数组中省略,以减少内存使用(如果它们位于`GPIO_PinConfig`数组的末尾)。 | ||
+ | |||
+ | 回调函数语法应符合以下条件: | ||
`void (* GPIO_CallbackFxn )(unsigned | `void (* GPIO_CallbackFxn )(unsigned | ||
+ | index 参数与传递给 GPIO_setCallback()的索引相同。这允许通过使用索引来识别哪个 GPIO 发生了中断,可以将相同的回调函数用于多个 GPIO 中断。 | ||
+ | |||
+ | 以下是 CC26XX 设备的 GPIO_CallbackFxn 数组的特定示例: | ||
+ | |||
+ | 清单22. 设置 GPIO 回调函数数组 | ||
- | index参数与传递给GPIO_setCallback()的索引相同。这允许通过使用索引来识别哪个GPIO发生了中断,可以将相同的回调函数用于多个GPIO中断。以下是CC26XX设备的GPIO_CallbackFxn数组的特定示例: | ||
- | 清单22. 设置GPIO回调函数数组 | ||
```C | ```C | ||
+ | |||
// | // | ||
// Array of callback function pointers | // Array of callback function pointers | ||
行 688: | 行 765: | ||
}; | }; | ||
``` | ``` | ||
- | 4. 将前面提到的两个数组以及每个数组的元素数量添加到`GPIOCC26XX_Config`结构体中用于驱动程序接口调用。此处还指定了所有能产生中断的引脚的中断优先级。中断优先级的值是是跟设备有关的,同一个引脚的中断优先级对于不同设备是不同的。在将此参数设置为非默认值之前,应熟悉设备中使用的中断控制器。优先级的默认值用于表示应使用最低可能的优先级。以下是初始化`GPIOCC26XX_Config`结构的示例: | + | |
- | 清单23. 设置GPIO配置结构 | + | 4. 将前面提到的两个数组以及每个数组的元素数量添加到 `GPIOCC26XX_Config` 结构体中用于驱动程序接口调用。此处还指定了所有能产生中断的引脚的中断优先级。中断优先级的值是是跟设备有关的,同一个引脚的中断优先级对于不同设备是不同的。在将此参数设置为非默认值之前,应熟悉设备中使用的中断控制器。优先级的默认值用于表示应使用最低可能的优先级。 |
+ | |||
+ | 以下是初始化 `GPIOCC26XX_Config` 结构的示例: | ||
+ | |||
+ | 清单23. 设置 GPIO 配置结构 | ||
```C | ```C | ||
const GPIOCC26XX_Config GPIOCC26XX_config = { | const GPIOCC26XX_Config GPIOCC26XX_config = { | ||
行 699: | 行 781: | ||
}; | }; | ||
``` | ``` | ||
- | 以下是需要在`simple_peripheral.c`中添加的内容: | + | |
+ | 以下是需要在 `simple_peripheral.c` 中添加的内容: | ||
5. 按键回调函数: | 5. 按键回调函数: | ||
+ | |||
清单24. 设置按键的回调函数 | 清单24. 设置按键的回调函数 | ||
+ | |||
```C | ```C | ||
// | // | ||
行 713: | 行 799: | ||
} | } | ||
``` | ``` | ||
- | 6. 初始化和使用GPIO(将其添加到simple_peripheral_init()): | + | |
- | 清单25. 初始化和使用的GPIO | + | 6. 初始化和使用 GPIO (将其添加到simple_peripheral_init()): |
+ | |||
+ | 清单25. 初始化和使用的 GPIO | ||
```C | ```C | ||
// Call GPIO driver init function | // Call GPIO driver init function | ||
GPIO_init(); | GPIO_init(); | ||
- | |||
// Turn on user LED | // Turn on user LED | ||
GPIO_write(CC2640R2_LAUNCHXL_PIN_RLED, | GPIO_write(CC2640R2_LAUNCHXL_PIN_RLED, | ||
- | |||
// install Button callback | // install Button callback | ||
GPIO_setCallback(CC2640R2_LAUNCHXL_PIN_BTN1, | GPIO_setCallback(CC2640R2_LAUNCHXL_PIN_BTN1, | ||
- | |||
// Enable interrupts | // Enable interrupts | ||
GPIO_enableInt(CC2640R2_LAUNCHXL_PIN_BTN1); | GPIO_enableInt(CC2640R2_LAUNCHXL_PIN_BTN1); | ||
``` | ``` | ||
+ | |||
7. 编译 | 7. 编译 | ||
+ | |||
8. 下载 | 8. 下载 | ||
+ | |||
9. 运行 | 9. 运行 | ||
#### 其他驱动程序 #### | #### 其他驱动程序 #### | ||
- | TI-RTOS附带的其他驱动程序有:UART,SPI,加密(AES),I2C,PDM,Power,RF和UDMA。由于协议栈使用了电源,RF和UDMA,因此在使用它们的时候,必须特别小心。与其他驱动程序一样,这些驱动程序也在文档中被详细记录,BLE5-Stack中提供了示例。 | + | TI-RTOS 附带的其他驱动程序有:UART、SPI、加密(AES)、I2C、PDM、Power、RF 和 UDMA。由于协议栈使用了电源、RF 和 UDMA,因此在使用它们的时候必须特别小心。与其他驱动程序一样,这些驱动程序也在文档中被详细记录,BLE5-Stack 中提供了示例。 |
## 功耗管理 ## | ## 功耗管理 ## | ||
- | 所有功耗管理功能由外设驱动程序和低功耗蓝牙协议栈进行处理。可以通过包含或移除POWER_SAVING预处理器定义的符号来启用或禁用此功能。当POWER_SAVING被使能时,设备根据低功耗蓝牙事件,外设事件,应用计时器等的要求进入和退出休眠状态。当POWER_SAVING未定义时,设备保持唤醒。有关修改预处理器定义的符号的步骤,请参阅[用CCS开发]http:// | ||
- | 有关电源管理功能的更多信息,包括API和自定义UART驱动程序的示例,可以在安装的TI-RTOS中包含的CC26xx的TI-RTOS电源管理中找到。只有使用自定义驱动程序时,才需要这些API。 | + | 所有功耗管理功能由外设驱动程序和低功耗蓝牙协议栈进行处理。可以通过包含或移除 POWER_SAVING 预处理器定义的符号来启用或禁用此功能。当 POWER_SAVING 被使能时,设备根据低功耗蓝牙事件,外设事件,应用计时器等的要求进入和退出休眠状态。当 POWER_SAVING 未定义时,设备保持唤醒。 |
- | 另请参阅`Measuring Bluetooth Smart Power Consumption (SWRA478)`,以分析系统功耗和电池寿命。 | + | 有关修改预处理器定义的符号的步骤,请参阅[用 CCS 开发](http:// |
+ | |||
+ | 有关电源管理功能的更多信息,包括 API 和自定义 UART 驱动程序的示例,可以在安装的 TI-RTOS 中包含的 CC26xx 的 TI-RTOS 电源管理中找到。只有使用自定义驱动程序时,才需要这些 API。 | ||
+ | |||
+ | 另请参阅 `Measuring Bluetooth Smart Power Consumption (SWRA478)` ,以分析系统功耗和电池寿命。 | ||
## 加入我们 ## | ## 加入我们 ## | ||
+ | |||
文章所有代码、工具、文档开源。加入我们[**QQ群 591679055**](http:// | 文章所有代码、工具、文档开源。加入我们[**QQ群 591679055**](http:// | ||
<div> | <div> | ||
行 752: | 行 845: | ||
</ | </ | ||
</ | </ | ||
- |