cc2640蓝牙芯片软件开发指导说明文件(7)

2018-12-04 16:58

1. 消息和线程的同步; 2. 堆的调用和管理。

这些中的一部分已经抽象为Utility函数,在Section 3中看相关模块。 4.2.3.1 消息和线程同步

由ICall提供的消息和线程同步的函数使设计一个在多线程RTOS环境下应用到协议栈的接口成为可能。 在ICall中,两个任务间的消息传达是经由消息队列从一个线程发送一个消息块到另一个线程。发送者分配内存,写消息的内容到内存块中,发送这个内存块到接收端。消息的通知交付使用一个信号旗完成。接收器在信号旗中唤醒,复制消息内存块,处理消息并释放内存块给堆。

栈使用ICall来通知和发送消息给应用。。这些“服务消息”(如状态变化通知等)通过应用任务接收的是通过ICall交付并在应用任务的上下文中处理的。 4.2.3.2 堆分配和管理

ICall提供的应用有一个全局堆结构时留给动态内存调用的。ICall堆的大小是由HEAPMGR_SIZE预处理符配置的,该宏在应用工程中定义。参考3.11.5获取更多动态内存管理的细节。ICall使用这个堆给所有的协议栈消息和获取其他ICall服务的内存。同时也要求应用使用这些ICall接口在应用中实现动态调用。 4.2.4 ICall初始化和注册

为了实例化和初始化ICall服务,下面的函数就必须在在应用中的main()函数中调用,并且要先于SYS/BIOS内核调度启动前调用: /* Initialize ICall module */ ICall_init();

/* Start tasks of external images - Priority 5 */ ICall_createRemoteTasks();

调用ICall_init()函数初始化ICall基本服务(如对管理)和框架。调用ICall_createRemoteTasks()创建单并不开始BLE协议栈任务。 在使用ICall协议栈服务前,服务和客户端都必须在ICall中登记注册。服务端登记一个在编译时间中计算的服务。服务函数处理器注册使用一个全局的唯一的标识来标记每个服务。比如BLE使用ICALL_SERVICE_CLASS_BLE接收经由ICall的BLE协议栈消息。 下面是一个关于使用登记BLE协议栈服务在OSAL_IcallBle.c: // ICall enrollment

/* Enroll the service that this stack represents */

ICall_enrollService(ICALL_SERVICE_CLASS_BLE, NULL, &entity, &sem); 注册机制则是客户端用来发送和/或接收经由ICall调度的消息。 为了使一个客户端(如应用任务)可以使用BLE-Stack接口,必须首先注册他的函数到ICall。这种注册一般是在应用任务的初始化函数中完成。这里是一个simpleBLEPeripheral.c文件中SimpleBLEPeripheal_int()的例子:

// Register the current thread as an ICall dispatcher application // so that the application can send and receive messages. ICall_registerApp(&selfEntity, &sem);

应用支持selfEntity和sem输入,这些决定于ICall_registerAPP()的返回,为客户端(如应用)任务初始化。这些实体随后被ICall使用促进应用和服务任务之间的消息传递。

sem参数代表的信号旗用来发送信号,而selfEntity代表任务目的消息队列。每个任务注册在ICall中都会有唯一的sem和selfEntity标识。

注意:BLE协议栈接口定义在ICallBLEApi.c中,其他的ICall基本服务则不会在ICall注册前到达。

4.2.5 ICall线程同步

ICall模块是通过抢占和有RTOS提供的信号旗同步服务实现的在应用和栈线程间切换。 两个ICall函数为了检束和排序消息,像上面提到的,是不阻塞函数。这就是,他们将检查在队列中是否有接收到消息和那里是否没有消息,函数将会立即用ICALL_ERRNO_NOMSG返回值。为了允许一个客户端或一个服务端线程阻塞直至检索到一个消息,ICall提供了下面的函数可以阻塞直到与RTOS线程调用者有关的-信号旗到来。 //static inline ICall_Errno ICall_wait(uint_fast32_t milliseconds) ICall_Errno errno = ICall_wait(ICALL_TIMEOUT_FOREVER);

“milliseconds”是超时时间单位是毫秒,当到了超时时间后如果函数还没有返回,那么函数就会返回ICALL_ERRNO_TIMEOUT。如果ICALL_TIMEOUT_FOREVER作为超时参数,那么ICall_wait()就会永远的阻塞直到某个信号旗的到来。允许一个应用或服务线程阻塞是非常重要的目的是为了收集进程资源给另一个低优先级线程或通过关闭电源和/或时钟来节省能量在需要的时候。

与信号旗相关联的RTOS线程由下面的条件之一标记: 1. 一个新的消息排到了应用RTOS线程队列中。 2. ICall_signal()被信号旗调用。

ICall_signal()的作用就是一个应用或者一个服务可以添加自己的是事件来疏通ICall_wait()和同步线程。ICall_signal()接收信号旗句柄作为它的唯一的参数,如下所示: //static inline ICall_Errno ICall_signal(ICall_Semaphore msgsem) ICall_signal(sem);

与信号旗句柄相关的线程可以通过调用ICall_enrollService()或ICall_registerAPP()获得。 4.2.6 ICall用法示例

下面的图展示了一个应用发向BLE协议栈的消息通过ICall框架并把返回值返回给应用的例子。ICall_init()初始化ICall模块实例自身,ICall_createRemoteTasks()创建一个任务每个外部镜像带一个进入函数在已知地址。初始化ICall之后,应用任务注册到ICall通过ICall_registerApp()。在SYS/BIOS调度者开始并且应用任务已经运行起来之后,应用发送一个协议命令,这些命令在ICallBLEAPI.c中定义诸如GAP_GetParamValue()。注意协议命令不在应用线程中执行,但是密封在ICall消息中并且通过ICall框架发送给BLE协议栈。换句话说,这个命令是发送给ICall调度者并在那里接受调度和在服务端执行(比如BLE栈)。应用线程同时阻塞(比如等待)等待相应的命令状态消息(如状态和GAP参数值)。当BLE协议栈完成执行该命令后,该命令的状态消息回复就会通过ICall返回到应用线程。

4.3 通用应用架构

这一章将描述一个应用任务的创建的更多的细节 4.3.1 应用初始化函数

第3.3章描述了一个任务是如何构建的。在任务被构建之后,SYS/BIOS内核调度进程就开始,在任务构建过程通过的函数会在任务就绪时运行(比如SimpleBLEperipheral_taskFxn)。该函数第一件应该做的事就是低啊用一个应用初始化函数。举例说明,在SimpleBLEPerpheral.c中:

static void SimpleBLEPeripheral_taskFxn(UArg a0, UArg a1) {

// Initialize application

SimpleBLEPeripheral_init(); // Application main loop for (;;) { …

这个初始化函数,SimpleBLEPeripheral_init(),配置了数个服务给任务同时也设置系列硬件和软件的配置设置和参数。一些共同的例子就是:初始化GATT客户端,注册各种Profile中的回调函数,设置GAPRole,设置绑定管理,设置GAP层,配置硬件模块如LCD,SPI等。在该指导中查看相关章节获取这些例子的更多信息。

注意:在应用初始化函数中,ICall_registerApp()必须在其他栈接口调用之前调用。 4.3.2 任务函数中的事件进程。

在初始化函数之后,像上面代码中展示的一样,任务函数会进入一个无限循环中,它将会保持阻塞并等待一个信号旗发送一个新的原因给进程:

ICall_Errno errno = ICall_wait(ICALL_TIMEOUT_FOREVER); if (errno == ICALL_ERRNO_SUCCESS) { ...

一旦一个事件或其他的刺激发生并处理,任务就会再次进入顶戴信号旗并保持阻塞状态知道其他的原因去执行。请看下面的逻辑图:

如上图所示,这里有多种理由可以导致信号旗通过并且使任务激活执行,他们包括: 4.3.2.1 内部任务消息

5 6 7 8 9

if (ICall_fetchServiceMsg(&src, &dest, (void **)&pMsg) == ICALL_ERRNO_SUCCESS) {

if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity)) {

10 // Process inter-task message

11 SimpleBLEPeripheral_processStackMsg((ICall_Hdr *)pMsg); 12 } 13 if (pMsg) 14 {

15 ICall_freeMsg(pMsg);

16 }

这些是其他任务(如BLE协议栈)通过ICall发送给应用的消息。在SimpleBLEPeripheral工程中没有这样的例子。其他可能的例子有: 1. 一个确定的发送自协议栈确认的空中指示

2. 一个发送自协议栈的、连接事件和广播结束的通知。

3. 一个回复给GATT客户端的操作。看5.3.3章有这方面的例子。 4.3.2.1.1 内部任务事件

在一些少数的案例中,BLE协议栈将会通过ICall在应用任务中设置一个事件。这样的例子就

是当HCI_EXT_AdvEventNoticeCmd()调用时。在这些案例中,新增的代码需添加到if语句中目的是为了与BLE协议栈事件和消息作出区别。这一点将会在本文档的任何需要的地方都会明确的提示。这样添加检查的例子就是GAPRole任务的任务函数:

if (ICall_fetchServiceMsg(&src, &dest, (void **)&pMsg) == ICALL_ERRNO_SUCCESS) {

if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity)) {

ICall_Event *pEvt = (ICall_Event *)pMsg; // Check for BLE stack events first if (pEvt->signature == 0xffff)

{

if (pEvt->event_flag &GAP_EVENT_SIGN_COUNTER_CHANGED) {

// Sign counter changed, save it to NV

VOID osal_snv_write(BLE_NVID_SIGNCOUNTER, sizeof(uint32_t), &gapRole_signCounter);

} } else {

// Process inter-task message

gapRole_processStackMsg((ICall_Hdr *)pMsg); } }

if (pMsg)

{

ICall_freeMsg(pMsg);

} }

注意上面的代码,如果事件来自于BLE协议栈pEvt->signature 将总是等于0xFFFF。

当为了一个内部事件选择了一个事件值时,这个值对于任务必须是唯一的并且必须是多于2的(因此只需设置一个位)。由于pEvt->event变量是以uint16_t初始化的,所以最大允许16个事件。那些已经用于BLE OSAL 全局事件的事件值是不能再用的。

/********************************************************************* * BLE OSAL GAP GLOBAL Events */

#define GAP_EVENT_SIGN_COUNTER_CHANGED 0x4000 //!< The device level sign counter changed

注意这些内部任务是一些列与4.2.3.2中提到的内部任务事件不同的事件。 4.3.2.2 发送到应用任务的RTOS队列中的消息

5. // If RTOS queue is not empty, process app message. 6. if (!Queue_empty(appMsgQueue)) 7. {

8. sbpEvt_t *pMsg = (sbpEvt_t *)Util_dequeueMsg(appMsgQueue); 9. if (pMsg)


cc2640蓝牙芯片软件开发指导说明文件(7).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:小学党员教师个人自我剖析材料

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: