cc2640蓝牙芯片软件开发指导说明文件 - 图文

更新时间:2024-01-24 16:41:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

1概述

该文档的目的是为了给出TI simpleLink 低功耗蓝牙cc2640无线MCU软件开发工具的概述,从而开始创建一个智能蓝牙的定制应用。该文档也提供了低功耗蓝牙的特性的介绍,在本文档中,低功耗蓝牙特指BLE。然而,这些不能作为BLE完整的技术规格的替代。阅读蓝牙内核规范来了解更多的细节,或者是TI BLE wiki页中提供的一些介绍资料。 1.1 介绍

4.1版本的蓝牙规范允许两种无线技术系统:基本频率和低功耗蓝牙。BLE系统设计用来一次发送非常小的数据包,所以比BR设备消耗更少的电量。

可以支持BR和BLE的设备就是双模式设备,运行在Bluetooth? Smart Ready下。在蓝牙

无线技术系统中的典型应用,一台移动智能手机或者笔记本电脑就是双模式设备。设备只制成BLE的就是单模式设备,运行在Bluetooth? Smart下。这些单模式设备同城用在优先考虑低功耗的应用场景中,比如那些基于纽扣电池的设备。

1.2 BLE 协议栈基础

BLE协议栈属性如下所示:

BE协议栈(就是简称协议栈)由两个部分组成:控制器和主机。这两个部分经常独立实现,这就将主机和控制器设备与蓝牙的BR设备区分开了。任何配置和应用都位于GAP和GATT层的上面。 物理层(PHY)是一个1Mbps适配调频的GFSK接收器,操作在公开的2.4GHz带宽上的。 链路层(LL)控制设备上的RF的状态,设备会处于五种当中的一种状态:就绪、广播、扫描、初始化、连接。广播者发送数据不需要连接,扫描者监听广播。初始者就是一个设备响应一个广播者的连接请求。如果广播者接受连接,那么广播者和初始者就都处于

连接状态。当一台设备处于连接状态,他将处于两种角色当中的一种,主机或从机。初始化连接的设备就成了主机,而接受请求的就变成了从机。

主机控制接口(HCI)层,提供了一种控制器和主机之间的通讯手段的标准接口。该层也可以通过一个软件接口或者一个硬件接口如UART,SPI,或者USB来实现。标准的HCI命令和事件是在蓝牙内核中指定的。TI所使用的命令和事件在Vendor Specific Guide中说明。

链路逻辑控制和适配协议层(L2CAP)层为长层提供数据打包服务,允许端对端的数据交互。

安全管理层(SM)定义了配对和密匙分配的方法,也为其他协议层提供安全连接和数据交换的功能。5.4中有TI的SM层的实现的更多的细节。

通用通道协议层(GPA)层直接面向应用层(也可有)配置,来处理设备的发现和连接相关的服务。GAP处理安全因素的初始化,5.1中有更多关于TI GAP层实现的信息。

属性协议层(ATT)协议允许一个设备展现一个确定长度的数据给另一个设备,就是所谓的属性。

通用属性协议(GATT)层是个服务的框架,定义了辅助进程来使用ATT。所有发生在两个BLE连接设备之间的通讯都是通过GATT的副进程处理的,因此,应用(可以有)配置将直接使用GATT。5.3重点讲解ATT和GATT层。 2 Texas Instrucments 软件开发平台

TI的免费版的BLE-Stack?软件开发工具(SDK)是一个完成的软件平台,可以永爱开发单模式的BLE应用。它基于SimpleLink CC2640,完整的SOC Bluetooth? Smart解决方案。cc2640结合了一个2.4GHz的RF收发器,128kB的嵌入式可编程的内存。20看BDSRAM,和一个全尺寸的外设。设备基于ARM? Cortex-M3?串行处理器来处理应用层和BLE协议栈,同时自动的天线内核基于ARMCortex-M0?处理器来处理所有低级的天线控制和与物理层及部分链路层的进程。传感器控制块提供了一种额外的灵活的方法来自动完成独立于M3核的数据获取和控制,因此也加强了cc2640的低功耗的能力。结构框图在下面,更多的细节在cc2640技术说明指南中。

2.1 协议栈/应用配置

平台支持两种不同的协议栈/应用配置

1. 单个设备:控制器,主机,配置和应用都在cc2640上实现作为一个真正的芯片解决方案。

这是使用cc2640是最简单和最常见的配置。同时这也是我们大多数类程中使用的配置。是最佳性价比的技术和提供最低功耗的性能、

2. 网络处理器:控制器和主机一起在cc2640上实现,而配置和应用则是分开实现的。应用

和配置与cc2640的通讯是通过HCI命令或事件完成的,使用的是SPI或UART接口。这种配置对于运行在其他设备(如外部微处理器或电脑)上的应用来说是有用的。在这些案例中,应用可以独立开发,而BLE协议栈依旧运行在cc2640.网络工程不在该文档中展示。

2.2 解决方案

这部分将描述各种安装在BLE协议栈SDK中的组件,协议栈的目录结构和开发过程中所需的任何工具。

这个解决方案包含了下面的组件:

1. 实时操作系统(RTOS),TI-RTOS SYS/BIOS kernel支持最优化的电源管理,

和外设驱动(如SPI,UART等) 2. cc26xxware驱动库:提供一个寄存器抽象层并被软件或驱动用来控制cc2640

的Soc。

3. BLE 协议栈:以库的形式提供,和存放在cc2640 ROM中的协议栈的部分。 4. 应用和配置类程:使基于合适的、通用的方案开发更加简单。BLE SDK中提

供所有的应用和配置都是经过蓝牙技术联盟测试合格的。 当前支持的编译器有: IAR和CCS 2.3 目录结构

BLE SDK的安装包中包含了大量的工程从最基本的BLE功能到使用案例特定的应用诸如:心率传感器,血糖采集等。这些应该用于应用开发的起始点的基本工程会在下面做简单的描述。更多的谢姐和其他的工程请查看12章。

SimpleBLEPeripheral工程由简单的代码组成,展示了一个非常简单的但设备模

式下的BLE从机应用。可以作为开发从机/外设应用的指导。

SimpleBLECentral工程展示了连接的另一边。展示了一个简单的单设备配置模式下主机/中心应用,可以作为开发主机或中心的应用的指导。 SensorTag工程上一个外设应用,用来运行在cc2650 SensorTag指向的硬件平台,

与传感器目标通讯如温度传感器等。

HostTest工程用来给cc2640创建一个网络处理器的软件。包含了主机和从机的

角色配置,并可以被电脑端的应用Btool控制。参考供应商提供的HCI应用接口指南,在文档

文件夹中有提供网络处理器实现的APIs。

2.4 设置开发环境

在进一步编程之前, 有必要来设置综合开发环境(IDE),目的是为了浏览本文档中提到的工程和代码。所有的适合cc2640的软件都是基于IAR或者ccs开发的。这部分讲在哪里可以找到这些软件,并正确的配置这些IDE。 该文档中提到的所有的路径和文件都必须是BLE SDK安装的默认路径。强烈要求做个BLE SDK的备份来进行修改。BLE SDK使用的是相对路径,设计为模块化的,这样就允许顶层目录可以复制到任何合法的地址了。 注意:确保最大的文件系统命名路径长度没有改变。

2.5.1 安装SDK

运行安装包“ble_cc26xx_setupwin32_2_00_00_xxxxx.exe”来安装BLE协议栈的SDK。 1. xxxx是SDK的版本号;

2. 默认的SDK的安装路径是:C:\\ti\\simplelink\\ble_cc26xx_2_00_00_xxxx.exe

这个安装包同时也会安装TI-RTOS和XDC工具的附件,前提是以前没有安装过,也包括Btool电脑应用。下面的图列出了BLE-Stack SDK支持的软件、工具和测试。

2.5.2 IAR

这部分不予翻译。

2.5.3 Code Composer Studio(CCS)

ccs包含了许多因素都超出了本文档的范围,可以在ccs网站获取更多的信息和文档:

http://www.ti.com/tool/CCSTUDIO

确保检查过BLE SDK的释放说明,注意ccs的版本和其它的工作相关的要求。另外,有CCS编译生成的代码在大小上和表现上可能与IAR的生成的代码不同。

2.5.3.1 配置ccs

这部分秘书安装和配置当前办呢的CCS和必要的工具。 1. 下载6.0.1或更高级的版本的ccs,下载地址为:

http://processors.wiki.ti.com/index.php/Download_CCS

2. 安装支持simplelink Wireless MCUs;

cc26xx 设备支持 TI ARM 编译器

3. 安装XDS100或XDS200模拟器支持包。

4. 一旦CCS安装成功后所有体统的应用可以通过选择 .help -> Check for Updates来更

新。

注意:这个操作可能会要求ccs在每次更新完一个应用后就会重启 5. 充应用中心(APP Center)中安装最新的TI ARM 编译器

5.1. 选择View –>CCS App Center 5.2. 选择TI ARM 编译器 5.3. 选择要安装的软件

6. 当所有的更新都安装完成后,通过选择help -> About Code Composer Studio来验证安

装细节。

ARM Compiler Tools: 5.2.0

CC26xx Device Support: 1.12.2.00 (or later)

2.5.3.2 使用CCS

这部分将描述怎样打开和编译一个存在的工程。这里将使用SimpleBLEPeripheral工程作为例子。同样的,所有包含在CCS开发包中的CCS BLE工程都有相似的结构。 2.5.3.2.1 导入一个存在的工程。 首先,打开CCS编译器的开始菜单。一旦CCS已经打开了,单击Project-> Import CCS Project选择下面的目录:

$BLE_INSTALL$\\Projects\\bleProjects\\ble\\SimpleBLEPeripheral\\CC26xx\\CCS

这个是CCS中SimpleBLEPeripheral工程的目录。ccs将会发现两个工程(一个应用,一个栈工程)。检查两个工程,选择“Copy Projects Into Workspace”,最后选择“Finish”完成导入。

2.5.3.2.2 工作空间概述

这个工作空间,其实所有的cc2640工程的工作空间,都包含两个工程:应用工程

(SimpleBLEPeripheral)和栈工程(SimpleBLEPeripheralStack)。二者之一可以通过在浏览器中单击工程名字来激活。下图中,应用工程被选为激活工程,每个工程都会生成一个独立的下载镜像。采用这种双镜像的目的是应用工程可以独立于栈更新了。

处于实际原因,SimpleBLEPeripheral例程的主要作用是作为一个通用应用开发的指导。SimpleBLEPeripheral工程实现了一个基本的BLE外设设备,包括GATT和GATT服务。这个工程可以用作一个开各种外设角色应用的固件。 2.5.3.2.3 编译和下载

因为工作空间分成了两个工程(应用和栈),因此编译和下载有固定的顺序: 1. 选择应用工程作为激活工程使用Project->Build ALL 编译工程; 2. 选择栈工程作为激活工程,使用Project->Build ALL 编译工程; 3. 选择栈工程作为激活工程,使用Run->Debug下载栈工程; 4. 选择应用工程作为激活工程,使用Run->Debug下载应用工程。

注意:以上操作只限于初次下载和栈工程有修改的时候。只要栈没有修改,则只需执行下面的步骤: 1. 编译应用 2. 下载应用

2.6 访问预处理符号

贯穿整个文档,所有的C的预处理符号可能需要修改或者是调整工程的等级。访问预处理符号的流程是基于使用的IDE的。下面的流程展示了如何访问和修改预处理符号(IAR的方法不讨论)。 在CCS中,预处理符号可以通过选择和打开各自的工程properties(比如,应用或栈工程),按照CCS Build ??ARM Compiler ??AdvancedOptions ??Predefined Symbols的顺序找

到预处理符号。然后添加,删除或编辑一个预处理符号,如下图所示:

2.7顶层软件架构 在顶层,cc2640 BLE软件环境由三个独立的部分组成:一个实时操作系统(RTOS),一个应用镜像和一个栈镜像。TI-ROTS是一个实时的、抢占式的、多线程的操作系统采用同步任务的方式运行软件解决方案。应用和协议栈都有独立的任务运行在RTOS中,BLE协议栈的任务拥有最高的优先权。一个管理固件,指示调用(Icall),用来在应用和协议栈之间进行安全线程的同步。架构图如下:

1. 栈镜像文件:这个镜像包含BLE协议栈的底层从LL层到GAP和GATT层,大多数的协议栈的

代码都是以库的形式提供。

2. 应用镜像文件:这个镜像包含了相关的配置,应用代码,驱动,和Icall模块。 2.7.1标准工程任务层次

所有的工程都包含至少三个RTOS的任务。按照优先级的顺序排列,因此高任务序号对应着高优先级任务,对于SimpleBLEPeripheral工程来说,这些任务分别是:

5: BLE协议栈任务;

3: BAPRole任务(外设角色);

1:应用任务(SimpleBLEPeripheral); RTOS任务在3.3章中会介绍。BLE协议栈的接口在第五章中讲解,GapRole任务在5.2中进行介绍,应用任务在4.2.1中介绍。

3 RTOS 概述

TI-RTOS是操作在cc2640上的BLE工程的操作环境。TI-RTOS内核是SYS/BIOS的定制版,作为一个实时、抢占、多线程的操作系统操作同时结合同步和调度的工具(XDCTools)。SYS/BIOS内核管理四个层次的执行的线程:硬件中断(HWI)服务惯例,软件中断惯例, 任务和隐藏的空闲功能。

注意:TI-RTOS 内核和SYS/BIOS内核是可互换的。

这部分将描述着四个执行线程和各种贯穿于整个RTOS的消息和同步的结构体。

注意:在大多数案例中基本的RTOS功能都已经抽象在了util.c文件中,更高层的应用可以使用。底层的RTOS函数在SYS/BIOS模块部分描述。该文档中同时也定义了所有TI-RTOS中的包和模块。 3.1 RTOS 的配置

SYS/BIOS内核提供的安装包,可以通过修改RTOS配置文件来修改(比如,SimpleBLEPeripheral工程的appBLE.cfg文件)。 文档中没有说ccs中怎么修改配置

3.2 信号量

内核包提供了几个同步任务的模式,比如信号量。信号量CC2640软件中主要的同步资源。信号量用来调整一系列竞争任务到一个共享资源的通道,比如应用和BLE栈。信号量用于任务的同步和互斥。

信号量的功能如下图所示。信号量可以是计数的信号量或二项信号量。计数信号量可以被Semaphore_post()函数发送的信号持续的触发。这个实用性非常强,比如,当有一组用于任务间的资源。这些任务可能调用Semaphore_pend()来检查一个资源在使用前是否已经准备好了二项信号量只有两种状态:available(计数为1)和unavailable(计数为0)。可以用于在任务间分享单个资源。也可用于一个基本的信号机,这样信号就可以多次发送了。二向信号量不可以通过计数触发;他们只是简单的通过信号量是否被发送来触发。

3.2.1 信号量的初始化

下面的代码描述如何在RTOS中初始化一个信号量。一个这方面的例子就是

SimpleBLEPeripheral工程中的任务被Icall模块激活的时候:Icall_registerApp(),最终会调用Icall_primRegisterApp()。这些信号量用来调整任务的进程。4.2节中将会有更多的这方面的描述。

Semaphore_Handle Sem;

sem = Semaphore_create(0, NULL, NULL); 3.2.2 挂起一个信号量

Semaphore_pend()用来等待一个信号量。这是一个后台调用,允许其他的任务运行。超时参数允许一直等待直到超时,或者模糊的等待,或者一直等待。返回值用来指定信号量是否已经通知成功了。

Semaphore_pend(sem, timeout); 3.2.3 发送一个信号量

Semphore_post()用来发送一个信号量。如果一个任务正在等待信号量,这将任务从信号队列中移出并放到就绪队列。如果没有任务等待,Semphore_post()简单的增加信号量的计数

并返回。对于一个二向信号量,计数总是被设为1. Semaphore_post(sem); 3.3 任务

RTOS任务等同于一个独立的线程,在一个单个的C工程中的并行的执行函数。实际上,通过切换一个个任务来实现并发。

每个任务在运行中的任何时间点都处于五种运行模式中的一种: 运行:任务正在运行;

就绪:任务正在被调度执行; 阻塞:正在运行中的任务被挂起; 终止:任务被终止执行; 闲置:任务在闲置列表中。

永远只有一个(仅有一个)任务正在运行,即使是个空闲任务。当前的任务可以通过调用穿件任务模块的函数来从运行状态中阻塞起来,同时函数提供了其他的模式比如信号量。当前的任务也可以从运行状态中终止。在任何一个例子中,进程被切换至应经就绪的最高优先级的任务。

可以在SYS/BIOS接口中(package ti.sysbios.knl)查看任务模块的函数中查看更多的信息。 数字优先级被安排给任务,可能多个任务有同样的优先级。任务是严格的按照优先的顺序读取执行。同一优先级的任务按照先来先保存的原则调度。当前运行的任务的优先级永远不会低于已经就绪的任务的优先级。相反的,当前运行的任务会在任何时刻被某个存在的优先级更高的任务抢占和调用执行的。在SimpleBLEPeripheral应用中,BLE协议栈的任务被分配为最高优先级,应用任务则是最低优先级。

每个RTOS任务都有一个初始化函数,一个事件处理函数,和一个到多个回调函数。 3.3.1 创建一个任务 当一个任务被创建,它就会被提供一个用来保存当地变量的运行时间栈,为了下一次的函数调用。在单个工程中执行的所有的任务共享一个公共的全局变量,依据C函数定义的标准高的规则来调用。这一系类的内存就是所谓的任务的上下文。 下面是一个在SimpleBLEPeripheral工程创建应用任务的例子:

void SimpleBLEPeripheral_createTask(void) {

Task_Params taskParams; // Configure task

Task_Params_init(&taskParams); taskParams.stack = sbpTaskStack;

taskParams.stackSize = SBP_TASK_STACK_SIZE; taskParams.priority = SBP_TASK_PRIORITY;

Task_construct(&sbpTask, SimpleBLEPeripheral_taskFxn, &taskParams, NULL); }

任务的创建是在主函数main()中完成的,要先于SYS/BIOS调度进程被BIOS_start()函数启动。任务在调度进程开始后,按照被安排的优先级开始执行。 尽管要求使用存在的应用任务来满足应用指定的进程,但是如果需要其他的任务的话那么就要尊寻确定的指导方针。当添加一个额外的任务到应用工程中时,任务的优先级就必须在RTOS的优先级等级范围中安排一个优先级,定义在appBLe.cfg RTOS配置文件中:

/* Reduce number of Task priority levels to save RAM */ Task.numPriorities = 6;

另外新增任务的优先级不要大于等于BLE协议栈任务的优先级和相关支持的函数(见GapRole任务)。看2.7.1部分获取系统任务的层次。 最后,任务应该分配预先定义的最小的栈空间512 bytes。在最小程度,每个栈都必须足够大以备处理正常的子程序和一个任务抢占的上下文。上下文的抢占就是当一个优先级更高的准备就绪的中断进程到来是,正在执行的任务的上下文的保存。使用IDE中的TI-RTOS配置工具,任务可以通过分析来决定最高任务栈使用惯例。

注意:术语“created”用来描述一个任务的初始化。然而,正真的TI-ROTS的方法事构建任务。参考3.11.6部分获取创建RTOS实体的更多的细节。 3.3.2创建任务函数

如上所见,当一个任务被构建之后,一个任务函数的函数指针

(SimpleBLEPeripheral_taskFxn)就传递到了task_Construct函数。这是个当该函数第一次获得机会运行时RTOS就会云心更多函数。该函数的通常的拓扑结构如下:

在SimpleBLEPeripheral任务中,任务似乎在多数时间下都是处于阻塞状态等待一个信号量。一旦它的信号量从一个中断,回调函数或队列中发送出来,该任务就处于就绪状态,然后处理进入暂停前的数据和事件。4.2.1节中有更多的细节。 3.4 时钟

时钟实例是一种可以在确定次数的时钟脉冲后被调度的函数。时钟实例可以是单次的也可以是周期的。他们可以是在创建之后立即开始也可以配设置为在一定延时之后开始。它们也可以在任意时间停止。所有的时钟实例都是在一个软件中断的上下文中结束后执行的。 最小分辨率是在RTOS配置中设置的RTOS时钟脉冲周期:

/* 10 us tick period */ Clock.tickPeriod = 10;

每个脉冲,由RTC驱动,发送一个时钟软件中断,这个中断会把运行的时钟脉冲计数和每个时钟周期进行比较,然后决定想关联的函数是否运行。

对于高分变率的定时器来说,要求使用16位的硬件定时器通道或sensor Controller。 3.4.1 API(接口)

直接和使用RTOS时钟模块函数是可以的(参考时钟模块在SYS/BIOS API 0)。为了实用性,这里在util.c文件中提供了下面的函数:

Clock_Handle Util_constructClock (Clock_Struct *pClock, Clock_FuncPtr clockCB, uint32_t clockDuration, uint32_t clockPeriod, uint8_t startFlag,UArg arg) Description Parameters: Initialize a TIRTOS Clock instance. pClock –pointer to clock instance structure clockCB –function to be called upon clock expiration clockDuration –length of first expiration period. clockPeriod –length of subsequent expiration periods. If set to 0, clock is a one-shot clock. startFlag –TRUE to start immediately, FALSE to wait. If FALSE, Util_startClock() must be called later. arg –argument passed to callback funciton return void Util_startClock(Clock_Struct *pClock) Description Parameters: Start an (already constructed) clock. pClock –pointer to clock structure handle to the Clock instance bool Util_isActive(Clock_Struct *pClock) Description Parameters: Returns: Determine if a clock is currently running. pClock –pointer to clock structure TRUE: clock is running FALSE: clock is not running void Util_stopClock(Clock_Struct *pClock) Description: Parameters: stop a clock. pClock –pointer to clock structure 3.4.2 功能示例 下面的例子来自于SimpleBLEPeripheral工程中详细描述了一个时钟实例的创建和如何处理它的到期。

1 定义时钟处理函数来处理时钟到期的软件中断。 simpleBLEPeripheral.c:

//clock handler function

static void SimpleBLEPeripheral_clockHandler(UArg arg) {

// Store the event. events |= arg;

// Wake up the application. Semaphore_post(sem); }

2创建时钟实体

simpleBLEPeripheral.c

// Clock instances for internal periodic events. static Clock_Struct periodicClock;

// Create one-shot clocks for internal periodic events.

Util_constructClock(&periodicClock, SimpleBLEPeripheral_clockHandler, SBP_PERIODIC_EVT_PERIOD, 0, false, SBP_PERIODIC_EVT);

3,等待时钟实体到期并在应用的上下文中处理。如下面的流程图所展示:绿色的部分是与运行在应用上下文进程有关的。红色的与软件中断有关。

3.5 队列

队列允许应用以一种有序的方法来处理事件,即FIFO的顺序来进行事件的处理。一个工程可能使用队列来管理来自应用,配置或其他任务的事件。鉴于时钟会在一个对时序要求严格的事件必须执行的时候使用,队列对于需要按照指定顺序执行的事件来说是非常有用的。 队列模块提供了两个任务间消息处理的单向方法,遵循FIFO原则。 下图中,一个队列配置为任务A到任务B的单向通讯通道。任务A把消息压到队列中,任务B按照顺序取出消息。

3.5.1 接口

RTOS的队列应经抽象成了函数放在了util.c文件中。在SYS/BIOS API 【9】中查看队列模块隐藏的函数。这些实用的函数结合了队列和能力来通知容器任务通过信号量传递过来的消息。在cc2640软件中,这样用的信号量与用于通过Icall实现任务同步的信号量一样。SimpleBLePeripheral_enqueueMsg()函数中提供了这样的一个例子。

队列通常用来限制应用回调函数在更高优先级任务的上下文中的执行时间。在这种方式中,如果一个优先级更高的任务把一个消息压到了应用的队列中,应用就会 延迟进程而不是在自己的上下文中立即处理。

3.5.2 功能示例

下面是SimpleBLEPeripheral工程中的有关队列的例子,是用来处理一个按键的硬件中断处理。

1.定义任务队列函数以便使用任务的信号量来唤醒自己

static uint8_t SimpleBLECentral_enqueueMsg(uint8_t event, uint8_t status, uint8_t *pData) {

sbcEvt_t *pMsg;

// Create dynamic pointer to message. if (pMsg = ICall_malloc(sizeof(sbcEvt_t))) {

pMsg->event = event; pMsg->status = status; pMsg->pData = pData; // Enqueue the message.

return Util_enqueueMsg(appMsgQueue, sem, (uint8_t *)pMsg); }

return FALSE; }

2. 静态调用然后构建队列

// Queue object used for app messages static Queue_Struct appMsg;

static Queue_Handle appMsgQueue;

...

// Create an RTOS queue for messages to be sent to app. appMsgQueue = Util_constructQueue(&appMsg);

3. 等待按键被按下和在应用的上下文中处理。下面的流程图展示了整个流程。绿色的部分

是运行在应用上下文中的进程,红色的是硬件中断部分。

3.6 空闲任务

空闲模式就是用来指定一系列函数在没有其他任务在系统中运行时调用。在CC2640软件中,空闲任务与运行电源管理方案有关。 3.7 电源管理

一般来说,所有的电源管理的功能性都是由外设驱动和BLE协议栈处理。这种特性通过包含或不包含POWER_SAVING预处理宏来使能或禁能。当POWER_SAVING声,设备就会在BLE事件,外设事件和应用定时器等需要时进入或退出睡眠。当POWER_SAVING没有定义,设备就会一直醒着。9.2中有修改与预编译宏的步骤。

关于电源管理的更多信息包括应用接口和一个使用简例用户串口驱动可以在Power

Management User’s Guide [8]文档中发现,该文档在RTOS安装包中。需要注意的是这些接口只在用户的驱动中有必要添加。

所以,查阅Measuring Power Consumption App Note [3]来分步分析系统的电源消耗和电池寿命。

3.8 硬件中断(HWI)

硬件中断处理应用必须对外部同步事件作出回应的临界进程。SYS/BIOS 目标/设备指定的硬件中断模块用来管理硬件中断。对象,向量和中断函数性的特定的信息可以在技术说明指导中找到Technical Reference Manual [2].另外,SYS/BIOS的用户知道详细描述了硬件中断接口和提供的一系列软件案例。

通常来讲,硬件中断已经从他们从属的外设驱动中分离出来了。在第9中可以找到一个使用GPIO作为硬件中断的例子。这是优先考虑采用的使用方法。使用Hwi_plug()函数,可能需要写的中断服务函数不会与SYS/BIOS相互影响。然而中断服务函数必须做他们的上下文以防止阻塞时序要求高的BLE协议栈。

为了使BLE协议栈能够配合RF的时序要求,所有的应用定义的硬件中断必须以最低优先级执行。因为这个原因,在新添加一个硬件中断时不要修改硬件中断的默认的优先级。 通常,这里不要有应用定义的临界的部分,以免阻塞RTOS或BLE协议栈中时序要求比较严格的部分。代码执行在临界的部分时会阻止实时中断现相关的事件的中断。 3.9 软件中断

查看SYS/BIOS用户指导来获取软件中断的接口和详细信息。软件中断的优先级要高于任务但低于硬件中断。因此,软件中断中处理的进程的数量应该严格限因为这个进程的优先级将会超过BLE协议栈任务的。就想在3.4中描述的一样,使用模块使用软件中断来抢占任务。只处理时钟进程的软件中断会设置一个事件,发送一个信号量给应用来继续软件中断之外的进程。任何可能的时间,时钟模块应该用来实现软件中断。如果必要,一个软件中断可以按照SYS/BIOS用户指导中描述的软件中断模块来实现。

注意,为了保护RTOS的栈,动态创建软件中断应该像3.11.6中描述的一样限制。

3.10Flash

flash被划分为每个可擦除页为4kB。另外,应用和栈工程必须独自开始在4kB开始的地

址。flash的各功能区和相关连接文件分别是:

应用镜像:给应用工程留的代码空间。这个可以在应用连接配置文件:cc26xx_ble_app.icf (IAR) and cc2650f128_tirtos_ccs.cmd(CCS).配置。

栈镜像:给栈工程刘的代码空间。配置文件为:cc26xx_ble_stack.icf (IAR) and

cc2650f128_tirtos_ccs_stack.cmd (CCS).

简单NV(SNV):用来存放非易失性数据的一个区域,主要用阿里保存GAP绑定信息,也可以提供给应用使用。3.10.4讲诉配置SNV。当配置完,SNV就是栈的一部分。

用户配置区(CCA):flash的最后一块就是永爱存储用户指定芯片配置参数的部分。CCA中没有用的空间分配给应用工程。

3.10.1 flash 内存映射

这部分将阐述flash在系统层面上的映射。符号指向的实线队列可以在应用连接文件中找到,符号指向的虚线的队列可以在栈连接文件中找到。

下面的表中概述了Flash系统映射的定义(上图所指),提供了相关的链接宏/符号,这些宏/符号可以在各自的IDE链接文件中找到。 Symbol / Region Meaning Project CCS Definition IAR Definition APP_FLASH_START Start of flash / Start of App code image APP_FLASH_END End of App code image. (ICALL_STACK0_ADDR-1) STACK_FLASH_START Start of Stack code image (ICALL_STACK0_ADDR) Stack App APP_BASE FLASH_START App ICALL_STACK0_ADDR - APP_BASE - 1 ICALL_STACK0_ADDR FLASH_END FLASH_START STACK_FLASH_END End of Stack flash code image, including SNV. Stack FLASH_SIZE - RESERVED_SIZE - ICALL_STACK0_ADDR FLASH_END CCA Sector Last sector of Flash. Contains the CCFG App FLASH_LAST_PAGE FLASH_LAST_PAGE Last 86 bytes of CCA CCFG Region Location in CCA where App Last 86 bytes of CCA

Customer Configuration (CCFG) parameters are stored 3.10.2 应用和栈Flash边界

应用和栈的代码镜像是基于共同的ICALL_STACKO_ADDR定义的。这个值定义了栈镜像的整个功能硬编码flash地址(4kB对齐):本质上就是应用-协议栈工程边界的Flash地址。为了确保适当的连接,应用和栈工程都必须使用同一个ICALL_STACK0_ADDR定义值。默认的ICALL_STACK0_ADDR被配置为调用没用的flash给应用工程,但是可以通过边界工具手动或自动修改。参考3.10.3节是关于手动修改flash边界地址的,3.12节是关于使用边界工具配置flash边界地址的。

3.10.3 手动修改flash地址

如上所诉,边界工具(Boundary Tool)是用来调整ICALL_STACK0_ADDR应用-栈flash边界的以便可以分配最大的flash内存空间给应用工程。尽管不是典型的要求,ICALL_STACK0_ADDR可以通过下面的步骤进行修改: IAR的步骤:

不翻译 CCS的步骤:

1. 调整在TOOLS/ccsLinkerDefines.cmd文件中的ICALL_STACK0_ADDR

--define=ICALL_STACK0_ADDR=0x0000B000

2. 调整应用工程中的预编译符号ICALL_STACK0_ADDR

ICALL_STACK0_ADDR=0xB000

3.重新编译应用和栈工程,验证是两个工程中是否有编译错误。

修改ICALL_STACK0_ADDR时需注意的几点:

1. ICALL_STACK0_ADDR的值必须是个4kB对齐的地址。分配给应用的flash内存空间

越大,分配给栈的空间就越小。

2. ICALL_STACK0_ADDR的值必须符合IAR-Boundary.xcl & IAR-Boundary.bdef文件,

或者要符合ccsLinkderDefines.cmd & preprocessor symbol for CCS。 3. 所有的应用和栈工程必须在ICALL_STACK0_ADDR修改后重新编译。 4. 如果在手动修改ICALL_STACK0_ADDR时发生了连接错误,验证每个工程是否有足

够的内存调用。

3.10.4 使用简单的NV(SNV)

flash中的SNV区域是用来保存稳定的数据以一种稳定的方式,诸如绑定的加密密匙或者存储用户参数。协议栈可以通过配置分配最高2个4kB的flash页给SNV。为了达到最少对flash的擦写,SNV管理者会在扇区存在80%以上的非法数据时会对扇区进行压缩。一次压缩就是将有效数据复制到紧跟着用来存储预先存储的数据的内存存储块的后面的区域。依据OSAL_SNV的值,作出如下的决定,有效数据是保存在新擦除的区域中还是继续保留在新的区域中。可以分配给SNV的flash内存块的数量可以通过配置栈工程中的OSAL_SNV预处理符的值来决定。下面的表列出了有效数值和相关的取舍问题: OSAL_SNV 值 0 描述 禁能SNV,在NV中保存绑定信息是不可能了。最大的应用和(或)栈工程的代码空间。GAP绑定管理必须禁能。在栈工程中,设置预处理符号NO_OSAL_SNV和禁能GAP绑定管理。参考10.4章中关于配置BLE协议栈 1 2 一个flash块调用给SNV。绑定信息存储在NV中。Flash 默认值。分配两个Flash扇区给SNV。绑定信息存储在NV中。SNV数据在压实阶段可以防止掉电丢失。 其他所有的值都是无效的。使用少于最大值的情况下对调用更多代码空间给应用或栈工程有用。

SNV可以通过下面的接口读写: Uint8 osal_snv_read(osalSnvld_t id, osalSnvLen_t len, void *pBuf 描述:从SNV中读取数据 参数: Id:有效的NV的主题 Len:要读取的数据长度 pBuf:存储读取到的数据的缓存指针 返回值: SUCCESS:NV 主题读取成功 NV_OPER_FAILED:读取失败 Uint8 osal_snv_write(osalSnvld_t id, osalSnvLen_t len, void *pBuf) 描述:往SNV中写数据 参数: Id:有效的NV的主题 Len:要写的数据长度 pBuf:要写的数据的缓存指针 返回值: SUCCESS:NV 主题写成功 NV_OPER_FAILED:写失败 由于SNV在BLE SDK中是与其他模块诸如GapBondMgr共享的,所以有必要仔细管理NV的主题ID。通常默认情况下,提供给用户的ID在bcomdef.h文件中定义:

// Customer NV Items - Range 0x80 - 0x8F - This must match the number of Bonding entries #define BLE_NVID_CUST_START 0x80 //!< Start of the Customer's NV IDs #define BLE_NVID_CUST_END 0x8F //!< End of the Customer's NV IDs

3.10.5 用户配置区域(CCA)

CCA占领了flash的最后一页,允许用户在用户配置表(CCFG)中配置不同的芯片和系统参数。CCFG在ccfg_appBLE.c文件中定义,可以在应用工程的设置文件夹中找到。CCA的最后86个字节被系统预留给CCFG表。通常默认情况下,连接器调用CCA扇区中没用到的flash给应用镜像作为代码或数据存储空间用,然而连接器可以修改为整个扇区都留给用户参数数据(举例说明,板子序列号和其他证明参数)。

CCA分配在应用的连接文件中定义,定义符号为FLASH_LAST_PAGE;位置与编译器有关:

CCS:

FLASH_LAST_PAGE (RX) : origin = FLASH_SIZE - 0x1000, length = 0x1000 …

.ccfg : > FLASH_LAST_PAGE (HIGH)

IAR:

define region FLASH_LAST_PAGE = mem:[from(FLASH_SIZE) - 0x1000 to FLASH_SIZE-1]; …

place at end of FLASH_LAST_PAGE { readonly section .ccfg };

参考TRM【2】获取CCFG区域和有关配置操作的更多信息。

3.11内存管理(RAM)

与flash相似,RAM也是在应用和栈工程间共享。RAM扇区在它们的相关连接文件中配置: 1. 应用镜像:应用和共享头的RAM空间。这个是在应用连接配置文件中配置:

cc26xx_ble_stack.icf(IAR)和cc2650f128_tirtos_ccs.cmd(CCS).

2. 栈镜像:栈的.bss和.data扇区的RAM。这个实在栈的连接配置文件中配置:

cc26xx_ble_stack.icf(IAR)和cc2650f128_tirtos_ccs_stack.cmd(CCS).

3.11.1 RAM内存映射

下图展示了默认程序SimpleBLEPeripheral工程的系统内存映射。注意这是概况的和精准的内存分配对于给定的编译来说,可以在SimpleBLEPerihperalApp.map和SimpleBLEPeripheralStack.map文件中找到,这些文件位于IAR的输出文件夹或者是CCS中的FlashROM文件夹中。阅读第9.11章获取这些文件的更多信息。在下面的图中,符号指向的实线队列可以在应用连接文件中找到,符号指向的虚线队列可以在栈的连接文件中找到。

3.11.2 应用和栈的RAM边界

应用和栈RAM内存映射是基于共同的ICALL_RAM0_ADDR宏。这个值定义了硬件编码的应用的RAM空间的尾部和栈镜像的.BSS和.DATA扇区的起始的RAM边界。注意与flash边界的不同知乎在于,栈工程的元素,诸如任务栈和头,是在应用工程中调用。为了确保合适的连接,应用

工程和栈工程都必须使用同一个ICALL_RAM0_ADDR值。默认情况下,ICALL_RAM0_ADDR用来配置为分配没有用到的RAM给应用工程但是可以通过边界工具修改位自动或手懂设置。扇区0是手动修改RAM边界地址,3.12张介绍使用边界工具来配置RAM的边界地址。

3.11.3 系统栈

另外像上面提到的RTOS和ICall头,有其他的内存可以考虑。就像在3.3.1中描述的一样,每个人物都有自己的运行时间栈以适应上下文切换。甚至于,这里有RTOS使用的另一个运行时间栈给main()函数,硬件中断和软件中断。这个系统栈在应用连接文件中调用用来替换应用尾部的应用RAM

对于IAR来说,这个RTOS系统栈是通过CSTACK符号定义的:

//////////////////////////////////////////////////////////////////////////////// // Stack //

define symbol STACK_SIZE = 0x400; define symbol STACK_START = RAM_END + 1;

define symbol STACK_END = STACK_START - STACK_SIZE;

define block CSTACK with alignment = 8, size = STACK_SIZE { section .stack }; //

define symbol STACK_TOP = RAM_END + 1; export symbol STACK_TOP; //

place at end of RAM { block CSTACK };

在IAR中,通过修改应用连接文件中的STACK_SIZE符号的值傀来改变CSTACK的大小

对于CCS来说,RTOS系统栈是appBLE.cfg中的Program.stack参数定义的RTOS配置文件:

/* main() and Hwi, Swi stack size */ Program.stack = 1024;

通过连接器在应用RAM空间中替换:

/* Create global constant that points to top of stack */ /* CCS: Change stack size under Project Properties */ __STACK_TOP = __stack + __STACK_SIZE;

3.11.4 手动修改RAM的边界

像上面提到的,边界工具用来调整ICALL_RAM0_ADDR应用栈的边界以便于给应用工程提供忒大的RAM。尽管不是要求的,ICALL_RAM0_ADDR可以通过下面的步骤手动修改。 IAR的做法:

1. 在栈工程中应用边界工具,阅读3.12配置边界工具 2. 在IAR_Boundary.xcl中调整ICALL_STACK0_ADDR

--config_def ICALL_RAM0_ADDR=0x200043AC

3. 重新编译应用和栈工程。验证两个工程是否有编译错误。 CCS的做法:

1. TOOL/ccsLinkerDefines.cmd 中调整ICALL_RAM0_ADDR:

--define=ICALL_RAM0_ADDR=0x200043AC

2. 重新编译应用栈工程,验证两个工程是否有编译错误。

当修改ICALL_RAM0_ADDR时需记住以下几点

1. ICALL_RAM0_ADDR的值必须是4字节对齐的地址。

2. 当ICALL_RAM0_ADDR被修改后,应用和栈工程都必须清除并重新编译。

3. 当手动修改ICALL_RAM0_ADDR之后发生了连接错误,验证每个工程是否有足够的RAM调

用空间

3.11.5 动态内存调用

该系统使用了两个堆来实现动态内存调用。因此明白每个堆的用法很重要,这样应用开发者就可以最大限度的使用提供的内存了。

RTOS配置了一个小的堆在appBLE.cfgRTOS配置文件中:

var HeapMem = xdc.useModule('xdc.runtime.HeapMem'); BIOS.heapSize = 1668;

这个堆(“heapMEM”)用于初始化RTOS实体和分配BLE协议栈任务的运行时间的栈。这个堆的大小需要选择以适应系统初始化的要求。因为该堆的小尺寸,不允许调用RTOS堆的内存空间给一般应用使用。要获得关于TI-RTOS堆配置的更多信息,请看SYS/BIOS用户指导中的堆的实现章节

相反,这里有一个单独的堆是留给应用使用的。Icall模块直接初始化一个应用的RAM,heapmgrHeapstore,这个区域可以被各种任务使用。这个Icall堆的大小是在应用进程宏HEAPMGR_SIZE定义的,SimpleBLEPeripheral工程中默认设为2672。尽管Icall堆在应用工程中定义,但是同样与BLE协议栈共享。为了增加Icall堆的大小,需要在应用工程中调整预处理符HEAPMGR_SIZE的值

为了确定Icall堆使用的数量,定义应用工程中的HEAPMGR_METRICS预处理符。参考$BLE_INSTALL$\\Components\\applib\\heap中heapmgr.h文件提供的堆度量。 这里有个关于动态在Icall堆中调用一个可变长度队列的例子:

//define pointer uint8_t *pArray;

// Create dynamic pointer to array.

if (pArray = (uint8_t*)ICall_malloc(n*sizeof(uint8_t))) {

//fill up array } else {

//not able to allocate }

这里是一个关于释放上面的队列的例子: Icall_free(pMsg->payLoad);

3.11.6 关于初始化RTOS实体的一个主意

因为RTOS的堆的大小是有限制的,所以强烈要求构建而不是创建RTOS实体。为了阐明这点,考虑Clock_construct()和Clock_create()函数之间的区别。这里是它们来自SYS/BIOS接口中的定义:

通过声明一个静态的Clock_Struct实体并发送这个实体到Clock_construct()中,这个真实的Clock_Struct的.DATA区域开始使用。不是限制的RTOS堆。相反,Clock_create()将会导

致RTOS为Clock_Struct分配RTOS的限制堆。

一般而言要尽可能的在贯穿整个工程中初始化时钟和RTOS实体。如果必须使用创建RTOS实体,那么RTOS堆就可能需要在appBLE.cfg中进行调整大小。

3.12使用边界工具来调节RAM & Flash的边界配置

边界工具(boundary.exe)在调整各自的预处理符时非常实用,ICALL_STACK0_ADDR(flash)和ICALL_RAM0_ADDR(RAM),这些边界都是在应用和栈工程之间共享的。边界工具可以通过调整边界来把没有用到的flash或者RAM的空间分配给应用工程。该工具消除了工作在双工程环境下手动调整RAM和Flash边界的必要。

边界工具不会修改工程文件。另外,边界工具也不会修改任何源代码或任何编译/链接的优化表现。该工具只是基于对工程映射和链接配置文件的分析后简单的调整RAM和Flash各自的边界地址。

边界工具安装路径为:

C:\\Program Files (x86)\\Texas Instruments\\Boundary

该路径下有个ReadMe.txt文件,该文件包含了该工具的其他信息。

3.12.1 配置边界工具

边界工具使用一个XML文件,BoundaryConfig.xml,位于工具的安装路径,用来配置默认的工具操作。要求保持这些默认值。

每个SDK中的工程都有一系列的配置文件,这些文件由IDE的连接器和编译器使用来设置或调整Flash和RAM各自的值。这些配置文件位于各自工程的如下位置:

$BLE_INSTALL$\\Projects\\ble\\\\CC26xx\\\\Config 当是工程(如SimpleBLEPeripheral)并且是IAR或CCS.

1. 边界连接器配置文件:IAR-Boundary.xcl [IAR] or ccsLinkerDefines.cmd [CCS].用于定义

ICALL_STACK0_ADDR和ICALL_RAM0_ADDR边界地址。这个文件位于TOOLS IDE文件夹下面并且在有调节需求是由边界工具更新。

2. 边界C的定义文件IAR-Boundary.cdef [IAR] or ccsCompilerDefines.bcfg [CCS].限于IAR或

CCS连接器的限制,ICALL_STACK0_ADDR必须在该文件中的定义与连接器中的配置文件中的定义一致。这个文件位于TOOLS IDE文件夹,当有调节需求时由边界工具更新。

3.12.2 边界工具操作

边界工具(boundary.exe)被作为一个栈工程IDE编译后的操作。如果需要对RAM和/或Flash的边界进行调整,边界工具会更新连接器或编译器各自的配置文件并生成一个编译后的错误来通知一个变化应经发生。为了配合配置值的更新,在应用和栈工程中各执行一个“Project

??Rebuild All”。

除了代码和内存的大小外,边界工具也会在计算ICALL_STACK0_ADDR的值时考虑预留下来的flash页。flash保留页的例子包含在CCA页。flash预留页在栈工程连接器配置文件中定义。 一个使用边界工具的先决条件就是应用和栈工程第一次编译必须是成功的。如果产生一个连接器错误,首先应验证本次的改变有没有超出设备flash和/或RAM内存的最大值。典型的连接器错误是当栈工程配置为使用要求外部flash内存的特性。依据错误是否由flash或RAM空间造成,在各自的连接器和编译器配置文件中手动调整flash和/或RAM的边界。一旦工程可以连接成功,边界工具会重新调整各自的边界值为最佳值。然后按照要求执行“Rebuild All”。

注意:除非IAR配置为展示所有的编译消息,否则是不会给出为什么边界工具会产生一个编译错误的原因的。当IAR配置为显示所有的编译消息,“TheProject Boundary Address Has Been Moved”就会显示在编译输出窗口。在IAR中设置Tools ??Options ??Messages ??Show Build Messages to “All”.

一个关于执行了边界修改的编译消息的例子如下:

/////////////////////////////////////////////////////////////////////// Boundary Operation Complete

/////////////////////////////////////////////////////////////////////// <<< WARNING >>>

The Project Boundary Address Has Been Moved Or A Config File Change Is Required Rebuild This Project For The Address Or Config File Change To Take Effect ///////////////////////////////////////////////////////////////////////

3.12.3 禁止使用边界工具

为了禁能边界工具,打开栈工程的工程操作项选择“Build Actions”(IAR)或者“Steps”在CCS Build window(CCS),然后想下图所示一处编译后命令行。最好提前对该命令进行备份以便后面会用到边界工具。一旦边界工具被禁能了,连接器和编译器各自的配置文件就可以像3.10.3和3.11.4节中的步骤进行手动编辑了。

4 应用

这一章经描述SimpleBLEPeipheral工程的应用部分,这些应用部分都包含: 1. Pre-RTOS初始化;

2. SimpleBLEPeripheral任务:这是个应用任务,是系统中优先级最低的任务。该任务的

代码包含在编译器IDE文件夹“Application”中的simpleBLEPeripheral.c和simpleBLEPeripheral.h文件中。

3. Icall:一个抽象了的栈和其他任务之间的通讯接口模块。

注意:GAPRole任务也是应用工程工作空间的一部分。但是这个任务的功能性更接近于协议栈所以在5.2章中描述。 4.1 Start-up in main()

main()函数在IDE “Startup”文件下的main.c文件中,是整个运行时间的开始点。这里也是板子提出中断禁能和驱动初始化的地方。同样在这个函数中,电源管理也会初始化,任务会构建或创建。在最后一步,使能中断,SYS/BIOS内核调度通过调用BIOS_start()启动,没有返回,看第8张获取main()函数到达前的启动顺序:

Void main() {

PIN_init(BoardGpioInitTable); //enable iCache prefetching VIMSConfigure(VIMS_BASE, TRUE, TRUE); // Enable cache

VIMSModeSet(VIMS_BASE, VIMS_MODE_ENABLED); #ifndef POWER_SAVING

/* Set constraints for Standby, powerdown and idle mode */ Power_setConstraint(Power_SB_DISALLOW); Power_setConstraint(Power_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(); }

应用和GAPRole任务想3.3中描述的一样被构建了。栈任务在Icall_createRemoteTasks()中被创建了。Icall模块通过Icall_init()初始化了。整个IDE工作空间的主题,main.c存在于应用工程,这意味着当它编译后会处于应用调用的flash扇区中。 4.2 ICALL 4.2.1 介绍 间接调用框架(Icall)是一个模块,提供了应用面向BLE协议栈服务(如BLE-Stack APIs)和由RTOS提供的原始的服务(如线程同步)的机制。结合的,Icall允许应用和协议栈在一个统一的RTOS环境中高效的操作,通讯和共享资源。 Icall架构的核心组成部分是调度者,该调度者促进了应用和BLE协议栈任务间的应用程序接口通过双镜像边界。尽管多数的ICall交互抽象成了BLE协议栈接口(如:GAP,HCI等),应用开发者也有必要明白这种潜在的架构以便于在多线程RTOS环境中做到合适的BLE协议栈操作。

ICall模块源代码在应用工程中的“ICall”&“ICallBLE” IDE文件夹中。

4.2.2 ICall BLE协议栈服务

就像上图描述的一样,ICall内核使用案例包含了一个服务实体(如BLE协议栈任务)和一个客户端实体(如应用任务)之间的通讯。注意ICALL框架不能与BLE协议定义的GATT服务/客户相混淆。这样架构的原因是双重的:为了可以分开更新应用和BLE协议栈也是为了维持划分自cc2640的TI-RTOS逻辑层的软件的一致性(就想cc254x的OSAL)。

ICall BLE协议栈服务充当所有的BLE-Stack API的应用接口。当一个BLE协议栈接口被应用调用时,ICall模块将命令输送给BLE协议栈,适当的时候,将来自BLE蓝牙协议栈的消息返回给应用。

由于ICall模块时应用工程的一部分,应用任务可以通过直接函数调用访问ICall。注意由于BLE协议栈以最高优先级执行,应用任务将会阻塞直到回复的到来。某些协议栈的接口可能会立即响应,然而应用线程将会阻塞,因为该接口通过ICall调度给了BLE协议栈。其他的BLE协议栈接口可能通过ICall同步回应给应用,将恢复发送给应用任务的事件处理器中。 4.2.3 ICall的基本服务

另外,ICall包含了一个基本的服务就是抽象各种操作系统相关的函数。由于要共享资源和维持内部进程的通讯,应用应该使用下面的基本服务函数:

10. {

// Process message.

SimpleBLEPeripheral_processAppMsg(pMsg); // Free the space from the message. ICall_free(pMsg); }

1. }

这些消息用SimpleBLEPeripheral_enqueueMsg()函数排序。因为它们被发送到队列中,当它们发送时会被处理。一个这方面的通用的例子就是在回调函数中接收到一个事件(阅读5.3.4.2.4章)。

4.3.2.3 通过内部事件值发送的事件

5. if (events &SBP_PERIODIC_EVT) 6. {

7. events &= ~SBP_PERIODIC_EVT; 8. Util_startClock(&periodicClock); 9. // Perform periodic application task 10. SimpleBLEPeripheral_performPeriodicTask(); 11. }

这些发送给应用任务的同步事件是通过设置应用任务的events的值的相关位实现的,每个位对应着一个定义好的事件,比如:

// Internal Events for RTOS application #define SBP_STATE_CHANGE_EVT 0x0001 #define SBP_CHAR_CHANGE_EVT 0x0002 #define SBP_PERIODIC_EVT 0x0004

函数在设置这些事件值的位时必须确保发送给信号旗来激活应用来执行。这方面的例子就是时钟处理函数用来处理时钟的超时。这部分的描述在3.4.2中。

注意当添加一个事件时,对于给定的任务其值必须是唯一的并且必须多于2(这样就可以只有一个位可以设置)。由于events变量是按照uint16_t初始化因此最多可以定义16个内部事件。 4.3.3 回调

应用代码同样也包含了各种给协议层、profile和RTOS模块的回调函数。为了确保线程的安全,在一个确切的回调函数中应该进行最小的处理,大量的处理应该在应用的上下文中完成。因此每个回调函数通常都对应两个函数(参考GAPRole状态改变回调函数): 1. 确切的回调函数:这个函数在调用函数的上下文中调用(如GAPRole任务)。为了在调

用上下文中进行最小的处理,所有这个函数的应该将一个事件排列到应用队列中进行处理。

static void SimpleBLEPeripheral_stateChangeCB(gaprole_States_t newState) {

SimpleBLEPeripheral_enqueueMsg(SBP_STATE_CHANGE_EVT, newState); }

2. 在应用上下文中进行的函数:当应用被回调函数的排序唤醒后,这个函数就会在事件通

过应用队列发送和处理之后被调用。

static void SimpleBLEPeripheral_processStateChangeEvt(gaprole_States_t newState) {

...

}

看5.2.1节中这种处理的流程图。 5 BLE协议栈

这一章将描述BLE协议栈的功能性和提供的一些列面向协议栈的API。 栈工程和它相关的文件服务于实现BLE协议栈任务。这是系统中最高优先级的任务并且像本文第二张插图所示的实现BLE协议栈。

多数BLE协议栈都以实体代码的形式在一个单个库文件中提供(Texas Instruments不提供协议栈的源代码作为一种政策)。因此,明白不同协议栈层的功能性和他们是怎样与应用和Profile互动的是非常重要的。本章将努力解释这些层。 5.1 通用通道配置(GAP)

BLE协议栈的GAP层与连接功能相关。它处理设备的通道模式和处理包括设备发现,链路建立。链路终止,初始化安全因素和设备配置。

基于设备被配置为那种角色,设备可以处于多种状态如上图所示并在下面进行了描述。 1. 就绪(standby):初始空闲状态由重启进入; 2. 广播者(Advertiser):设备广播指定的数据让任何初始化的设备都知道自己是个可以连

接的设备。广播包中包含了设备地址和可以包含一些额外的数据如设备名称。

3. 扫描者:扫描设备,取决于接收到的广播包,发送一个扫描请求“scan request”给广播

者。广播者恢复一个“scan response”扫描回复。这是设备发现的处理,在这种情况下扫描设别现在意识到广播设备,也知道了该设备可以连接。

4. 初始者:当初始化时,模拟器必须指定一个确定设备的地址来连接。如果一个广播包接

收到了并且符合确定设备的地址,初始化设备就就随后发送出一个建立连接的请求(链路)与广播设备,这些请求中的连接参数在5.1.1中描述。

5. 从机/主机:一旦一个连接形成了,广播者就会作为从机,而初始化者就会作为主机。 5.1.1 连接参数

这部分将描述不同的连接参数,它们与连接请求一起有初始设备发出,可以被建立连接后的两个设备的任一设备修改。这些参数分别是:

1. 扫描间隔:在BLE连接中使用了调频计划,在这种背景下两台设备各自发送和接收到的

数据只在特定时刻的特定的通道,然后相遇在一个新的通道(BLE协议栈的链路层处理通道的变化)在指定的时间计数之后。这种相遇中两个设备发送和接收的数据就是所谓的“连接事件”。即使没有应用数据发送或接收,两个设备都会交换链路层的数据以维持连接。连接间隔就是两次连接事件之间的时间间隔,最小单位为1.25ms。连接间隔可以从最小值6(7.5ms)到最大值3200(4s)。

不同应用可能要求不同的连接间隔。像在5.1.3中描述的,这样会影响设备的功耗。关于设备功耗的更多的信息请参考《power consumption application note [3].》 2 从机潜伏(Slave Latency):这个参数可以是从机设备(外设)可以跳过数个连接事件。这样就给予了外设设备一些灵活性。在这种情况下如果他没有任何数据去发送它就可以选择跳过数个连接事件并保持睡眠,这样就节省了一些电力。决策由外设设备决定。 从机潜伏值与可以跳过的最大事件数量有关。它的范围可以从0(意味着一个事件也不跳过)到一个最大的499:然而最大值必须不能超过有效连接间隔的16s。

3 超时检测:这个是两次成功连接事件间的最大时间。如果在这个时间段内没有出现一次陈宫的连接事件,设备就会考虑连接丢失,返回非连接状态。这个参数的值的单位为10ms。超时检测值的范围可以从10(100ms)到3200(32s)。另外,超时时间必须大于有效连接间隔(后面再解释)。 5.1.2 有效连接间隔

有效连接间隔等于两次连接事件间的时间间隔,假设从机潜伏开启了并跳过了最大可能的事件(有限连接间隔就等于确切的连接间隔如果从机潜伏设为0)。可以使用下面的公式计算有效连接间隔:

Effective Connection Interval = (Connection Interval) * ( 1 + (Slave Latency) )

参考下面的例子:

1. 连接间隔为80(100ms); 2. 从机潜伏:4

3. 有效连接间隔就为:100ms * (1 + 4) = 500ms;

这告诉我们在从机没有数据发送给主机的情况下,从机每隔500ms发送一次连接事件。 5.1.3 连接参数的注意事项 在许多应用中,从机将会跳过最大的连接事件,因此当选择或请求连接参数是考虑连接参数是非常有用的。选择正确的连接参数组合在BLE设备的功耗优化中扮演着重要的角色。下面列出了权衡连接参数设置的一些通用的总结: 1 降低连接间隔将会:

1. 两台设备同时增加了功耗; 2. 增加了两个方向的吞吐率;

3. 降低了任一方向的数据发送的时间消耗。 2 增加连接间隔将会:

1. 降低两台设备的功耗; 2. 降低两个方向的吞吐率;

3. 增加了任一方向的数据发送的时间消耗。 3 降低从机潜伏(或者设置为0)将会:

1. 增加外设设备的功耗;

2. 降低了中央设备发送数据给外设设备的时间消耗 3 增加从机潜伏将会:

1. 降低外设在外设没有数据发送给中央设备期间的功耗; 2. 增加了中央设备发送给外设设备的时间消耗。 5.1.4 连接参数更新 在某些案例中,中央设备将会要求一次与外设设备的连接包含连接参数是不符合外设设备的。在另一些案例中,一个外设设备可能有在连接过程中修改连接参数的需求,基于外设设备应用。外设设备可以请求中央设备修改连接设置通过设置“Connection Parameter Update Request”。对于BT4.1的设备,这种请求是至直接被链路层处理。对于BT4.0设备来说,协议栈的L2CAP层将处理这种请求。注意BLE栈自动选择更新方式。

这种请求包含了四个参数:最小连接间隔,最大连接间隔,从机潜伏,和超时时间。这些值代表了外设在连接时想要的参数值(连接间隔给定的是个范围)。当中心设别接收到这个请求后,它就会执行接受或不接受这些新的参数。 5.1.5 连接终止

一个连接可以被主机或从机以任何理由主动的终止。一边启动终止,另一边就必须依据两边设备离开连接状态之间的情况作出回应。 5.1.6 连接安全

GAP同样处理在BLE连接过程中的开启安全因素。确信的数据可能只在已认证的连接中读和写。一但一个连接形成了,两个设备可以通过所谓配对的过程。当配对完成后,密匙就会建立用来加密和认证链路。在一个典型的案例中,外设设备将请求中心设备提供的密码来完成配对进程。这将是个固定的值,比如“000000”,或者可以是一个随机产生的值提供给用户(诸如在一次显示中)。在中心设备发送完正确的密码之后,两个设备交换安全密匙来加密和认证链路。

在许多案例中,同一个中心设备和外设设备将会频繁的连接和断开连接。BLE有一个安全因素允许两个设备,当配对时,给彼此一个长期的安全密匙。这个因素就叫做绑定,允许两个设备每次连接在不经过全面的配对过程后重新连接后能快速的重新建立加密或认证,只要它们保存着长期的密匙的信息。 在SimpleBLEPeripheral应用中,GAP角色的管理是由GAP角色 profile处理,绑定信息有GAP安全profile管理。 5.1.7 GAP 抽象

应用和profile直接调用GAP API函数来实现BLE相关的工程如广播和连接时有可能的。然而多数GAP的功能是由GAPRole任务处理的。这个抽象层在下面的图中描述:

因此,使用它的接口面向GAP层更好更容易的配置GAPRole模块。这部分在5.2节描述。因为这个原因,下面的GAP层部分将描述那些不通过GAPRole任务处理货配置的函数和参数,因为他必须直接通过GAP层修改。 5.1.8 配置GAP层

GAP层的函数多数定义在库代码中。函数头可以在协议栈工程中的gap.h文件中找到。像上面声明的一样,这些函数多数是由GAPRole使用,不会直接调用。作为参考,GAP接口定义在附件4中。然而,这里有几个参数需要在开始GAPRole之前进行修改。这些可以通过GAP_SetParamValue()和GAP_GetParamValue()函数设置或获取,干好了广播/扫描间隔,窗口等(查看接口获取更多信息)。作为一个例子,这里有个在SimpleBLEPeripheral_init()中完成的GAP层配置:

// Set advertising interval {

uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL; GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MIN, advInt); GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MAX, advInt); GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MIN, advInt); GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MAX, advInt);

}

5.2 GAP任务

像上面提到的,GAPRole任务是个独立的任务,通过处理多数GAP层的功能来卸载应用。有应用依据初始化使能和配置。就这种配置,许多BLE协议栈事件将直接有GAPRole任务配置并永远不会通过应用。然而,这里有些回调函数,应用可以注册到GAPRole任务中,这样它就可以被特定的事件或相关的进程通知。

基于设备的配置,GAP层中是操作下下面四种角色中的一种: 1. 广播者:一个广播者不可连接

2. 检测者:扫描广播包,但是不启动连接;

3. 外设:一个广播者并且可以连接,作为从机操作在一个链路层连接

4. 中心:扫描广播包并启动连接。作为主机操作在一个或多个链路层连接。当前,BLE中

心协议栈支持三个同时连接。

甚至于,BLE规格书允许多角色结合,所有BLE协议栈支持的角色。参考10.4节,BLE栈元

素的配置。

5.2.1 外设角色

外设GAPRole任务定义在peripheral.c和peripheral.h中。全部的API包含命令,可配置参数,事件和回调函数,这些在附件1.3中描述。使用该模块的通用步骤为: 1. 初始化GAPRole参数。这应该在应用初始化函数中完成。(比如:SimpleBLEPeripheral_init())

{

// For all hardware platforms, device starts advertising upon initialization uint8_t initialAdvertEnable = TRUE; uint16_t advertOffTime = 0;

uint8_t enableUpdateRequest = DEFAULT_ENABLE_UPDATE_REQUEST; uint16_t desiredMinInterval = DEFAULT_DESIRED_MIN_CONN_INTERVAL; uint16_t desiredMaxInterval = DEFAULT_DESIRED_MAX_CONN_INTERVAL; uint16_t desiredSlaveLatency = DEFAULT_DESIRED_SLAVE_LATENCY; uint16_t desiredConnTimeout = DEFAULT_DESIRED_CONN_TIMEOUT; // Set the GAP Role Parameters

GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &initialAdvertEnable);

GAPRole_SetParameter(GAPROLE_ADVERT_OFF_TIME, sizeof(uint16_t), &advertOffTime);

GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData), scanRspData);

GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData); GAPRole_SetParameter(GAPROLE_PARAM_UPDATE_ENABLE, sizeof(uint8_t), &enableUpdateRequest);

GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t), &desiredMinInterval);

GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t), &desiredMaxInterval);

GAPRole_SetParameter(GAPROLE_SLAVE_LATENCY, sizeof(uint16_t), &desiredSlaveLatency);

GAPRole_SetParameter(GAPROLE_TIMEOUT_MULTIPLIER, sizeof(uint16_t), &desiredConnTimeout);

}

2. 初始化GATRole任务。这个应该在应用初始化函数中完成。这包含把函数指针发送到应

用的回调函数中。这些回调函数在附件II.3中定义。

// Start the Device

VOID GAPRole_StartDevice(&SimpleBLEPeripheral_gapRoleCBs);

3. 按照应用需要的发送GAPRole命令。这里是个应用使用GAPRole_TerminateConnection()

的例子。绿色的与应用上下文相关,红色的与BLE协议栈的上下文相关。

注意BLE协议栈返回的值仅仅指示是否尝试终止连接被启动成功。确定的连接终止时间将会通过返回接着被描述。附件II.3中的接口列出了每个命令的返回参数和相关回调函数事件。 4. GAPRole任务将处理更多的来自BLE协议栈GAP相关的事件。然而,有些事件同时也朝

向应用。这里有个关于从BLE协议栈发送GAP_LINK_TERMINATED_EVENT到应用的例子。绿色代表了应用的上下文,橙色的代表了GAPRole上下文,红色的是协议栈上下文:

5.2.2 中心角色

中心角色人物定义在central.c和central.h文件中。全部的命令,可配置参数,事件,和回调函数都在附件III中描述。 使用该模块的一般步骤为: 1. 初始化GAPRole参数。这些参数定义在附件III中。这些应该在应用初始化函数中完成(如

SimpleBLECentral_init()):

// Setup GAP

GAP_SetParamValue(TGAP_GEN_DISC_SCAN, DEFAULT_SCAN_DURATION); GAP_SetParamValue(TGAP_LIM_DISC_SCAN, DEFAULT_SCAN_DURATION);

GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, (void *)attDeviceName);

2. 开始GAPRole任务。这应该在应用初始化函数中完成。这包含把函数指针发送给应用回

调函数。这些回调函数在附件III.3中定义:

// Start the Device

VOID GAPCentralRole_StartDevice(&SimpleBLECentral_roleCB);

3. 按照应用的需求发送GAPRole命令。这里是一个关于使用GAPCentralRole_StartDiscovery()

的例子。绿色部分是应用的上下文,红色部分是BLE协议栈的上下文。

注意BLE协议栈的返回值仅指示尝试执行设备发现是否开启。确切的连接终止将会同步返回并随后处理。API列出了每个命令和相关回调事件的返回参数。

4. GAPRole任务将会处理多数来自BLE协议栈任务的GAP相关的事件。然而有一些事件也

是指向应用的。这里有个从BLE协议栈发送给应用的GAP_DEVICE_DISCOVERY_EVENT事件的例子。绿色是关于应用上下文的,橙色是GAPRole上下文的,红色是关于协议栈上下文的。

5.3 通用属性配置(GATT)

鉴于GAP层处理多数与连接相关的功能,BLE协议栈的GATT层设计为应用用来处理两个连接了的设备之间的数据通讯。数据以特征值的形式存储和发送并存储在BLE设备的内存中。从GATT立足点来看,当两个设备建立连接后它们就分别是下面两种角色的一种: 1. GATT 服务端:该设备包含了特征值数据库可以被GATT客户端读写。 2. GATT客户端:该设备从GATT服务端读写特征值。

下面的插图描述了一个简单的BLE连接过程中的这种关系。外设设备如SensorTag作为GATT服务端,中心设备如智能手机作为GATT客户端。

以此典型的例子来说,需要注意的是GATT角色如客户端和服务端是完全不同于GAP角色中是外设和中心的。一个外设可以是GATT客户端,也可以是GATT服务端。同样中心设备也可是GATT的客户端或服务端。也可以同时扮演GATT客户端或服务端。 5.3.1 GATT特征值/属性 当特指BLE时,这两者的概念可以互换,因此把特征值看作值一组叫做属性的信息组成的是非常有帮助的。属性是确实传递在设备间的信息。特征值组织和使用属性作为数据值,性能或配置信息。一个电信更多特征值有下面的属性构成: 1. 特征值的值(Characteristic Value):就是特征值的数据值 2. 特征值声明(Characteristic Declaration):描述器存储其性能,位置和特征值的数据类

型。

3. 客户端特征值配置(Client Characteristic Configuration):这个允许GATT服务端配置特征

值为通知(发送给GATT服务端)或指示(发送给GATT服务端并期望一个回复)。 4. 特征值用户声明(Characteristic User Description):这是个ASCII字符串描述的特征值。 所有的这些属性都会存在GATT服务端的一个属性表中。除了其值本身,每个属性都有下面的性质:

1. 句柄(Handle)这是基本的属性在表中的索引。每个属性都有一个独一的句柄 2. 类型(type):这个用来说明属性数据代表的意思。经常是作为UUID(universal unique

Ididentifier)的。这其中的一些是Bluetooth-SIG定义的一些是用户的。

3. 许可(permissions):这个会强制一个GATT客户端设备是否和怎样访问属性的值。 5.3.2 GATT 服务/profile

一个 GATT服务端是一个特征值的集合。举例说明,心率服务包含了一个心率测量特征值和一个身体位置特征值和别的特征值。多个服务可以组合成一个profile。实际上,许多profile只实现一个服务因此两个概念有时是可以互换的。

在SimpleBLEPeripheral应用案例中,有四个GATT profile:

1. Mandatory GAP Service这个服务包含了设备和通道的信息,诸如设备名称,供应商识别,

生产识别,和一部分BLE协议栈。它要求每个GATT服务都遵循BLE规范。这个服务的源代码没有提供,编译进了栈的库了。

2. Device Info Service;这个服务展示的信息是关于设备的,如硬件,软件,固件版本,

regulatory & compliance info,和生产商名称。设备信息服务作为BLE协议栈的一部分,有应用配置。

3. SimpleGATTProfile Service:这个服务是个简单的profile是用来提供测试和示范的。在

simpleGATTProfile.c和simpleGATTProfile.h中提供完整的源代码。

4. SimpleGATTProfile Service:这个服务是一个简单的profile用来作为测试和示范的。完整的

代码在simpleGATTProfile.c和simpleGATTProfile.h文件中提供。

在SimpleBLEPeripheral工程中的属性表中给定部分与下面展示和描述的simpleGATTProfile服务是一致的。这部分的意思是介绍属性表。想了解这个profile是如何在代码中实现的信息,请参考5.3.4.2.

simpleGATTProfile包含了五个特征值

1. SIMPLEPROFILE_CHAR1:一个一字节的值可以从GATT客户端设备读和写。 2. SIMPLEPROFILE_CHAR2:一个一字节的值可以从GATT客户端设备读,但是不能写。 3. SIMPLEPROFILE_CHAR3:一个一字节的值可以从GATT客户端设备写,但是不能读。 4. SIMPLEPROFILE_CHAR4:一个一字节的值不能直接从GATT客户端设备读或写。是

可通知的:可以设置为通知发送给一个GATT客户端设备。

5. SIMPLEPROFILE_CHAR5:一个五字节的值,可以从一个GATT客户端设备读,但是

不能写。

这里逐条描述这个属性表,以句柄作为参考:

1. 0x001F:这个是simpleGATTProfile服务的声明。有一个值为0x2800的UUID(蓝牙定义

GATT_PRIMARY_SERVICE_UUID)。它的值就是simpleGATTProfile的UUID(用户定义的)。 2. 0x0020:这个是simpleProfileChar1 特征值声明。这个可以认为是SimpleProfileChar1值

的指针。有一个值为0x2803的UUID(蓝牙定义为GATT_CHARACTER_UUID)。它的值,同样包括所有其他特征值的声明,是一个五字节的值,下面解释这五个字节的意思(从最高有效位到最低有效位):

1. 字节0;是SimpleProfileChar1的权限。这些值定义在蓝牙说明书中。这里是部分相

关权限:

1. 0x02:允许读特征值的值

2. 0x04:允许写特征值的值不需要回复; 3. 0x08:允许写特征值的值需要一个回复; 4. 0x10:允许通知特征值的值(不需要确认); 5. 0x20:允许通知特征值的值(需要确认); 值为0x0A意味着特征值可读可写。

2. 字节1-2:这里字节保存了SimpleProfileChar1值的位置句柄(句柄Wie0x0021) 3. 字节3-4:SimpleProfileChar的UUID的值(用户定义为0xFFF1)。

3. 0x0021:这个是SimpleProfileChar1的值。它有个值为0xFFF1的UUID(用户定义)。它

的值是确切加载大哦特征值中的数据。依据它的特征值声明(句柄0x0020),它是可读可写的)。

4. 0x0022;这个是SimpleProfileChar1 的用户声明。UUID为0x2901(蓝牙定义)。它的值

是用户可读的字符串用来描述特征值。

5. 0x0023-0x002F:这些剩下的属性与上面描述的SimpleProfileChar1有着同样的结构可以

一视同仁。唯一不同的属性,句柄为0x002B,在下面描述

6. 0x002B:这个是SimpleProfileChar4客户端特征值配置。它的UUID值为0x2902(蓝牙定

义的)。通过写这个属性,一个GATT服务端就可以配置SIMpleProfileChar4为通知(写0x0001)或指示(写0x0002)。写0x0000到这个属性将会禁能通知或指示。

5.3.3 GATT客户端抽象

与GAP层相似,GATT层也是可以抽象的。然而这种抽象要依据设备扮演的是GATT客户端还是GATT服务端。甚至于,像BLE规范定义的,GATT层本身就是ATT层的一个抽象。 GATT客户端没有属性表或者它们聚合的profile,不存储,信息。因此,多数面向GATT层的将会直接从应用发送。在这个案例中,直接在附件IV中描述的GATT API将会被采用。抽象层可以是下面的形式:

5.3.3.1 直接使用GATT层

这一章将讲述如何在应用中直接使用GATT层。GATT层的函数是在库中实现的但是可以在GATt.h中发现头函数。完整的GATT API可以在附件IV中找到。关于这些命令的更多信息可以在《Appendix IV》中找到。如上面所描述的,这些函数将会首先用在GATT客户端的应用中。这里有一些特殊的函数在API中,但是这里不考虑。注意多数GATT函数都会返回一个ATT事件给应用,所以有必要了解附件IV中的ATT API。通常作为GATT客户端使用GATT层的步骤(在SimpleBLECentral工程中)的步骤如下: 1. 初始化GATT客户端

VOID GATT_InitClient();

2. 注册用来接收来自ATT指示/通知

GATT_RegisterForInd(selfEntity);

3. 执行GATT客户端进程。这是用GATT_WriteCharValue()作为例子,这个是在

SimpleBLECentral应用中被一个左键按下触发的。绿色的部分与应用上下文有关,红色的与协议栈上下文有关。

4. 在应用中接收和处理返回给GATT客户端进程的回复。在这个例子中,应用将会接收一

个ATT_WRITE_RSP事件。参考附件IV.5获取GATT命令列表和他们对应的ATT事件。绿色部分是应用上下文,红色部分是协议栈上下文。

本文来源:https://www.bwwdw.com/article/3yyw.html

Top