![]() |
![]() |
![]() |
应用程序
从这个章节开始,我们将详细讲解CC2640R2F BLE5.0的应用程序框架,在之前我们希望已经按照我们学习线路图储备了CC2640R2F平台的软硬件架构知识。明白应用工程区分App和Stack工程管理。这里我们主要是讲解基于TI-RTOS的App应用程序框架。
这里介绍以 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函数不返回,将在整个项目生命周期内保留其资源;
基本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消息模块必须由ICALL_init()完成初始化,并且通过ICall_createRemoteTasks()完成协议栈任务创建 。
ICALL
介绍
在软件架构章节我们讲解了由于历史兼容原因将整个应用工程区分App和Stack两个工程管理,正是基于这样的两个工程设计,App和Stack之间的通信就需要重新考虑了,因为我们没有办法像常规API调用和全局变量方式完成消息传递。TI引入了ICALL消息机制完成App和Stack独立工程管理的彼此通信。以下我们着重理解原理和代码实现,因其占了我们程序很大部分。
Indirect Call Framework (ICall 消息框架)基于TI-RTOS提供服务(例如,同步线程、消息、事件)完成BLE协议栈和应用程序在两个工程的消息交互,保证了应用程序和协议栈在统一的TI-RTOS环境中完成高效运行、相互通信和资源共享。
ICall架构和的核心组件是其消息调度(dispatcher),其帮助程序在两个镜像/工程中完成BLE5-Stack协议栈以及库配置中的应用程序交互。尽管大多数ICall交互在BLE5-Stack API(例如GAP,HCI等)中已经被抽象化(已经被封装为消息原语函数),但是我们必须理解其在BLE-Stack和多线程RTOS环境中正常运行的基础架构。
ICALL源码在应用工程(例如我们这里的simple_peripheral)的 ICALL BLE/ICALL 文件夹路径。
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的必需代码。
/* 初始化ICall模块 */
ICall _init ();
/* 启动协议栈任务 - 优先级 */
ICall _createRemoteTasks ();
调用ICall_init()初始化ICALL原语服务(例如,堆管理)和框架,调用 ICall _createRemoteTasks()创建但不启动BLE5-Stack任务。在使用ICall协议服务之前,服务器和客户端分别完成登记和注册。服务端在编译的时候就需要登记一个服务。登记函数使用一个全局的唯一标识符区分每个服务,也是作为后面通信地址。例如,BLE协议使用 ICALL_SERVICE_CLASS_BLE
做些蓝牙协议栈ICALL的消息交互的标识。
对于服务端的登记,包含在osal_icall_ble.c
文件