Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1379747
  • 博文数量: 860
  • 博客积分: 425
  • 博客等级: 下士
  • 技术积分: 1464
  • 用 户 组: 普通用户
  • 注册时间: 2011-08-20 19:57
个人简介

对技术执着

文章分类

全部博文(860)

文章存档

2019年(16)

2018年(12)

2015年(732)

2013年(85)

2012年(15)

我的朋友

分类: 嵌入式

2019-01-10 10:22:19

转载自:https://blog.csdn.net/qq_21842557/article/details/50771077

一、开篇

    本篇主要介绍一下关于BLE开发过程中必须了解的两个协议:GAP(通用访问协议)、GATT(通用属性协议)。两个协议都隶属于Host层,直接关系到应用层开发,与BLE开发人员的关系比较密切,其分别负责连接前数据广播和连接后的数据传输。



三、试验平台

Software Version:BLE_STACK_CC26XX_2.1.0

Hardware Version:CC2640/CC2650

IDE:IAR 7.40

四、GAP

    1、蓝牙低能耗技术“完成”一次连接(即扫描其它设备、建立链路、发送数据、认证和适当地结束)只需3ms。而标准蓝牙技术完成相同的连接周期需要数百毫秒。

    GAP层有4种不同类型的广播:通用的、定向的、不可连接的以及可发现的。

    设备每次广播时,会在3个广播信道上发送相同的报文。这些报文被称为一个广播事件。除了定向报文以外,其他广播事件均可以选择20ms - 10.28s不等的间隔。通常,一个广播中的设备会每一秒广播一次。广播事件之间的时间称为广播间隔。主机可以控制该间隔。但是,设备周期性的发送广播会有一个问题:由于设备间的时钟会不同程度的漂移,两个设备可能在很长一段时间同时广播而造成千扰。为防止选一情况的发生,在上一次广播事件发生后加入随机延时。它们发送下一个广播事件时也很可能不再冲突。

    通用广播:通用广播是用途最广的广播方式。进行通用广播的设备能够被扫描设备扫描到,或者在接收到连接请求时作为从设备进入一个连接。通用广播可以在没有连接的情况下发出,换句话说,没有主从设备之分。

    定向广播:有时候,设备间需要快速建立连接。如果从设备想这么做,就需要进行广播。定向广播事件就是为了尽可能快的建立连接。这种报文包含两个地址:广播者的地址和发起者的地址。发起设备收到发绐自己的定向广播报文后,可以立即发送连接请求作为回应。

    不可连接广播:不想被连接的设备使用不可连接广播事件。这种广播的典型应用包括设备只想广播数据,而不想被扫描或者连接。速也是唯一可用于只有发射机而没有接收机设备的广播类型。不可连接广播设备不会进入连接态,因此,它只能根据主机的要求在广播态和就绪态之间切换。

    可发现广播:最后一种广播事件是可发现广播。这种广播不能用于发起连接,但允许其他设备扫描该广播设备。这意味着该设备可以被发现,既可以广播数据,又可以响应扫描,但不能建立连接。这是一种适用于广播数据的广播形式,动态数据可以包含于广播数据之中,而静态数据可以包含于扫描响应数据之中。可发现广播不会进入连接态,而只能在停止后回到就绪态。

    如上面所述,BLE设备可以进行广播。但是,一个广播设备必须在广播中包含一些有用的数据。这意味着可以通过4种广播事件中的3种进行广播:通用广播、不可连接广播以及可发现广播。进行广播时,需要在广播报文中给数据打上标签。之所以要这么做,是因为并非所有设备都能理解所有可能的广播数据。因此,需要给广播数据打上标签并指出其长度。每个数据片段均起始于一个长度域,用以指示后面的类型及数据域的长度;接下来是类型域,接收机可根据其内容判断自己是否能够理解后面的数据。事例代码:


[plain] view plain copy
  1. // GAP - Advertisement data (max size = 31 bytes, though this is  
  2. // best kept short to conserve power while advertisting)  
  3. static uint8_t advertData[] =  
  4. {  
  5.   // Flags; this sets the device to use limited discoverable  
  6.   // mode (advertises for 30 seconds at a time) instead of general  
  7.   // discoverable mode (advertises indefinitely)  
  8.   0x02,   // length of this data  
  9.   GAP_ADTYPE_FLAGS,  
  10.   DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,  
  11.   
  12.   // service UUID, to notify central devices what services are included  
  13.   // in this peripheral  
  14.   0x03,   // length of this data  
  15.   GAP_ADTYPE_16BIT_MORE,      // some of the UUID's, but not all  
  16. #ifdef FEATURE_OAD  
  17.   LO_UINT16(OAD_SERVICE_UUID),  
  18.   HI_UINT16(OAD_SERVICE_UUID)  
  19. #else  
  20.   LO_UINT16(SIMPLEPROFILE_SERV_UUID),  
  21.   HI_UINT16(SIMPLEPROFILE_SERV_UUID)  
  22. #endif //!FEATURE_OAD  
  23. };  


五、GATT


    通用属性配置文件(GATT)在属性协议(ATT)的基础上构建,为属性协议传输和存储数据建立了一些通用操作和框架。

1)GATT定义了两个角色:服务器和客户端

    GATT的角色并不一定与特定的GAP角色有关联,但可能由更高层级的配置文件指定。GATT和ATT不是传输专用,也可以用于BR/EDR和低耗能。但是,由于GATT和ATT用作发现服务,故必须在低耗能技术中实施。GATT服务器存储通过属性协议传输的数据,并接受GATT客户端发出的属性协议请求、指令及确认。GATT服务器发送请求回复,而如果在配置时GATT服务器发生特定事件,则会向GATT客户端异步发送指示和通知。GATT还指定GATT服务器中所载的数据格式。

    属性在当经由属性协议传输时,会被格式化为相关的服务和特性。服务可能包括许多特征。特征包括单一值和许多描述特征值的描述符。

凭借经定义的服务、特征和特征描述符架构,并非配置文件特定的GATT客户端仍然可以遍历GATT服务器,并向用户显示特征值。特征描述符可用于显示特征值的描述符,从而可让用户了解该值。

2)GATT配置文件层级

    GATT配置文件规格规定了交换配置文件数据的架构。此架构定义了配置文件所用的基本元素,例如服务和特征。

该层级的最高层是配置文件(profile)。配置文件由实现用例所需的一个或多个服务组成。服务由特征或有关其它服务的引用组成。每一个特征包括一个值,还可能包括有关该值的可选信息。服务、特征以及特征的组件(即特征值和特征描述符)构成了配置文件数据,并全部存储在服务器的属性中。


    英文原版(摘自Core_V4.1 vol 1:6.5,p226):The top level of the hierarchy is a profile. A profile is composed of oneor more services necessary to fulfill a use case. A service is composed of characteristicsor references to other services. Each characteristic contains a value and maycontain optional information about the value. Theservice and characteristic and the components of the characteristic (i.e.,value and descriptors) contain the profile data and are all stored in Attributes on theserver.

0

3)服务

    服务是数据和完成设备或设备的某些部分的特定功能或特征的相关行为的集合。服务可能涉及其它主要或次要服务和/或构成该服务的特征集合。

服务分为两种类型:主要服务和次要服务。主要服务提供设备的主要功能。次要服务提供设备的辅助功能,引用自该设备至少一项主要服务。

为了令早前的客户端保持向后兼容性,服务定义的其后修订仅可增加新引用的服务或可选特征。服务定义的其后修订也不得改变该服务定义先前修订的特征。

服务可能用于一个或多个配置文件,以实现特定用例。

4)特征

    特征,连同属性和有关如何访问该值的配置信息以及有关如何显示或表述该值的信息,是用于服务的值。特征定义包含特征声明、特征属性和值。它还可能包含描述该值或允许服务器配置有关特征值的描述符。

协议栈代码实现如下:

[plain] view plain copy
  1. /*********************************************************************  
  2.  * Profile Attributes - variables  
  3.  */  
  4. // Simple Profile Service attribute  
  5. static CONST gattAttrType_t simpleProfileService = { ATT_BT_UUID_SIZE, simpleProfileServUUID };  
  6. // Simple Profile Characteristic 1 Properties  
  7. static uint8 simpleProfileChar1Props = GATT_PROP_READ | GATT_PROP_WRITE;  
  8. // Characteristic 1 Value  
  9. static uint8 simpleProfileChar1 = 0;  
  10. // Simple Profile Characteristic 1 User Description  
  11. static uint8 simpleProfileChar1UserDesp[17] = "Characteristic 1";  
  12.   
  13.   
  14. /*********************************************************************  
  15.  * Profile Attributes - Table  
  16.  */  
  17. static gattAttribute_t simpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] =   
  18. {  
  19.   // Simple Profile Service  
  20.   {   
  21.     { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */  
  22.     GATT_PERMIT_READ,                         /* permissions */  
  23.     0,                                        /* handle */  
  24.     (uint8 *)&simpleProfileService            /* pValue */  
  25.   },  
  26.     // Characteristic 1 Declaration  
  27.     {   
  28.       { ATT_BT_UUID_SIZE, characterUUID },  
  29.       GATT_PERMIT_READ,   
  30.       0,  
  31.       &simpleProfileChar1Props   
  32.     },  
  33.       // Characteristic Value 1  
  34.       {   
  35.         { ATT_BT_UUID_SIZE, simpleProfilechar1UUID },  
  36.         GATT_PERMIT_READ | GATT_PERMIT_WRITE,   
  37.         0,   
  38.         &simpleProfileChar1   
  39.       },  
  40.       // Characteristic 1 User Description  
  41.       {   
  42.         { ATT_BT_UUID_SIZE, charUserDescUUID },  
  43.         GATT_PERMIT_READ,   
  44.         0,   
  45.         simpleProfileChar1UserDesp   
  46.       },        


5)关于句柄handle 和UUID


    Handle即是地址(记住它,类比于C语言的指针操作)

    属性句柄:一台设备可以有许多的属性,例如温度传感器可能包含温度属性、设备名称属性和电池电量属性。表面看来,通过属性类型似乎足以判别某种属性。比如使用温度属性来获取温度,通过设备名称属性来获取设备名等。但是,如果设备包含了两种温度属性,比如一个室内温度传感器加上室外温度传感器,情况会变得怎样。这时你便无法直接读取温度传感器,而必须读取第一个或第二个温度属性。考虑到可能有任意多个温度传感器,问题将变得更加复杂。为了解决这个同题,我们使用了一个16位的地址,也就是属性句柄。有效的句柄范围从0x0001--xFFFF。0x0000为无效句柄,不能用于寻址属性。可以根据在软硬件或嵌入式方面的背景,把句柄(Handle)相应地想象为内存地址、端口号、属性值对应的硬件寄存器地址

    属性类型:可以被公开的数据有许许多多的类型:温度、压强、体积、距离、功率、时间、充电状态、开关状态、状态机的状态等。所公开的数据的种类称作属性类型。为了区分如此多的数据类型,一串128位的数字被用来标识属性的类型。这个唯一标识码就叫做通用唯一识别码(UUID),128位的UUID相当长,设备间为了识别数据的类型需要发送长达16个字节的数据。为了提高传输效率,蓝牙技术联盟( SIG)定义了一种称为“蓝牙UUID基数”的128位通用唯一识别码,结合一个较短的16位数使用。二者仍然遵循通用唯一识别码的分配规则,只不过在设备间传输常用的UUID时,只发送较短的16位版本,接收方收到后补上蓝牙的UUID基数即可。

蓝牙UUID基数如下:

000000000000—1000—8000—00805F9B34FB

例如要发送的16位识别码位0X2A01,完整的128位UUID便是:

00002A010000—1000—8000—00805F9B34FB

    由上所述,所以在协议栈代码中经常见到的都是16位的UUID而不常见128位的UUID的原因所在,下面是官方demo的UUID,供参考。


[plain] view plain copy
  1. // Simple Profile Service UUID  
  2. #define SIMPLEPROFILE_SERV_UUID               0xFFF0  
  3.       
  4. // Key Pressed UUID  
  5. #define SIMPLEPROFILE_CHAR1_UUID            0xFFF1  
  6. #define SIMPLEPROFILE_CHAR2_UUID            0xFFF2  
  7. #define SIMPLEPROFILE_CHAR3_UUID            0xFFF3  
  8. #define SIMPLEPROFILE_CHAR4_UUID            0xFFF4  
  9. #define SIMPLEPROFILE_CHAR5_UUID            0xFFF5  


谈到16位的UUID,通常不直接使用数值,而是冠以一个名称并加上书名号(这些UUID可以在gatt_profile_uuid.h中查看到

0x1800 - 0x26FF用作服务类通用唯一识别码

0x2700 - 0x27FF用于标识计量单位

0x2800 - 0x28FF用于区分属性类型

0x2900 - 0x29FF用作特性描述

0x2A00- 0x7FFF用于区分特性类型

UUID,就是用来唯一识别一个特征值的ID。

handle,就是对应的attribute的一个句柄。

具体细节详见:

摘抄一部分源码供参考:


[plain] view plain copy
  1. /**  
  2.  * GATT Service UUIDs  
  3.  */  
  4. #define IMMEDIATE_ALERT_SERV_UUID       0x1802  // Immediate Alert  
  5. #define LINK_LOSS_SERV_UUID             0x1803  // Link Loss  
  6. #define TX_PWR_LEVEL_SERV_UUID          0x1804  // Tx Power  
  7. #define CURRENT_TIME_SERV_UUID          0x1805  // Current Time Service  
  8. #define REF_TIME_UPDATE_SERV_UUID       0x1806  // Reference Time Update Service  
  9.   
  10. /**  
  11.  * GATT Characteristic UUIDs  
  12.  */  
  13. #define ALERT_LEVEL_UUID                0x2A06  // Alert Level  
  14. #define TX_PWR_LEVEL_UUID               0x2A07  // Tx Power Level  
  15. #define DATE_TIME_UUID                  0x2A08  // Date Time  
  16. #define DAY_OF_WEEK_UUID                0x2A09  // Day of Week  
  17. #define DAY_DATE_TIME_UUID              0x2A0A  // Day Date Time  
  18. #define EXACT_TIME_256_UUID             0x2A0C  // Exact Time 256  
  19.   
  20. /**  
  21.  * GATT Unit UUIDs  
  22.  */  
  23. #define GATT_UNITLESS_UUID                    0x2700  //   
  24. #define GATT_UNIT_LENGTH_METER_UUID           0x2701  // m, m  
  25. #define GATT_UNIT_MASS_KGRAM_UUID             0x2702  // kg, kg  


    所有对特征值的操作,都是通过对UUID 的搜索得到对应的handle之后,通过handle来操作特征值的。对于蓝牙通信来说,其都是通过一个个不同的UUID来标识区分不同的服务,区分不同的特性,甚至服务和特性之间的类别。



六、总结

    不知道写什么了,就这样吧,后续想到了写吧,学习BLE也不久,路还很长。路漫漫其修远兮,吾将上下而求索~~~

    PS:该死的房价。。。。啊啊啊啊啊啊。。。。

    参考:1)

               2)Bluetooth开发者门户

               3)Bluetooth Spec Core_V4.1

阅读(1938) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~