authentication-manager中的erase-jenkins credentialss属性无效,我用的是3.05版本,请问这个是版本的问题吗?

Service UnavailableSpringSecurity的remember-me(记住我,记住密码,免登陆)功能无效 - ITeye问答
我用的是版本是3.0.5版本authentication-manager标签里没有erase-credentials="false"的属性,下面上配置文件请大神们指点。
&beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd"&
&!-- 访问被拒绝时跳转到403界面 --&
&!-- 在http标签中配置 use-expressions="true" 支持sec:authorize权限控制后在所有非java文件的地方都要使用hasRole('') --&
&http entry-point-ref="authenticationProcessingFilterEntryPoint" auto-config="false"
access-denied-page="/403.jsp"&
&!-- 放行页面 --&
&intercept-url pattern="/*.css" filters="none" /&
&intercept-url pattern="/error.jsp" filters="none" /&
&intercept-url pattern="/captcha.jsp" filters="none" /&
&intercept-url pattern="/logout.jsp" filters="none"/&
&!-- 自定义登录页面 任何人都可以访问,此属性为只有https才可以访问 requires-channel="https" --&
&intercept-url pattern="/index*.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" requires-channel="any" /&
&intercept-url pattern="/**" access="isAuthenticated()" /&
&!-- 访问全部要有ROLE_JIANGYUAN或者ROLE_USER权限 --&
&intercept-url pattern="/*role_admin.jsp" access="ROLE_ADMIN" /&
&intercept-url pattern="/**" access="ROLE_USER" /&
&!-- ROLE_ADMIN和ROLE_USER都不是管理员权限 --&
&!-- 安全退出后的页面 --&
&logout logout-success-url="/logout.jsp" invalidate-session="true"
&!-- 两周内记住我
token-validity-seconds="300" key="springRocks"
services-ref="rememberMeServices" --&
&remember-me data-source-ref="dataSource" /&
&!-- session管理过滤器 --&
&custom-filter ref="concurrencyFilter" position="CONCURRENT_SESSION_FILTER" /&
&!-- 登录过滤器 --&
&custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER" /&
&!-- 免登陆过滤器
&custom-filter ref="rememberMeFilter" position="REMEMBER_ME_FILTER"/&
&!-- 防止session固话攻击 --&
&session-management session-fixation-protection="none" session-authentication-error-url="/time_out.jsp" invalid-session-url="/time_out.jsp" /&
&!-- session相关管理 --&
&session-management
session-authentication-strategy-ref="sas" /&
&!-- 启用jsr250的注解 --&
&global-method-security jsr250-annotations="enabled" /&
&!-- session管理过滤器 --&
&beans:bean id="concurrencyFilter"
class="org.springframework.security.web.session.ConcurrentSessionFilter"&
&beans:property name="sessionRegistry" ref="sessionRegistry" /&
&beans:property name="expiredUrl" value="/session-expired.htm" /&
&/beans:bean&
&!-- session管理相关注入 --&
&beans:bean id="sas"
class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy"&
&beans:constructor-arg name="sessionRegistry"
ref="sessionRegistry" /&
&!-- true限制不允许第二个用户登录,false第二个登陆用户踢掉前一个登陆用户 --&
&beans:property name="exceptionIfMaximumExceeded" value="false" /&
&!-- 当前用户最大连接数 --&
&beans:property name="maximumSessions" value="1" /&
&!-- 防止session攻击 --&
&!-- 每次都创建一个新的session --&
&beans:property name="alwaysCreateSession" value="true"/&
&!-- 不迁移session数据 --&
&beans:property name="migrateSessionAttributes" value="false" /&
&/beans:bean&
&beans:bean id="sessionRegistry"
class="org.springframework.security.core.session.SessionRegistryImpl" /&
&!-- session管理相关注入结束 --&
&!-- 自定义登录过滤 --&
&beans:bean id="loginFilter"
class="filter.UsernamePasswordAuthenticationExtendFilter"&
&!-- 认证器 --&
&beans:property name="authenticationManager" ref="authenticationManager" /&
&!-- 虚拟处理URL --&
&beans:property name="filterProcessesUrl" value="/login"/&
&!-- 用户名 --&
&beans:property name="usernameParameter" value="username"/&
&!-- 密码 --&
&beans:property name="passwordParameter" value="password"/&
&!-- 验证成功后的处理 --&
&beans:property name="authenticationSuccessHandler"
ref="loginLogAuthenticationSuccessHandler" /&
&!-- 验证失败后的处理 --&
&beans:property name="authenticationFailureHandler"
ref="simpleUrlAuthenticationFailureHandler" /&
&!-- session管理 --&
&beans:property name="sessionAuthenticationStrategy" ref="sas" /&
&beans:property name="rememberMeServices" ref="rememberMeServices"/&
&/beans:bean&
&!-- 开始注入登录过滤器 --&
&beans:bean id="loginLogAuthenticationSuccessHandler"
class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"&
&beans:property name="defaultTargetUrl" value="/welcome.jsp"/&
&/beans:bean&
&beans:bean id="simpleUrlAuthenticationFailureHandler"
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"&
&!-- 可以配置相应的跳转方式。属性forwardToDestination为true采用forward false为sendRedirect --&
&beans:property name="defaultFailureUrl" value="/index.jsp?error=true"/&
&/beans:bean&
&!-- 注入登录过滤器结束 --&
&!-- 免登陆过滤器
&beans:bean id="rememberMeFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter"&
&beans:property name="rememberMeServices" ref="rememberMeServices"/&
&beans:property name="authenticationManager" ref="authenticationManager"/&
&/beans:bean&
&beans:bean id="rememberMeServices" class="filter.IPTokenBasedRememberMeServices"&
&!-- 这个filter无论是自己重写的还是用Spring原声的 都不好使--&
&beans:property name="userDetailsService" ref="myUserDetailService"/&
&beans:property name="key" value="springRocks"/&
&beans:property name="cookieName" value="springRocks"/&
&beans:property name="parameter" value="_spring_security_remember_me"/&
&/beans:bean&
&beans:bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.RememberMeAuthenticationProvider"&
&beans:property name="key" value="springRocks"/&
&/beans:bean&--&
&!-- 认证器 --&
&authentication-manager
alias="authenticationManager"
&authentication-provider
user-service-ref="myUserDetailService" /&
&/authentication-manager&
&!-- 开始注入认证过滤器 --&
&beans:bean id="myUserDetailService" class="filter.MyUserDetailService" /&
&!-- 未登录的切入点 --&
&beans:bean id="authenticationProcessingFilterEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"&
&beans:property name="loginFormUrl" value="/index.jsp"/&
&/beans:bean&
&/beans:beans&
目前还没有答案
已解决问题
未解决问题cc2640蓝牙芯片软件开发指导说明文件-共享资料网
cc2640蓝牙芯片软件开发指导说明文件
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网站获取更多的信息和文档:/tool/CCSTUDIO 确保检查过BLE SDK的释放说明,注意ccs的版本和其它的工作相关的要求。另外,有CCS编译 生成的代码在大小上和表现上可能与IAR的生成的代码不同。2.5.3.1 配置ccs 这部分秘书安装和配置当前办呢的CCS和必要的工具。 1. 下载6.0.1或更高级的版本的ccs,下载地址为:http://processors./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 C&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 S 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 taskP // Configure task Task_Params_init(&taskParams); taskParams.stack = sbpTaskS 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 Cpointer to clock instance structure clockCB Cfunction to be called upon clock expiration clockDuration Clength of first expiration period. clockPeriod Clength of subsequent expiration periods. If set to 0, clock is a one-shot clock. startFlag CTRUE to start immediately, FALSE to wait. If FALSE, Util_startClock() must be called later. arg Cargument passed to callback funcitonreturnhandle to the Clock instancevoid Util_startClock(Clock_Struct *pClock)Description Parameters: Start an (already constructed) clock. pClock Cpointer to clock structurebool Util_isActive(Clock_Struct *pClock)Description Parameters: Returns: Determine if a clock is currently running. pClock Cpointer to clock structure TRUE: clock is running FALSE: clock is not runningvoid Util_stopClock(Clock_Struct *pClock)Description: Parameters: stop a clock. pClock Cpointer to clock structure3.4.2 功能示例 下面的例子来自于SimpleBLEPeripheral工程中详细描述了一个时钟实例的创建和如何处 理它的到期。 1 定义时钟处理函数来处理时钟到期的软件中断。 simpleBLEPeripheral.c://clock handler function static void SimpleBLEPeripheral_clockHandler(UArg arg) { // Store the event. events |= // Wake up the application. Semaphore_post(sem); }2创建时钟实体 simpleBLEPeripheral.c// Clock instances for internal periodic events. static Clock_Struct periodicC // 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 *pM // Create dynamic pointer to message. if (pMsg = ICall_malloc(sizeof(sbcEvt_t))) { pMsg-&event = pMsg-&status = pMsg-&pData = pD // Enqueue the message. return Util_enqueueMsg(appMsgQueue, sem, (uint8_t *)pMsg); } return FALSE; }2. 静态调用然后构建队列// Queue object used for app messages static Queue_Struct appM static Queue_Handle appMsgQ...// 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 cc_tirtos_ccs.cmd(CCS) .配置。 栈镜像:给栈工程刘的代码空间。配置文件为:cc26xx_ble_stack.icf (IAR) andcc_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 APP_FLASH_START Meaning Start of flash / Start of App code image APP_FLASH_END End of App code image. (ICALL_STACK0_ADDR-1 ) STACK_FLASH_STAR T Start of Stack code image (ICALL_STACK0_ADDR) Stack App ICALL_STACK0_ADD R - APP_BASE - 1 ICALL_STACK0_ADD R FLASH_START FLASH_END Project App CCS Definition APP_BASE IAR Definition FLASH_STARTSTACK_FLASH_ENDEnd of Stack flash code image, including SNV.StackFLASH_SIZE RESERVED_SIZE ICALL_STACK0_ADD RFLASH_ENDCCA SectorLast sector of Flash. Contains the CCFGAppFLASH_LAST_PAGEFLASH_LAST_PAG ECCFG RegionLocation in CCA whereAppLast 86 bytes of CCALast 86 bytes of CCA Customer Configuration (CCFG) parameters are stored3.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=0x2. 调整应用工程中的预编译符号ICALL_STACK0_ADDRICALL_STACK0_ADDR=0xB0003.重新编译应用和栈工程,验证是两个工程中是否有编译错误。 修改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 IDs3.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)和cc_tirtos_ccs.cmd(CCS). 2. 栈镜像:栈的.bss和.data扇区的RAM。这个实在栈的连接配置文件中配置: cc26xx_ble_stack.icf(IAR)和cc_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=0x200043AC3. 重新编译应用和栈工程。验证两个工程是否有编译错误。 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 *pA // 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\&PROJECT&\CC26xx\&IDE&\Config 当&PROJECT&是工程(如SimpleBLEPeripheral)并且&IDE&是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 包含了一个基本的服务就是抽象各种操作系统相关的函数。由于要共享资源和 维持内部进程的通讯,应用应该使用下面的基本服务函数: 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 *)pM // 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. 7. 8. 9. if (!Queue_empty(appMsgQueue)) { sbpEvt_t *pMsg = (sbpEvt_t *)Util_dequeueMsg(appMsgQueue); if (pMsg) 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) = 500 这告诉我们在从机没有数据发送给主机的情况下,从机每隔 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 层设计为应用用来处理两个连 接了的设备之间的数据通讯。数据以特征}

我要回帖

更多关于 jenkins credentials 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信