freebsder

个性签名:人已离开,无事别找,找也找不到。

  • 2019-03-26
  • 发表了主题帖: (转)CC2640R2F BLE5.0 BLE连接参数更新过程详解

    BLE连接参数更新过程详解有关连接参数概念和作用我们在 Generic Access Profile(GAP) 一章中已经详细讲解过,这一章演示如何通过修改simple_peripheral例程实现连接参数更新,并分析程序流程。 介绍连接参数更新过程中都是主机发起的,从机只能被动的接收主机设置的参数,这个过程发生在连接的时候,当连接之后如果主机设置的参数不利于从机,从机可以发送请求,请求中包含需要的连接参数,从机请求主机重新更新连接参数,主机可以拒绝,如果主机同意修改连接参数,并且参数合理,则主机发起连接参数更新,如果参数不合理(例如主机不支持过大或者过小的连接间隔),则主机会对参数进行协商,选择最合理的参数进行设置。由于从机发起连接参数更新请求的过程包含了主机发起连接参数更新的过程。本文以simple_peripheral工程为例,演示由从机发起连接参数更新请求的过程。 硬件环境使用USB连接CC2640R2FEvaluation Board。确保跳线帽正确连接,如下图所示软件环境l  IAR Embedded workbench 7.80.3l  BLE-STACK V3.0.1l  Packet Snifferl  BTool协议栈和IAR安装以及我们工程编译参考CC2640R2BLE 开发环境搭建 编译参考 编译第一个工程(simple_peripheral),有关BTool使用请参考BTool。 有关Packet Sniffer使用请参考 Packet Sniffer 。所有工具可以在CC2640R2开发工具集介绍里获得。 运行流程1.     在simple_perpheral中可以选择使用按键和定时器的方式进行连接参数更新,这里我们使用定时器的方式进行连接参数更新,并且对定时器启动过程进行分析。在peripheral.c文件中,定义了startUpdateClock的结构体变量,startUpdateClock作为连接参数更新的定时器事件句柄。在gapRole_init()中,可以找到其初始化过程。Util_constructClock(&startUpdateClock,gapRole_clockHandler,0, 0, false, START_CONN_UPDATE_EVT);l  参数1: startUpdateClock表示定时器事件句柄。l  参数2: gapRole_clockHandler定时事件回调函数。l  参数3: 定时器周期。l  参数4: 当该参数为0,则定时器执行一次,当该参数不为0,则周期性执行定时,周期值由该值确定。l  参数5: true表示立即执行,false表示等待。l  参数6: 定时器回调参数。所以这里创建了一个周期为0,并且只执行一次的定时器,其中回调处理函数的参数是START_CONN_UPDATE_EVT事件。2.     在gapRole_processGAPMsg中,GAP_LINK_ESTABLISHED_EVENT事件的处理过程里面可以看见 //source\ti\ble5stack\profiles\roles\cc26xx\peripheral.c  GAP_LINK_ESTABLISHED_EVENT Line 1195uint16_t timeout = GAP_GetParamValue(TGAP_CONN_PAUSE_PERIPHERAL); Util_restartClock(&startUpdateClock,timeout*1000);所以当一旦连接建立,就启动该定时器,这里timeout通过GAP_GetParamValue得到。在SimpleBLEPeripheral_init函数中,通过下面代码段已经设置了DEFAULT_CONN_PAUSE_PERIPHERAL值。 //examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\src\app\simple_peripheral.cDEFAULT_CONN_PAUSE_PERIPHERAL  Line 144// Connection Pause Peripheral time value(in seconds)#define DEFAULT_CONN_PAUSE_PERIPHERAL         6GAP_SetParamValue(TGAP_CONN_PAUSE_PERIPHERAL,DEFAULT_CONN_PAUSE_PERIPHERAL);当定时器时间到达后,调用下面回调函数,并传入初始化中设置的START_CONN_UPDATE_EVT参数,可以看出,该回调函数仅发送了一个事件。 //source\ti\ble5stack\profiles\roles\cc26xx\peripheral.c  gapRole_clockHandler Line 1626void gapRole_clockHandler(UArg a0){ gapRole_setEvent(a0);}然后在gapRole_taskFxn函数中进行相应事件处理。这里调用gapRole_startConnUpdate函数进行连接参数更新。 //source\ti\ble5stack\profiles\roles\cc26xx\peripheral.c  gapRole_taskFxn Line 895if (events & START_CONN_UPDATE_EVT){// Start connection update proceduregapRole_startConnUpdate(GAPROLE_NO_ACTION,&gapRole_updateConnParams);}而在simple_peripheral.c中使用以下函数端对连接参数变量进行赋值。其中需要特别注意DEFAULT_ENABLE_UPDATE_REQUEST。需要在其 定义处修改成GAPROLE_LINK_PARAM_UPDATE_INITIATE_BOTH_PARAMS。这样双方都可以启动连接参数更新过程。 //examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\src\app\simple_peripheral.cenableUpdateRequest line 510uint8_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; 3.     上述过程理解清楚后,我们就可以在peripheral.c文件中直接修改连接参数,在更新的时候使用我们自己设置的连接参数。//examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\src\app\simple_peripheral.cDEFAULT_DESIRED_MAX_CONN_INTERVAL line 118#defineDEFAULT_DESIRED_MAX_CONN_INTERVAL     500#define DEFAULT_DESIRED_CONN_TIMEOUT          2500#define DEFAULT_DESIRED_SLAVE_LATENCY         1 下图是使用BTool建立连接之后等待6秒收到的数据。可以看出在连接的时候使用的是主机端设置的连接参数。ConnInterval=80,ConnLatency=0,ConnTimout=2000。建立连接6秒之后,启动连接参数更新,并且更新为设置的 ConnInterval=500,ConnLatency=1,ConnTimout=2500。 下面我们使用Packet Sniffer抓包分析一下流程:如图示首先定时器时间到达之后,由从机向主机(S->M)发送连接参数更新请求命令,并携带更新参数值,主机接收到之后有权利拒绝本次更新,这里主机返回0X0000(M->S),表示同意本次连接参数更新。随后主机端会根据从机的参数进行一次协商,如果从机设置的参数不满足主机端设置的最大值或最小值,则主机会设置当前支持的最大值,举个例子,如果这里我们把DEFAULT_DESIRED_CONN_TIMEOUT改成一个比较小的数值,主机端可能就不会使用我们设定的值。而使用主机端所能支持的最小值。所以最后主机发送协商之后最终使用的连接参数(M->S)。从机接收并更新后返回应答响应。 //source\ti\ble5stack\inc\l2cap.hL2CAP_CONN_PARAMS_ACCEPTED line 210#define L2CAP_CONN_PARAMS_ACCEPTED       0x0000 //!< Connection Parametersaccepted#define L2CAP_CONN_PARAMS_REJECTED       0x0001 //!< Connection Parametersrejected 下面是连接过程中,由主机直接发起的连接参数更新请求,可以看出如果是主机直接修改连接参数,只需要发送连接参数更新请求。从机返回响应之后即完成了连接参数更新。

  • 发表了主题帖: (转)CC2640R2F BLE5.0 蓝牙协议栈概述

    Overview从这部分开始详细介绍蓝牙协议栈功能和接口,协议栈工程关联实现协议栈的文件以及实现协议栈的任务,该任务也作为协议栈应用的最高优先级任务。TI采用库文件提供方式作为协议栈提供的一个主策略,尽管如此,还是需要开发者了解协议栈各个抽象的功能和相互作用。 介绍蓝牙5.0核心规范包含LE和BR/EDR两种设备类型,其中LE主要是设计为低功耗、小数据终端产品。BLE5.0核心主要包含以下功能: l  2MSym/s PLY层设计(2M Symbol Rate 物理层)。l  LE 信道选择算法#2l  LE 安全连接l  LE 数据长度扩展l  LE 隐私l  LE LCAP 面向连接的信道支持l  LE 链路层拓扑结构l  LE Pingl  从机功能扩展l  连接参数请求以上功能均在蓝牙5.0协议栈实现,并且可以选择编译。 BLE协议栈基础 蓝牙协议栈包含一个Host和Controller两个逻辑实体,这种区分从经典蓝牙的BR/EDR就存在了,各种功能独立实现,任务配置文件和应用相关都在Host的GAP、GATT抽象层。BLE 工作在无需认证的2.4G免费频段,该频段广泛应用于ISM(工业、科学、医疗)领域。通过跳频通信实现抗干扰特性,GFSK调制,采用1Mbps码元率PHY层设计,可以实现1Mbps波特率通信,BLE5.0优化的物理层设计可以实现2Mbps的PHY层。 GAPGAP工作在以上5个状态机 在Standby状态,双方设备都处于未连接状态,Advertiser尝试广播数据,Scanner接收到广播数据后尝试进行扫描请求,并且得到扫描回复。此时Scanner产生连接意图,转变成Initiator发送连接请求,成功连接后发送广播的Advertiser作为Master,进行连接请求的Initiator成为Slave. 以上状态机转变,角色扮演以及中间完成的设备发现、链路建立、链路终止均由GAP完成。 HCI以上我们讲解过蓝牙系统由Host和Controller两个逻辑实体组成,他们之间的通信、交互通过HCI标准接口完成,基于该标准接口,Host和Controller可以独立在两个MCU实现,通过Uart/SPI等外设完成通信。 L2CAP逻辑链路控制适配协议层由上层服务提供逻辑链路层访问和进行端对端的数据通信。 SM安全管理层完成配对和密钥分布,提供同连接设备各层之间的通信安全。 GATT/ATTGATT负责主从设备之间的应用数据交换。GATT作为使用的ATT的子流程的一个服务型框架。为主从设备交互数据提供Profile、Service、Characteristic等概念的抽象、管理。

  • 发表了主题帖: (转)蓝牙BLE GATT完全分析和运用

    很多人都做过蓝牙开发,很多人也能够通过仿照GATT例程的方式添加一个属性服务,但是很多人都未必能够清晰地理解BLE的属性profile,也很容易被属性Attribute和特性characteristic所混淆。本文结合BLE的服务发现协议标准和DA14580平台、CC2541平台的应用实践来深入分析GATT,让大家能够自如地构建一个BLE的属性数据库。 一、 BLE GATT(Generic Attribute Profile)规范1.GATT定义       GATT是低功耗蓝牙属性应用规范,应用于主机和从设备之间的数据传输。其与GAP并列为BLE两大profile。Attribute是属性的意思。何为属性?在各蓝牙单芯片平台的SDK实际使用中,属性是指一条带有标签的、可以被寻址的数据。在蓝牙实际的规范中,寻址即用handle句柄来表示。每个属性都对应一个唯一的handle。 2.对属性协议需求的思考       蓝牙是无线通信,BLE利用属性协议进行传输,其如此重要,如果我们不理解其需求,那么我们也很难从真正去理解其规范。尽管在实际的蓝牙单芯片SDK中很容易通过模仿的方法进行应用,但是如果想深入地理解其为什么要设计呢?      2.1 连接的参数是一个设备的固有参数,一般会作为一个服务来提供,如GAP服务;而假设这个设备是一个温度采集器,那么这个温度采集明显跟设备的参数不属于一类,因此可以再作为一个服务。所以属性协议应该支持多个服务。如何来区分这些不同的服务?这即对应蓝牙标准规范规定的UUID。我们可以认为不同的UUID对应不同的确定的服务。      2.2 连接的参数可以有多个,如Connection Interval、Slave Latency等等,我们如何区分这是一个服务,又如何区分服务包含了这些特性参数。我们可以认为一个服务包含了多个特性(参数)。在蓝牙标准里面,同样是用不同的UUID来区分服务类型、特性类型等等。       2.3对于每个特性characteristic,要让对方获取这个特性,就必须要分别告诉对方这个特性的长度是多少,值是多少,而不能只给数值。除此之外,特性还可能有描述值(说明特性名称或者作用等)、特性单位等(国际单位,如米是公里/每小时还是米/秒)。后面这两个是非必选的。       2.4属性还应该有一个访问控制,如可读可写还是读写、或者是通知notify/indicate等等,这是数据通信必须具有的权限控制,不管是服务还是特性,它都具有访问控制属性。 3.属性和属性类型       属性由属性句柄、属性类型、属性值组成。如下图:       3.1属性句柄在实际的运用中可以认为是属性在属性数组中的下标。我们都知道在实际的编程中,下标并不需要专门存储,而只是通过元素的结构体来进行索引即可。因此可以认为属性句柄是一个无形的东西,它只能被所在的设备程序所认识,而不能用于无线传输。       3.2属性类型是真实存在的,其和属性值都会被实际存储。属性类型是由蓝牙标准组织所规范,其一般通过128位的UUID来表征一个具体的属性。由于BLE的GATT可以认为是蓝牙标准规范的精简版,所以BLE被允许只传输前面2字节(16位)的UUID,所有的BLE的UUID的基数都是一样的,如下,只有前面两字节不同。利用2字节(16位)也可以定义65536种属性了。事实上,蓝牙标准组织对这些UUID进行了分类。如下:属性类型即是0x2800~0x28ff,在实际的应用中,属性类型主要包括:我们主要使用服务和特性定义两种,其他两个很少用到。       3.3蓝牙标准不仅通过UUID来进行属性分类,而且还用UUID来确定各种具体的服务和特性。所以我们会看到UUID可能会出现在属性的属性类型和属性值两个地方。       3.4蓝牙标准组织规定两个ATT_DECL_PRIMARY_SERVICE服务之间的特性都隶属于第一个服务。这样可以理解在蓝牙服务发现协议中先通过UUID找到目标服务,然后通过ATT_DECL_PRIMARY_SERVICE这个属性类型找到下一个服务,接着即可以在这两个服务中进行特性的遍历,遍历的结果即是目标服务的所有特性。 4.属性值       属性值的长度可以最长到512字节,但对于某些属性,其长度是固定的。对于蓝牙标准里面规定的UUID所对应的属性(包括服务、特性定义、特性值、特性描述等等),服务、特性定义的长度是确定的,而特性值则是不固定长度的。所以,对于不同的属性,其属性值是不一样的。也即对于以上五类(通用服务、单位、属性类型、特性描述和区分特性类型)等属性,其属性值的规范是不一样,具体到不同的特性类型,其属性值也是不同的。       4.1通用服务类通过唯一的UUID(0x1800~0x26ff)来标识一种明确的服务。好比,0x180f代表电池电量服务。       4.2计量单位类通过唯一的UUID来标识一种单位。       4.3区分属性类型类通过唯一的UUID来标识该属性是首要服务定义、次要服务、包含服务还是特性定义等。其好比程序中的变量的类型,是整型、字节型、还是确定的结构体。       4.4特性描述类除了描述特性的名称、作用之外,还有一个非常重要的配置作用。例如如果提供的特性服务需要主动告知对方,那么对方就必须在连接时进行订阅配置。这样在该特性的数据值发生变更时能够主动地进行notify或者indicate。       4.5区分特性类型用于用户定义不同的特性,用于区分该设备里面所有的特性。 5. 特性       把特性理解为一个程序中的一个变量是最好理解的。变量有变量类型和值,变量类型有int整型、字节型等等(其实就是变量的存储长度),值即具体的数值。相应地,而特性则有值和存储值的长度的概念。如同变量的声明和定义,特性characteristic也有声明和定义(赋值)的概念。       一般地,在蓝牙标准里面,特性一般包括三个要素:声明、数值和描述。前两者都是必须的。作为通信交互,一个特性必须要告诉对方声明(存储长度和访问控制)、定义(具体赋值)。在某些特性(如notify或者indicate)里面,特性还需要告知对方附加的配置属性(提供订阅等)。特性声明必须作为服务属性之后的第一条属性,而数值必须紧随其后。      5.1 特性声明性质为一个8位字段,指示访问控制权限,包括读、写、notify或者indicate等。对于特性声明而言,其一般是只读的(这里只针对声明这条属性本身,而不是针对对应的特性数值)。数值句柄即用于直接寻址接下来的特性数值。其对于通信的对方是很有好处的,因为对方只需要记录该句柄即可在后续的访问中直接寻址,否则每次通信都要遍历。在实际的编程应用中,我们往往在初始化时填入0,代表由底层逻辑来自动更新该handle。而属性UUID和接下来的特性数值属性的区分特性类型值是一致的。       5.2特性数值特性数值也是一个属性,其属性类型填入特性声明的属性UUID。属性值要填入特性数值的访问权限、长度和数值。       5.3特性描述     其可以是字符串表示的特性名称,或者是notify/indicate要求的配置等等。 二、对属性协议重要的理解原则       1.无论是服务还是特性,它们都是一条条属性;特性的各个要素也是一条条属性。只不过,不同的服务是独立的;而一个服务如果有多个特性,那么不同的特性也是独立的;一个特性包含的多条属性则是关联的。       2.对于蓝牙通信来说,其都是通过一个个不同的UUID来标识区分不同的服务,区分不同的特性,甚至服务/特性之间的类别。       3.对于各个蓝牙单芯片SDK平台,其上层应用对于属性协议的支持并不一致,它们只需要保证底层的蓝牙数据格式一致即可。 三、属性协议范例说明       例如一个电池服务包括一个特性(电池电量),那么其至少包括以下属性:首要服务定义(电池服务)、当前电量的特性定义(定义值长度)、当前电量的特性值。如果希望电池电量在低于某个水平时主动告知对方,那么这个电量特性值不仅应该是可读的,还应该是能够notify的。由于有主动告知,因此该特性还需要包括配置要素,用于对方来订阅。       1.对于电池服务这个属性,其属性类型是ATT_DECL_PRIMARY_SERVICE(0x2800),属性值是访问可读和蓝牙标准组织规定的0x180f(位于0x1800到0x26ff之间);       2.电池当前电量的特性定义这个属性,其属性类型是ATT_DECL_CHARACTERISTIC(0x2803),属性值是可读、特性值句柄和特性值的UUID(0x2A19)。       3.对于当前电量的特性值这个属性,其属性类型是0x2A19(0x2A00~0x7fff之间,区分特性类型),其用于区分多种不同的特性(如一个温度采集器可能要采集多个温度,这里就要用户通过不同的UUID来区分不同的特性了),属性值即访问控制(可读/indicate)、长度(1字节)、数值。       4.配置属性,用于notify的订阅配置。其属性类型是0x2902,属性值是可读/可写(要能写入订阅方的handle)、长度(2个字节)、handle值。     根据以上分析,我们来重构这个蓝牙的数据底层数据库。四、DA14580平台SDK属性结构定义1.单个属性定义对于不同的属性,其length长度由value的类型来决定。例如服务的属性值对应的结构就是uint16_t;而特性声明的属性值对应的结构就是: 2.电池电量服务定义五、CC254X平台SDK属性结构定义1.单个属性定义同样的,属性值pValue的数据结构由其对应的UUID来决定。例如服务属性的pValue对应的结构是gattAttrType_t(包括长度2和具体的服务UUID)。而特性声明的pValue对应的结构是一个字节的访问权限控制。特性数值的pValue对应的结构则是直接的变量定义,其可能是一个字节或者是字节数组,其长度由底层通过判断数组来决定。2.电池电量服务定义 六、参考文献1.   BLE标准core_V4.0,英文规格书2.   低功耗蓝牙开发权威指南,蓝牙标准起草人撰写,中译本。如需要这两本电子书请关注微信公众号:嵌入式企鹅圈之后发消息获取。​做蓝牙开发的童鞋们记得点赞啊,网上几乎没有类似这样对底层协议结构进行分析的文章,最多就是写写属性、服务、特性之类的定义,看了过眼就忘。这篇文章应该可以好好收藏,不记得的时候就翻出来看看:-)

  • 发表了主题帖: (转)CC2640R2F BLE5.0 蓝牙协议栈通用属性配置文件(GATT)

    本帖最后由 freebsder 于 2019-3-26 12:00 编辑 通用属性配置文件(GATT)正如GAP层负责连接相关的功能,GATT主要是负责在两个已经连接的设备交互数据,GAP层把BLE设备区分为主机Master(Central)和从机Slave(Perpherial),在GATT层则区分为Server和Client。客户端读取和写入存储在服务端的特征值(Characteristics )。 Server该设备包含由GATT客户端读取或写入的characteristic。 Client从GATT服务器读取或写入数据的蓝牙设备。 注意:对于GATT分层的Server/Client角色和GAP分层的Master(Central)/Slave(Perpherial)并没有直接关系。如上图,手机作为Central/Client,CC2640R2开发板作为Peripheral/Server。 GATT的Profile、Service、Characteristics、Attributes对于GATT层的Profile、Service、Characteristics、Attributes概念,我们一定要深入理解,因为一旦建立连接后,不管是嵌入式端还是应用端进行数据交互的都是这些概念。为了帮助理解,我们抽象以下包含关系: 一个或者多个Characteristic组成一个Service,一个多个Service组成Profile,Characteristic又由多个Attributes组成,每个Attribute由包含 Handle、Type、Permissions三个属性。以下我们着重理解Characteristic,也是我们数据交互的最终实体,每个特征包含以下4个Attributes。 l  Characteristic Value(特征值)用于characteristic的值 l  Characteristic Declaration(特征声明)存储特征值的属性,位置和类型的描述符 l  Client Characteristic Configuration(客户端特征配置)允许GATT服务器配置要通知的特性(异步发送消息)或指示的配置(与确认异步发送消息) l  Characteristic User Description(特征用户描述)描述特征的ASCII字符串这些属性存储在属性表中的GATT服务器中。除了该值之外,以下属性与每个属性相关联。 以上的每个Attributes 又由以下元素组成。 l  Handle(句柄)表中属性的索引(每个属性都有一个唯一的句柄) l  Type(类型)指示属性数据表示什么(称为UUID [通用唯一标识符],其中一些是蓝牙SIG定义的,一些是自定义的) l  Permissions(权限)强制GATT客户端设备如何以及如何访问属性的值 GATT客户端抽象层下图所示,在GATT客户端应用程序大部分是直接使用GATT的API(少部分直接使用ATT层API),没有profile文件,因为GATT客户是得到数据,不需要建立属性表和配置文件。 GATT服务端抽象层如图所示,在GATT服务端,GATT层的大部分功能由独立的profiles处理,然后可以看见perfiles又使用GAttservApp模块(一个可配置的模块,用于储存和管理属性表,详见BLE Stack API Reference)处理。 所以在建立GATT服务端的时候首先需要配置profiles文件,profiles文件调用GattServAppApp模块并使用其API与GATT层接口。在这种情况下应用程序不需要直接调用GATT层API,应用程序和Profiles文件接口。 GATT Services 和 Profile在概述部分讲过,GATT service是characteristic的集合,多个service可以组合在一起形成一个profile,许多profile仅实现一个service,所以profile和service可以互换。 在simple_peripheral示例应用程序项目中定义了四个GATT服务。 l  GAP GATT服务(GGS)此服务包含设备和访问信息,例如设备名称,供应商标识和产品标识。为此服务定义了以下特征: 设备名称表现(Appearance)外围首选连接参数 l  通用属性服务该服务包含有关GATT服务器的信息,是蓝牙低功耗协议栈的一部分,每个GATT服务器设备都需要根据蓝牙5.0版本核心规范。 l  设备信息服务此服务公开了有关设备的信息,如硬件,软件版本,固件版本,规范信息,合规性信息和制造商名称。设备信息服务是蓝牙低功耗协议栈的一部分,由应用程序配置。 l  simple_gatt_profile服务此服务是用于测试和演示的示例配置文件。完整的源代码在simple_gatt_profile.c和simple_gatt_profile.h文件中提供。          // Initialize GATT attributes         GGS_AddService(GATT_ALL_SERVICES);           // GAP GATT Service        GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT Service         DevInfo_AddService();                        // Device InformationService         SimpleProfile_AddService(GATT_ALL_SERVICES);// Simple GATT Profile下图显示了simple_peripheral项目中的属性表,这些属性的设置都是通过上面4个服务函数。可以逐一打开查看修改。 使用BTool获取的简单GATT配置文件特性表。红色表示Profile声明,黄色表示字符声明,White表示与特定声明相关的属性。 simple_gatt_profile包含以下特性: l  SIMPLEPROFILE_CHAR1可以从GATT客户端设备读取或写入的1字节值l  SIMPLEPROFILE_CHAR2可以从GATT客户端设备读取但不能写入的1字节值l  SIMPLEPROFILE_CHAR3可以从GATT客户端设备写入但不能读取的1字节值l  SIMPLEPROFILE_CHAR4不能从GATT客户端设备直接读取或写入的1字节值(该值是通知属性)l  SIMPLEPROFILE_CHAR5可从GATT客户端设备读取(但不写入)的5字节值0x001C是simple_gatt_profile服务声明。此声明的UUID为0x2800(蓝牙定义 GATT_PRIMARY_SERVICE_UUID)。该声明的值是simple_gatt_profile(自定义)的UUID。 0x001D是SimpleProfileChar1特征声明。该声明可以被认为是指向SimpleProfileChar1值的指针。该声明的UUID为0x2803(蓝牙定义GATT_CHARACTER_UUID)。下面解释characteristic声明值的含义(MSB到LSB): l  字节0是蓝牙核心规范版本5.0中定义的SimpleProfileChar1的属性(以下是某些相关属性)。0x02:允许读取特征值0x04:允许写入特征值(无响应)0x08:允许写入特征值(带响应)0x10:允许通知特征值(无确认)0x20:允许通知特征值(带确认)characteristic1 0x0A的意义是该特性可读可写(0x02|0x08) l  字节1-2:SimpleProfileChar1的值的句柄(句柄0x001E) l  字节3-4:SimpleProfileChar1值的UUID(自定义0xFFF1) l  0x001E是SimpleProfileChar1特征值该值的UUID为0xFFF1(自定义)。该值是characteristic的实际有效载荷数据。如其SimpleProfileChar1声明(句柄0x01D)所示,该值是可读写的。 l  0x001F是SimpleProfileChar1特征用户描述该描述的UUID为0x2901(蓝牙定义)。该描述的值是描述特征的用户可读字符串。 l  0x0020 - 0x002C剩下的四个特征描述具有和simpleProfileChar1相同结构的属性。唯一不同的属性是处理0x0028时,描述如下。 0x0028是SimpleProfileChar4客户端特征配置。此配置的UUID为0x2902(蓝牙定义)。通过写入此属性,GATT服务器可以将SimpleProfileChar4配置为通知(写入0x0001)或指示(写入0x0002)。将0x0000写入此属性将禁用通知和指示。 GATT安全如GATT 服务端抽象所述,GATT服务器可以为每个特性独立定义权限。服务器可能允许任何客户端访问某些characteristic,同时将访问其他characteristic仅限于认证或授权的客户端。这些权限通常被定义为更高级别的配置文件规范的一部分。对于自定义配置文件,用户可以选择他们认为合适的权限。有关GATT安全性的更多信息,请参阅蓝牙核心规范版本5.0的安全注意事项部分([第3卷,第G部分,第8部分])。 认证(Authentication)在客户端通过认证配对方法之前,无法访问需要身份验证的特性。此验证在堆栈内执行,无需应用程序处理。唯一的要求是使GATT服务端正确注册该特性。例如,simple_gatt_profile的characteristic5设置需要认证的读取。 //characteristic 5 {   {  ATT_BT_UUID_SIZE , simpleProfilechar5UUID  },  GATT_PERMIT_AUTHEN_READ ,   0 ,  simpleProfileChar5 },当未经身份验证的客户端尝试读取此值时,GATT服务器将自动拒绝它,而不调用simpleProfile_ReadAttrCB()。只有当认证成功之后,读写的请求才会转发到profiles的读写回调,请参阅一下代码,该代码在simple_gatt_profile.c中simpleProfile_ReadAttrCB()函数里面。 case SIMPLEPROFILE_CHAR1_UUID: case SIMPLEPROFILE_CHAR2_UUID: case SIMPLEPROFILE_CHAR4_UUID:   *pLen = 1;   pValue[0] = *pAttr->pValue;   break; case SIMPLEPROFILE_CHAR5_UUID:   *pLen = SIMPLEPROFILE_CHAR5_LEN;   VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR5_LEN );   break; default:   // Should never get here! (characteristics 3 和 4没有读权限)   *pLen = 0;   status = ATT_ERR_ATTR_NOT_FOUND;   break;授权(Authorization)授权是发生在认证之后对一些文档的访问权限更改删除,需要授权。授权是BLE已经实现的一个安全层。由于应用程序需要定义自己的授权要求,所以协议栈将这些特性的读/写请求转发到配置文件的应用程序层。 要从profile文件注册GATT服务器的授权信息,它必须使用堆栈定义一个授权回调。如下面伪代码(例程中没有使用授权)。 1.     注册授权回调       CONST  gattServiceCBs_t  simpleProfileCBs  = {           simpleProfile_ReadAttrCB ,      //读回调函数指针          simpleProfile_WriteAttrCB ,     //写回调函数指针          simpleProfile_authorizationCB   //授权回调函数指针       };2.     实现授权回调代码       static  bStatus_t simpleProfile_authorizationCB ( uint16  connHandle ,gattAttribute_t  * pAttr , uint8  opcode  )       {          //这只是一个示例实现,正常的用例将需要          更复杂的逻辑来确定设备是否被授权           if (clientIsAuthorized)             return SUCCESS ;           else             return ATT_ERR_INSUFFICIENT_AUTHOR ;        }授权回调在协议栈上下文中执行,因此在这个函数中不应该执行太复杂的处理。具体实现由开发人员决定;上述回调应该被视为一个shell。如果客户端有权访问该特征返回SUCCESS,如果没有返回ATT_ERR_INSUFFICIENT_AUTHOR,授权需要事先通过连接进行身份验证,否则ATT_ERR_INSUFFICIENT_AUTHEN将作为错误响应发送。 如果需要授权的特性并注册了授权回调,但没有定义应用级授权回调,那么协议栈将返回ATT_ERR_UNLIKELY。因为这个错误并不明确,所以TI建议使用授权回调。 直接使用GATT层上面客户端抽象说过,应用程序也可以直接使用GATT层API,本节就介绍如何在应用程序中使用GATT层,GATT层的功能在库中实现,但头文件功能可在gatt.h中找到。BLE Stack API Reference中有完整的API参考。作为GATT客户端(在simple_central项目中)使用GATT层的一般过程如下: 图中可以看出,GATT客户端发送命令之后,通过ICALL给协议栈发送ATT命令,然后协议栈处理之后返回响应,再由ICALL以ATT事件通知应用程序,应用程序得到ATT事件之后异步处理。注意:除了收到对自己的命令的响应外,GATT客户端还可以从GATT服务器接收异步数据作为指示或通知。使用GATT_RegisterForInd(selfEntity)注册接收这些ATT通知和指示。这些通知和指示也作为ATT事件(ATT_HANDLE_VALUE_NOTI& ATT_HANDLE_VALUE_INDG)发送到应用程序。这些事件必须按照GATT Services和Profile中的说明进行处理。 GAP GATT 服务(GGS)在前面GATT服务和简介中说过,GGS服务包含设备和访问信息,例如设备名称,Appearance,外围首选连接参数。GGS的目的是在设备发现和连接启动过程中进行辅助。有关GGS的更多信息,请参阅蓝牙核心规范版本5.0的“GAT service”和“Characteristics for GATTServer”部分([Vol 3],C部分,第12节)。 1.     包含标题#include “gapgattserver.h”2.     初始化GGS参数// GAP GATT Attributes static uint8_t  attDeviceName [GAP_DEVICE_NAME_LEN ]  =  “This is a text” ;    GGS_SetParameter (GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN ,attDeviceName );3.     使用GGS初始化应用程序回调(可选)。当GGS中的任何特征发生变化时,都会通知应用程序。GGS_RegisterAppCB (&appGGSCBs );4.     将GGS添加到GATT服务器。bStatus_t GGS_AddService (GATT_ALL_SERVICES );经过上面4个步骤的配置,就成功设置了GGS的参数,在central设备连接外围设备的时候就能获取这些参数。

  • 2019-03-13
  • 回复了主题帖: C/C++回忆录

    居然坐地铁有座看书。。。

  • 2019-03-05
  • 回复了主题帖: 诸如#35: #error directive“Not supported.”是什么原因,请大神指点一下

    没有设置GPIO_REG_LIST宏或者设置的值不对

  • 2019-03-01
  • 回复了主题帖: 格物致知04——有限角位移不是矢量

    maychang 发表于 2019-3-1 20:13 一个集合,其中的元素和运算满足你在2楼贴出后来我又在115楼重复贴一次的那些条件,那么这个集合是向量空 ...
    元素和向量都混淆着,整篇论证完全就是关公打秦琼的张冠李戴,没有数学背景就别学人开口向量闭口希尔伯特,你们说是啥就是啥吧,你们赢了。

  • 回复了主题帖: 格物致知04——有限角位移不是矢量

    本帖最后由 freebsder 于 2019-3-1 19:24 编辑
    maychang 发表于 2019-3-1 10:59 把2楼图片重新贴在这里,免得翻来翻去的麻烦。 图片中F没有问题,是个数域。通常我们考虑的是实数域R ...
    你错了。 你们一直混淆了元素和向量。 向量和向量空间的结构,就是最纯粹的代数结构,里面根本不涉及具体元素是函数还是实数还是物理量的角转动。这也是楼上的楼上不懂空间的意义,以为是函数了不起了。 我们通常认知的向量形如x=(r1,r2,r3.....),这只是一种写法和表达,不准确理解说可以表达有序的组成关系。所有x构成所谓的空间X,我们不去追究这个是不是真的空间,姑且认为他是空间。空间是所有x=X,也就是定义中的V,元素是x,组成x的r来自空间定义中出现的F,这里就有几个结构出现了,x,所有x=X,所有r=F。 另一方面,如果再把x作为整体,形如y=(x1,x2,x3...),如果所有的y可以构成向量空间,那也是向量空间,这里的结构是元素y,所有的y=Y,所有的x=X,所有来自X的x。这里的V就是Y,F就是X。 你们一直混淆了x作为向量还是元素。一会你觉得x是元素,它构成y,一会你混淆了x是向量,觉得它是它行如(r1,r2,r3....)。 这不是问题,问题是,第一种,x如果作为元素,那所有的x就是Y的空间定义中的F,X需要满足域的定义,所以必须在X其中定义+*两个运算,否则x不能成为F,谈何空间元素?如果第二种,你需要要一个F也就是所有的r,在r中同样需要+*。 不可能的情况是你在F中定义两种运算满足域,到了空间你换根本无关的两个运算,这就是关公打秦琼。 向量空间的这种纯粹的代数结构,你可以再构成空间,但是分解下去你需要递归满足F和V,不说不代表没有,默认不代表不存在,F,V,+*不用管你元素是实数还是函数,运算是加法还是移动,关键是结构和运算的形式满足。 在域F中,运算符号+和*首先需要满足在F中的构造,然后构造出域F,否则,如果是两个完全无关的对象,定义中的rX怎么定义r和X的组成元素的*运算?突然就冒出来了?如果V和F无关,你觉得数学这种多一个字都嫌多的语言,为啥要把F加到定义中去?空间的定义包含了F这个前提,也就是构成空间元素的元素。所以上面两楼,我说过如果构成空间的元素可以分解和不可以分解两种情况。 随你们放在那种层面,上面我说过,也就是分两种情况。你要当元素x,那就证明自己在移动旋转等运算上构成一个域,这才能构成下一步的空间元素的坐标的基础。如果你要当向量X,那你证明的移动和旋转的操作在组成X的F中能不能满足就开撸,这就真是非欧朋友所谓的臆想了。 这就是向量,就是一个纯粹的结构,根本无关角位移还是函数,无关加还是移动旋转。

  • 2019-02-28
  • 回复了主题帖: 工程师你不站在工程的角度不站在产品的角度做开发,你咋对得起“工程”二字...

    是的,说的是

  • 2019-02-26
  • 回复了主题帖: 格物致知04——有限角位移不是矢量

    本帖最后由 freebsder 于 2019-2-26 14:59 编辑
    cruelfox 发表于 2019-2-26 13:39 常用的 n 维实向量空间 R^n , 复向量空间 C^n  (姑且这么表示吧,你懂的)只是特例。并不等于说向量就都 ...
    有序 是指的 位置的有序,而不是域元素的有序。我原话是“有序排列”而不是你说的“有序元素排列”,谢谢。

  • 回复了主题帖: 格物致知04——有限角位移不是矢量

    本帖最后由 freebsder 于 2019-2-26 09:26 编辑 顺着你们,分两种情况说吧。 一种是角位移是元素,一种是角位移是不是向量。 为什么分两种情况,理由如下:角位移是元素,这个很明确的提出来了。一楼证明试图有限角位移不是向量,同时也说明了无穷小角位移是向量,所以这里是把角位移纳入向量的讨论范畴(不管是有限还是无穷)。 针对第一种,角位移是元素的情况。姑且认为角位移是一个点,组成点集,不可再分解。如果还能分解,那就是下面的第二种情况,由某些东西构成了角位移这个元素。 角位移的集合,是一个点集,这个点即是某个角位移元素的抽象,写作a吧。形式上,这里数学操作是可行的,形式上和大家最熟悉的数的点集是一样的,数也是某些对象的抽象,这个写作c吧。 因为是讨论集合中的元素,所以,我们只能证明这些元素首先是否能构成域。为什么是域而不是群不是环,是因为你们的目标是论证向量,要讨论是否是向量,那么必然要引入向量空间V这个概念,那个定义中的F必须是域,也就是角位移这个元素属于F。如果这个共识都不能达成,那我真不知道这篇帖子你们试图论证个啥。。。 那么来了。在a的集合中,我们定义两个运算+,*,在c中我们也定义两个运算+,*。这里+,*都不是我们意识上的加法和乘法。无所谓吧,现在代换,数学上可以的,代换,把a集合上的+代换成某个符合 a + a' = a' + a 的运算,至于+是什么,我们不追究,只是假设这个 + 存在。同样的,把 c 集合上的+代换成我们通常意义上的加法,所以 c + c' = c' + c。同样的,把a集合上的*代换成某个操作,这个操作 a * a' != a' * a,把 c 集合上的*代换成 乘方运算吧,乘方运算下 c * c' != c' * c。 那么你们的问题来了,你们用相同的形式,证明了c不是域。是这样吗?不是的,大家都知道c是实数域,只需要把 * 这个操作代换成我们熟悉的乘法。这个形式代换用以说明:你们不可以用一个运算的不满足来论证a是不是域(进而能不能构成向量),你们需要的是证明所有的运算都不满足,或者找不到这样的运算满足条件,或无法定义这样的运算满足条件,从而a的集合才不构成域(进而不构成向量)。所以,角位移集合元素运算中的*,只能说明 角位移在*运算下不构成域,很明显,这个*也就是你们称之为“合成”的不满足交换的那一个运算(一个)。形式上一样的,数集在乘方操作下不构成实数域。形式和代换这两种基本数学思维,我觉得不需要再争论了。 很难想象口谈非欧的朋友,试图用这样的背景来告诉我们这就是非欧。所以我一直追问38楼口谈中的所谓非欧想是个啥。你们的整个论证形式上就是用乘方运算来证明了实数不构成域,进而不是(不构成)向量。 这就是我说的1楼论证过程的问题,所以38楼的问题也在这里。 好,第二种情况。角位移纳入向量的范畴。 既然要讨论向量必然涉及到域F和集合V,F是构成V的域。F和V不是乱组合乱构成的,V的定义中构成元素来自F。这也就是我们常见的空间写法R3(右上标), R4(右上标)的意义:告诉我们这个集合里面的元素是由3个、4个同样都来自实数域R的有序排列组成的对象。V的+和*(数乘)运算需要F里面首先满足F域的运算,这也就是为什么在V的定义中要有+和*:ax引入F中的*,x+y引入F中的+(x,y由F构成),而F作为域,必然是封闭完备的。F和V如果组成向量空间,不仅需要满足2楼贴图的性质,也需要有F,+,*的条件。F和V构成空间后,当然可以定义其他运算,这些运算满不满足V的定义,已经不重要了。 首先矛盾的是你们一直强调角位移是元素,如上第一种情况所说,你们把它当F,论证过程有问题。 既然讨论第二种情况,那么显然,角位移在你们的讨论中(同样“无穷小角位移是向量”这个表达式中),它是在V这个集合里面。好了,那么你们论证的F是什么?你们的角位移V是如何用F构成的,你们直接就在V上使用的+在F上有定义吗?F在+这个运算上构成域吗? 如果构成角位移V的F本身都不能构成域,那么你用F写出来的角位移本就不是向量,先不说你直接把角位移当V来论证是不是向量,就说构造出一个本就不是向量的东西,然后再用向量的逻辑通篇论证一遍,这就是我前面说的你们是想用向量的理论来证明{和尚,美女,水,猴子,爱恨}不是向量?这就是我说的关公打秦琼,因为这东西是你自己构造的,关键词是自己和构造。 所以,你们只能论证如果F,+,* 构成域,并且F构成的V在+运算上不可交换。 这就是我说的1楼论证过程的问题。

  • 2019-02-25
  • 回复了主题帖: 关于atmel studio 7生成.lib库文件

    zhuzd 发表于 2019-2-25 11:00 我刚才试了一下,你说的是不是新建工程的时候选择那个 静态库  那个?但是有一个疑问,我新建库工程编译 ...
    引入器件的头。就算和器件无关,引入头也不会造成什么问题。

  • 回复了主题帖: 关于atmel studio 7生成.lib库文件

    zhuzd 发表于 2019-2-25 08:44 不是  我现在是要导出一个lib库给别人用
    新建一个库工程,重新放入文件编译。

  • 2019-02-24
  • 回复了主题帖: 【 ST NUCLEO-H743ZI测评】一个关于TCP的故事之Server/Client建立

    bigbat 发表于 2019-2-24 18:53 楼主的代码是哪个版本,我去到网站https://www.oryx-embedded.com/download.htmlDownload Latest Version 1 ...
    网络问题,你换个网络,或者手机网络试试,无权限我也遇到过。

  • 回复了主题帖: 【 ST NUCLEO-H743ZI测评】一个关于TCP的故事之Server/Client建立

    强大的cyclonetcp秒杀lwip

  • 回复了主题帖: 格物致知04——有限角位移不是矢量

    我很难想象连向量空间的定义都要扯半天的知识结构能完备到哪里去。

  • 回复了主题帖: 格物致知04——有限角位移不是矢量

    cruelfox 发表于 2019-2-24 16:16 你一直没把 V 的元素是什么搞清楚。 就自己臆想半天“位移运算”,“旋转运算”,你这不是数学思维。
    不是我臆想的运算,而是你知识结构局限而不知道罢了。

  • 回复了主题帖: 格物致知04——有限角位移不是矢量

    cruelfox 发表于 2019-2-24 16:16 你一直没把 V 的元素是什么搞清楚。 就自己臆想半天“位移运算”,“旋转运算”,你这不是数学思维。
    非欧那么多,我问你精确的指什么,你又不开腔,呵呵。你搞懂了就说,别装深度。

  • 回复了主题帖: 格物致知04——有限角位移不是矢量

    cruelfox 发表于 2019-2-24 16:16 你一直没把 V 的元素是什么搞清楚。 就自己臆想半天“位移运算”,“旋转运算”,你这不是数学思维。
    来,我是臆想,你从昨天到今天都没说出你要定义的V是什么,要喷就拿出你的说法,别装高深。不要弄物理,我不会物理,要打就在一个平面打,别一会物理一会数学。我就不信你家物理把旋转这种操作就抹掉了。

  • 回复了主题帖: 格物致知04——有限角位移不是矢量

    maychang 发表于 2019-2-24 16:14 你自己定义的空间,“我定义+是两个元素的一个运算,先进行一次转动,再进行另一次转动” 称之为 + 吧, ...
    你知道你的逻辑错在那里吗? 话糙理不糙,勿介意: 你要证明某人是个男人,好的,你应该描述所有男人的性质,然后来证明这人是不是男人,符不符合这些性质。而你和38楼,妄图定义一个人,然后你说你论证的对象没有子宫,所以不是男人。 这就是你们整个逻辑的错误,用一个扩大外延来论证内涵。错。框就是框卡尺就是卡尺,你用一个大框去套小框,然后描述小框不符合大框的性质,所以小框不是小框。。。我无法再往多的解释了。

最近访客

< 1/6 >

统计信息

已有428人来访过

  • 芯币:3017
  • 好友:8
  • 主题:75
  • 回复:1466
  • 课时:--
  • 资源:1

留言

你需要登录后才可以留言 登录 | 注册


yezi2989 2018-2-26
您好!看您在论坛上分享了很多主题经验,我们现在有一个投稿活动有兴趣了解下不?
查看全部