蓝牙可以分为经典蓝牙和低功耗蓝牙,本文重点介绍低功耗蓝牙(BLE).
一、BLE协议栈结构
以TI的CC26XX芯片为例,BLE协议栈可以由如下图所示部分组成:
1、物理层:
物理层是BLE协议栈最底层,规定了BLE通信的基础射频参数,包括信号频率、调制方案等。BLE4的物理层是1Mbps的GFSK调制。在BLE 5的物理层除了兼容原来1Mbps 的GFSK外,还增加了2Mbps GFSK调制,这样做的好处是单位时间内数据传输量增加一倍或者传输相同数据量仅用一半的时间,就可以有效降低功耗;
2、LL层:
用于控制设备的射频状态,可工作于advertising、sacnning、Initiating、connecting四中状态。advertising和sacnning状态是配对出现的,一个设备处于advertising状态,像外部设备发送广播包,处于scanning状态的设备可以接受广播包。如果处于sacnning状态的设备通过发送连接请求来回应advertising设备,如果广播设备接受连接请求,那么广播设备与发起连接的设备将会进入连接状态。
3、HCI层:
为主机和控制器之间提供标准通信接口。这一层可以是软件或者硬件接口,如UART、SPI、USB等。
4、L2CAP层
L2CAP对LL进行了一次简单封装。
5、ATT/GATT
GATT负责主从设备之间的应用数据交换。GATT作为使用的ATT的子流程的一个服务型框架。为主从设备交互数据提供Profile、Service、Characteristic等概念的抽象、管理。当两个设备建立连接后,就处于GATT服务器或者GATT客户端的角色。
6、GAP
GAP是对LL层的一部分进行封装,主要用来广播,扫描和发起连接等。
7、SM
定义了配对和秘钥分配方式,并为协议栈其他层与另一个设备之间的安全连接和数据交换提供服务。
GAP层的角色有广播者,观察者,外设和集中器四种。处于advertising状态的设备称为广播者,等待scanning设备称为观察者,当scanning设备发送连接请求,连接建立后,advertising状态的设备作为从机,scanning设备作为主机。
GATT层的角色有服务器和客户端。为GATT客户端提供数据服务的设备称为服务器,从GATT服务器读写应用数据的设备称为客户端。
二、BLE5的新特性
1、2X的数据传输速度:蓝牙5的数量提升了一倍是因为PHY层支持2M数据传输率。
2、4X的传输距离:得益于信道传输中编码方式的改变,详见下面链接:
https://blog.csdn.net/weixin_40204595/article/details/83069007
3、8X的大广播数据包:相比BLE4.x及以前协议只支持37、38、39、信道广播最多31字节数据,BLE5扩展了除37、38、39外的其他信道作为第二广播信道,最多可传输255字节数据。这解决了beacon应用中广播数据太少的问题。
三、蓝牙工作状态:
1、广播状态:针对BLE5.0协议栈,广播设备利用37、38、39主信道和其他的信道作为第二信道向外部发送广播数据,此时并没有指定接收者,所有的处于scanning状态的设备都可以接收到该广播数据。广播状态的数据包pdu结构如下图所示:
由16bit Header+payload组成。首先4bits PDU Type决定了广播状态,可以分为如下表格种类:
2、连接状态:
当两个设备建立连接后,GATT开始发挥作用,一个设备作为服务器,另一设备作为客户端。
五、BLE协议栈应用开发
利用协议栈进行蓝牙通信,当连接建立后,主要使用的就是GATT。一个GATT服务器中可包含一个或多个GATT服务,GATT服务是完成特定功能的一系列数据的集合。
“特性”(Characteristic)是服务用到的值,以及其内容和配置信息。GATT定义了在BLE连接中发现、读取和写入属性的子过程。GATT服务器上的特性值及其内容和配置信息(称为描述符)存储于属性表中。属性表是一个数据库,包含了成为属性的小块数据,除了值本身,每个属性都包含句柄,类型和权限三个部分,句柄就是属性在表中的地址,每个属性有唯一的句柄;类型通常是UUID;权限代表GATT客户端对属性的访问权限;
GATT定义了若干在GATT服务器和客户端之间的通信的子过程:
(1)读特性值:客户端设备请求读取句柄处的特性值,服务器将此值回应给客户端(假定属性有读权限)。
(2)使用特性的UUID读:客户端请求读基于一个特定类型的所有特性值,服务器将所有与指定类型匹配的特性的句柄和值回应给客户端设备(假设属性有读权限)。
(3)读多个特性值:客户端一次请求中读取几个句柄的特性值,服务器将这些特性值回应给客户端(假设属性有读权限),客户端需要知道如何解析这些不同的特性值数据。
(4)读特性描述符:客户端请求读特定句柄处的特性描述符,服务器将特性描述符的值回应给客户端设备(假设属性有读权限)。
(5)使用UUID发现特性:客户端通过发送“特性”的类型(UUID)来请求发现这个“特性”的句柄。服务器将这个”特性”的声明回应给客户端设备,其中包括特性值的句柄以及“特性”的权限。
(6)写特性值:客户端设备请求向服务器特定的句柄处写入特性值,服务器将数据是否写入成功的信息反馈给客户端(假设特性有写权限,另外有一种特殊的写类型是不需要服务器来反馈是否写入成功的信息的,使用的时候根据具体应用来具体分析使用)。
(7)写特性描述符:客户端设备请求向服务器特定的句柄处写入特性描述符,服务器将特性描述符是否写入成功的信息反馈给客户端(假设特性描述有写权限)。
(8)特性值通知:服务器将一个特性值通知给客户端,客户端设备不需要向服务器请求这个数据,客户端收到这个数据时,不需要属性协议层确认特性值是否被成功接收。
(9)特性值指示:服务器将一个特性值指示给客户端,客户端设备同样不需要向服务器请求这个数据,但是跟通知不一样的是,客户端收到这个数据之后,属性协议层必须确认特性值被成功接收。
(注:该部分引用自https://blog.csdn.net/zzfenglin/article/details/51706290)
六、重点问题关注
1、属性表中GATT_PROP_READ和GATT_PERMIT_READ辨析
打个比方说明,属性表是一列火车,它有SERVAPP_NUM_ATTR_SUPPORTED这么多节车厢,GATT_PERMIT_READ是每节车厢的钥匙。此时第18节~21节车厢装的是宝箱char6,GATT_PROP_READ是宝箱char6的钥匙。虽然两把都是钥匙,但是作用的对象不一样。实际上GATT_PERMIT_READ是针对属性表使用的,而GATT_PROP_READ是针对特征值使用的。
2、什么是CCC?
答:
Client Characteristic Configuration,俗称CCC。
notify属性的特征值,会多读、写属性的特征值多一个CCC。
从机要想使用notify函数时能正常发送出数据,就必须保证CCC是被打开的。
3、CCC如何打开?
答:notify开关可由主机端或者从机端打开,但应尽量保证由主机来打开比较合适,毕竟它是“主机”,“主机“就该有主动权。
1)主机端打开(推荐)
先获取到CCC的特征值句柄,然后利用CCC的特征值句柄往CCC的特征值中写入0x0001。
参考本博客博文《CC2541之主机端获取notify数据》。
2)从机端打开(不推荐)
GATTServApp_WriteCharCfg(connHandle, simpleProfileChar4Config, 0x0001);
七、最大可发送数据包长
详见https://blog.csdn.net/zzfenglin/article/details/51706290