前言
(1)本章节主要是对
BLE
技术进行简单的介绍,熟悉蓝牙技术的发展过程,了解相关术语方便后续的学习。
(2)为了防止单篇博客太长以至于看不下去,因此我基础概念章节分为两篇来写。
(3)叠甲一:本人刚接触BLE开发不久,文章尽可能的查询了各方面资料进行学习。如果依旧存在错误,愿各位大佬不吝赐教。
(4)叠加二:本文仅用于科普入门,因此讲解深度并不是很深,愿周知。
蓝牙与BLE关系
蓝牙的起源
(1)1994-1997年之间,包括爱立信,英特尔,诺基亚在内的一些科技公司,想要制定一种短距离无线通信协议,用于各种电子设备之间通讯,取代当时的有线通信形式。
(2)可是各个公司都推出了自己的通信协议,不同厂商的设备并不兼容。于是在1997年的一次通信会议上,各个厂商希望制定一种统一的通信协议,使的他们的设备相互兼容。在这场会议中,商定了“Bluetooth
”作为通信协议的名称。
Bluetooth
是由一位英特尔的工程师 Jim Kardach 推荐的。原因是他当时正在阅读有关维京人和哈拉尔国王的历史小说,由于哈拉尔国王以统一了因宗教战争和领土争议而分裂的挪威与丹麦而闻名于世,国王的成就与此次会议的的理念不谋而合,而且传说哈拉尔国王有一个坏死的牙齿,变成的蓝色,所以世人就给他起了个绰号“Bluetooth
”,因此该工程师提议使用哈拉尔国王的绰号“Bluetooth
”作为通信协议的名称。- 蓝牙的图标也是和这位哈拉尔国王有关。 哈拉尔国王真名拼写“Harald”,蓝牙组织将国王的真名“
Harald
”和绰号“Bluetooth
”的首字母拼合在一起,配以蓝牙的底图,就形成了蓝牙图标。
(3)1998 年 2月,5个跨国大公司,包括爱立信、诺基亚、BM、东芝及 Intel 组成了一个特殊兴趣小组(
SIG
),他们共同的目标是建立一个全球性的小范围无线通信技术,即现在的蓝牙。
蓝牙发展
(1)1999年,爱立信联合其他科技公司在制定了蓝牙1.0 协议版本。早期的协议版本在兼容性、安全性、传输速率等方面都有很多的不足,所以此本并未得到大量应用。此时的蓝牙称为基本码率,
Basic Rate
,简称BR
。
(2)2004年,蓝牙2.0的协议版本发布,增加了增强速率模式(EDR
:Enhanced DataRate
),同传输速率提升至2-3Mbps.
(3)2009年,引入全新的交替射频技术(AMP
:Alternate MACIPHY
),推出蓝牙3.0+ HS(High Speed)高速蓝牙的功能。HS
使得Bluetooth
能利用Wi-Fi
作为传输方式进行数据传输,允许蓝牙协议栈针对任一任务动态地选择正确射频,通过瞬间使用消费者设备中已存在的辅助无线电提供更快的吞吐量,使其支持的传输速度最高可达24Mbps。
(4)2010年,推出蓝牙4.0标准,它是第一个综合性规范,其加入了全新的蓝牙低功耗技术,即蓝牙 4.0集三种规格于一体。最重要的特点就是省电,可以使一粒纽扣电池连续工作数年。三种规格如下:
- 传统蓝牙(Classic Bluetooth):适用于高数据传输需求的应用,如音频和视频流。
- 低功耗蓝牙(Bluetooth Low Energy, BLE):适用于低数据传输需求且需要长时间运行的设备,如传感器和可穿戴设备。
- 高速蓝牙(High Speed Bluetooth):高速蓝牙并不是一种独立的蓝牙类型,而是经典蓝牙的一种增强模式。它利用了
Wi-Fi
技术来实现更高的数据传输速率。具体来说,高速蓝牙允许设备在需要传输大量数据时,切换到Wi-Fi
连接,从而实现更快的数据传输速度。适用于需要快速传输大量数据的场景。(5)2016年,推出蓝牙5.0标准。相比较蓝牙4.0,具有 2倍的数据速率、4 倍的覆盖范围和8倍的广播能力。新增高精度室内定位的功能,针对
loT
物联网进行底层优化,力求以更低的功耗和更高的性能为智能家居服务。
关于经典蓝牙和高速蓝牙的问题
在编程时,通常只会看到选择
BR/EDR
(经典蓝牙)和BLE
(低功耗蓝牙)的选项,而看不到关于高速蓝牙(High Speed Bluetooth)的内容,主要有以下几个原因:
- 高速蓝牙的实现方式:高速蓝牙(High Speed Bluetooth)并不是一种独立的蓝牙模式,而是经典蓝牙的一种增强功能。它通过结合
Wi-Fi
技术来实现更高的数据传输速率。这意味着高速蓝牙的实现依赖于设备同时支持蓝牙和Wi-Fi
,并且需要在蓝牙协议栈之外进行额外的Wi-Fi
配置和管理。但并不是所有的蓝牙设备同时支持蓝牙和Wi-Fi
的硬件能力。- 开发工具和库的支持:大多数蓝牙开发工具和库主要关注经典蓝牙(
BR/EDR
)和低功耗蓝牙(BLE
),因为这两种模式是蓝牙技术的核心部分,应用最为广泛。高速蓝牙的实现涉及到跨协议的操作(蓝牙和Wi-Fi
),这增加了开发的复杂性,因此在标准的蓝牙开发工具和库中通常不会直接提供对高速蓝牙的支持。即使进行了相关支持,高速蓝牙在编程时通常被归类到经典蓝牙中,因为高速蓝牙本质上是经典蓝牙(BR/EDR
)与Wi-Fi
技术的结合。- 应用场景的需求:高速蓝牙主要用于需要传输大量数据的特定应用场景,如大文件传输或视频流传输。然而,许多蓝牙应用并不需要如此高的数据传输速率,经典蓝牙和低功耗蓝牙已经能够满足大多数应用的需求。因此,开发者在编程时通常只需要选择
BR/EDR
或BLE
即可。综上所述,在编程时通常只会看到选择
BR/EDR
和BLE
的选项,而看不到关于高速蓝牙的内容,是因为高速蓝牙的实现方式复杂,开发工具和库的支持有限,以及应用场景和硬件支持的限制。而且高速蓝牙并非独立的技术模式,而是经典蓝牙的一种特性,因此在编程时通常不会单独列出高速蓝牙的选项,而是集成在经典蓝牙(BR/EDR
)中一同使用。
蓝牙的三种LOGO和设备类型
三种LOGO
(1)上面我们说了,蓝牙4.0是第一个综合性规范,它集三种规格于一体。但是这三种规格又有不同的应用场景,在某些特定的场景中,我们仅仅只需要一种规范即可满足任务。因此蓝牙兴趣小组(
SIG
)规定了如下三种LOGO
用于标识版本之间的区别和兼容性。
(2)这三种LOGO的含义如下:
- Bluetooth Smart Ready:同时支持经典蓝牙(
BR/EDR
)和低功耗(BLE
)技术,设备可以与两种类型的蓝牙产品通信。如手机。- 传统 Bluetooth:仅支持经典蓝牙(
BR/EDR
)。如用于传输语音的蓝牙耳机。- Bluetooth Smart:仅支持低功耗蓝牙(
BLE
)技术的设备,主要用于低功耗和低复杂度的应用。如可穿戴设备、健身追踪器等。
单模与双模
(1)蓝牙4.0以后的版本分为两种模式,单模蓝牙和双模蓝牙。
- 单模设备:只支持低功耗蓝牙(
BLE
),如用 nRF51822 开发的蓝牙设备即为单模设备,单模设备对低功耗的要求很高。- 双模设备:既支持经典蓝牙(
BR/EDR
)也支持低功耗蓝牙(BLE
),这两种技术使用同一个射频前端和天线,双模设备一般都有足够的供电能力,对低功耗的要求不高。
(3)下图为单模设备和双模设备与传统蓝牙设备的兼容性:
- 双模设备和双模设备:可以通过LE通信,也可以通过传统蓝牙通信。
- 单模设备和单模设备:通过 LE 通信。
- 传统蓝牙和传统蓝牙:通过传统蓝牙(经典蓝牙)通信。
- 双模设备和单模设备:通过 LE 通信。
- 双模设备和传统蓝牙:通过传统蓝牙(经典蓝牙)通信。
- 单模设备和传统蓝牙:不能通信。
BLE的体系结构
BLE的三层结构
(1)
BLE
体系结构如下(新手百分百看不懂下面的内容,但是还是建议先过一遍,后续会再进一步进行介绍):
<1>控制层(Controller):
- PHY 层(Physicallayer 物理层):
PHY
层用来指定BLE
所用的无线频段,调制解调方式和方法等。PHY
层还决定了传输数据的速度、整个BLE
芯片的功耗、灵敏度以及selectivity
等射频指标的配置。- LL层(Link Layer 链路层):
LL
层是整个BLE
协议栈的核心,也是BLE
协议栈的难点和重点。像Nordic
的BLE
协议栈能同时支持20个link
(连接),就是LL
层的功劳。LL
层要做的事情非常多,比如具体选择哪个射频通道进行通信,怎么识别空中数据包,具体在哪个时间点把数据包发送出去,怎么保证数据的完整性,ACK
如何接收,如何进行重传,负责广播、扫描、建立和维护连接,以及确保数据包按照正确的方式组织、正确地校验值和加密序列等等。LL
层只负责把数据发出去或者收回来,对数据进行怎样的解析则交给上面的GAP
或者GATT
。- 主机控制器接口 HCI(Host controller interface):这个是可选的,在主机(
Host
)和控制器(Controller
) 之间提供一个标准化的接口。该层可以由应用程序接口API
实现或者使用硬件接口UART
、SPI
或USB
来控制。控制器通过HCI
发送数据和事件给主机,主机通过HCI
发送命令和数据给控制器。<2>主协议层(HOST):
- GAP层(Generic access profile):
GAP
是对LL
层payload
(有效数据包)如何进行解析的两种方式中的一种,而且是最简单的那一种。GAP
简单的对LL payload
进行一些规范和定义,因此GAP
能实现的功能极其有限。GAP
目前主要用来进行广播,扫描和发起连接等。- L2CAP 层(Logic link control and adaptation protocol):
L2CAP
对LL
进行了一次简单封装,LL
只关心传输的数据本身,L2CAP
就要区分是加密通道还是普通通道,同时还要对连接间隔进行调整。通俗来说L2CAP
就是快递分拣工,快递来到中转站,分拣工需要根据快递的地址来判断运往哪里。- SMP(Secure manager protocol):
SMP
用来管理BLE
连接的加密和安全的,如何保证连接的安全性,同时不影响用户的体验,这些都是SMP
要考虑的工作。- ATT(Attribute protocol):简单来说,
ATT
层用来定义用户命令及命令操作的数据,比如读取某个数据或者写某个数据。BLE
协议栈中,开发者接触最多的就是ATT
。BLE
引入了属性(Attribute
)概念用来描述一条一条的数据。属性(Attribute
)除了定义数据,同时定义该数据可以使用的ATT
命令,因此这一层被称为ATT
层。- GATT(Generic attribute profile ):
GATT
用来规范属性(Attribute
)中的数据内容,并运用group
(分组)的概念对属性(Attribute
)进行分类管理。没有GATT
,BLE
协议栈也能跑,但互联互通就会出问题,也正是因为有了GATT
和各种各样的应用profile
,BLE
摆脱了ZigBee
等无线协议的兼容性困境,成了出货量最大的2.4G无线通信产品。<3>应用层(Profiles):最上层的
Profiles
层里,包含的公共任务和私有任务。其中公共任务是蓝牙协议小组(SIG
)定义的蓝牙任务,私有任务是用户或者企业自定义的蓝牙任务。
Controller层拓展解释
PHY层
(1)前面我们说了:“
PHY
层用来指定BLE
所用的无线频段,调制解调方式和方法等。PHY
层还决定了传输数据的速度、整个BLE
芯片的功耗、灵敏度以及selectivity
等射频指标的配置。”现在我选取其中三点将尽可能详细的解释PHY
的作用:
<1> 无线频段:
PHY
层决定了BLE
工作在2.4GHz ISM
频段,频率范围为2.400-2.4835 GHz
。- 再将
2.4GHz
频段划分为40
个信道,可以有效利用频谱资源、减少干扰、优化连接建立过程,并提高信号传输质量和可靠性。- 其中广播信道固定为
37
、38
、39
,对应的中心频率是2402MHz
、2426MHz
、2480MHz
。广播信道之间至少相差24MHZ
。每次广播,都会在3
个信道上将广播数据发送一次,这能有效地避免干扰,即使1
个信道存在干扰,另外的信道也可以很好地工作,而3
个信道同时被干扰的情况极少。- 广播信道越多,各个信道同时受到干扰的几率越小,抗干扰性越强。但是广播信道越多发射数据占用的时间就越长,功耗也就越高。所以,在综合考虑抗干扰性和功耗的情况下
SIG
将广播设定为3
个。- 广播信道的选择主要是考虑到
WI-FI
接入点的干扰。通过正好避免了三个WI-FI
常用接入点信道的覆盖,避免了他们的干扰。
<2> 调制解调方式:采用高斯频移键控(
GFSK
),带宽时间积BT=0.5,调制因子=[0.45-0.55],有效频率偏移为±185kHz。
<3>灵敏度:芯片的灵敏度是一个重要指标,能够能大程度上决定芯片传输的距离。从专业的术语来解释的话,接收灵敏度就是对应的芯片能解调的最小的信号能量大小。以人的视力举例说明:一个图案,距离人越远就越难看清楚细节(信号在空中传播会衰减)。张三能够在5米之外就能够看清楚图案的细节,李四要在3米才能看清细节。这表明张三的视力要比李四好(灵敏度更强)。
LL层
(1)前面说了LL层工作包括了具体选择哪个射频通道进行通信,这里就是让除3个广播信道以外的其余
37
个数据信道采用自适应跳频技术。自适应跳频技术会自动避开受干扰的信道,选择质量较好的信道进行数据传输,能够减少数据包的丢失和重传,从而提高通信的稳定性和效率。自适应跳频通常是基于跳频扩频(Frequency-Hopping Spread Spectrum, FHSS)技术,在多个频率间快速切换,以增加信号的抗干扰能力。
(2)LL层要对链路进行管理和控制,负责广播、扫描、建立和维护连接,以及确保数据包按照正确的方式组织、正确地校验值和加密序列等。因此,在LL层的设备有如下5种状态:
- 就绪态(Standby):上电后,链路层进入并保持就绪态,直到接收到主机的命令。状态机的中心状态,处于其它状态下都可以进入到此状态。
- 广播态(Advertising):发送广播报文和扫描响应,用于回应主动扫描的设备。可被发现或者可被连接的设备需要处于广播态。向在一定区域内对其他设备进行广播数据,也需要处于广播态。为了成为广播者,设备必须有发射机,但也可以有接收机。只支持广播态的设备可以没有接收机,以降低芯片成本。但是,实际中,出货量大的收发机芯片往往比出货量小的单发射机芯片更便宜。广播态的设备停止广播后可进入就绪态。在收到发起者的连接请求之后,广播态的设备也可以进入连接态。
- 扫描态(Scanning):接收广播信道的报文。扫描态有两个子状态:主动扫描和被动扫描。被动扫描仅接收广播报文。主动扫描则发送扫描请求给广播态设备,并获取附加的扫描响应数据。扫描态的设备只能进入就绪态,转换的条件是停止扫描。
- 发起态(Initialing):发起态设备侦听自己试图连接的设备,如果收到了来自该设备的广播报文,链路层会向其发送连接请求并进入连接态,并假设广播者也进入连接态。如果发起者不再试图发起连接,也可以进入就绪态。
- 连接态(Connection):唯一一个用到数据信道的状态,其他状态均使用广播信道,两个设备只有在连接态中才能互相传送数据。连接态只能通过断开连接转换为就绪态,连接态有两个子状态:主、从。
(3)前面说了,扫描态(Scanning)有两个子状态:主动扫描和被动扫描。
- 被动扫描:设备只能被动地扫描,不能发送任何报文。因此,被动扫描可以在只有接收机的设备中实现。被动扫描主要适用于低功耗场景,因为只进行扫描,不会主动发送任何请求,因此能够节省电量。简单的设备发现和监控应用中,只需要获取设备的基本信息(如MAC地址和广播数据),被动扫描足以满足需求。
- 主动扫描:主机不仅监听广播信道的数据包,还会发送扫描请求(
SCAN_REQ
)给广播设备。广播设备接收到扫描请求后,会返回一个扫描响应包(SCAN_RSP
),提供额外的信息。扫描请求和响应报文都在广播信道中传输。因为就能耗而言,扫描请求、响应这两个额外的报文使得能耗更高。但是,主动扫描可以获取更多数据,如设备名称、服务数据等,因为它能接收额外的扫描响应包。(4)连接态也有两个子状态:主和从。下图为进入主、从两种状态的方法。
- 主连接态:主连接态只能从发起态进入。为了成为主设备,它必须发起连接请求,扫描周围的广播设备,并选择一个目标设备进行连接。一旦连接建立,主设备控制连接参数,如连接间隔、超时时间等,确保通信的稳定性。主设备必须定期向从设备发送报文。主设备通过发送数据包或空的PDU(协议数据单元)来维持连接,并可以请求从设备返回数据。
- 从连接态:从连接态只能从广播态进入。为了成为从设备,它必须进行广播,一旦接收到连接请求,要进行响应,最终建立连接。从设备没有主动发送数据的能力,只有当主机发送连接事件后,从机正确接收主设备的报文才能发送数据进行回应。如果从设备有更多的数据需要发送,必须等待主设备发送另外的报文再回复。如果从机没有数据需要发送,也可以忽略主机的报文,以达到节能作用。但是这个忽略次数是有限制的,由开发人员决定。超过一定次数,即认为断开连接了,此时将从连接态转换为就绪态。
HCI层
(1)从上面我们可以知道,
Controller
只实现链路层的状态机,管理连接参数、信道更新、加密等链路控制。通俗来说,就是它只管数据收没收到,至于数据安不安全、有什么含义、不同的设备之间能否兼容都不是由它来管。如果只需要进行点对点的简单无线数据传输,不考虑安全性、兼容性等问题,理论上可以只要Controller
层就可以了。
(2)因为需要保证不同的设备之间具备兼容能力和安全性等问题,于是有了HOST
层来确保BLE
协议栈的上层协议和逻辑,包括设备发现、连接管理、数据传输和安全性等功能。
(3)如果HOST
层和Controller
层集成在同一款芯片上,理论上是可以不需要HCI
层的。但是现实情况不是这样的,在手机这类复杂场景中,手机应用跑在AP
芯片上,然后手机厂商想自己设计一个BLE
协议栈(HOST
层工作)已达到定制的功能,这样他们就只需要一个Controller
层的芯片。为了降低工作量,手机厂商不可能再去找一个包含Controller
层的AP
芯片,于是就有了如下的设计方案。AP
芯片跑HOST
层,然后外置一个Controller
层的芯片。
(4)但是这又诞生了一个问题,如果手机厂商认为,此时有一个更好的Controller
层芯片了,我要进行替换怎么办呢?于是,为了降低替换的工作量,SIG
规定了一个HCI
层同一接口规范。
(5)上述方案是手机厂这类有一定蓝牙开发能力的公司采取的方案,如果有一些小公司,也想使用蓝牙功能,但是对蓝牙技术又不太了解怎么办呢?此时他们就可以买一些外置蓝牙模块,然后通过这些蓝牙模块的
AT
指令实现蓝牙传输功能。此时需要注意,外置的蓝牙模块内部包含Controller
层和HOST
层,这个外置的蓝牙模块可以没有HCI
层。
(6) 最后一种方案大多用于消费类电子,集成度很高,调调部参数可以直接使用。这类一般是半导体厂商半开源协议栈,基于特定的编译器,把蓝牙协议栈直接烧写到蓝牙芯片中,例如乐鑫
ESP32
、Nordic NRF51xxx
、TI CC2540
。这类型芯片不但可以直接使用蓝牙功能,还能外接一些设备,例如LCD
显示屏,音频播放器等。可以做出的产品有蓝牙手表,蓝牙音频等。这类型方案的芯片中也是内部包含Controller
层和HOST
层,可以没有HCI
层。
举例理解BLE体系结构
(1)通过上面的讲解,相信依旧有很大一部分朋友依然搞不明白每一层到底做了什么,有什么意义。这很正常,这些概念和术语首次理解起来确实会很抽象,这里我将会举一个简单的例子再次讲解说明促进理解。
(2)现在,我们假设手上的蓝牙耳机把自己目前的电量状态83%
(十六进制表示为0x53
)发给手机,应该怎么做?作为应用层开发者,肯定希望越简单越好,最好可以只需要调用一个API
接口即可完成任务,例如调用Battery_Capacity_Send(0x53)
函数,蓝牙耳机就可以将自己当前电量发送给手机。实际上,BLE
协议栈就是这样做的,上层应用只需要调用一个API
,其余的BLE
协议栈都会帮你处理好。但是呢,从物理层的角度来看,真的是如下图一样,只发送了一个0x53
吗?
(3)我能进行反问,就说明肯定不是这样的。仔细想想,上面我们说了,
BLE
存在40
个信道,那么现在我们发送数据,到底是将0x53
发送到哪个信道里面呢?毫无疑问,肯定不可能把数据发送到40
个信道里面,那样会造成信道的拥挤,并且能耗大大增加。为了解决这个问题,此时就需要引入LL层了。上层应用开发者调用Battery_Capacity_Send(0x53)
函数,但是这个函数其实就是LL_Send(Battery_Capacity,2402M)
函数的一次封装,这样标识电量数据固定在2402M信道频率上进行传输。
void Battery_Capacity_Send(int Battery_Capacity)
{
//假设电量数据固定在2402M信道频率上
/* Battery_Capacity : 电量信息
* 2402M : 数据发送信道
*/
LL_Send(Battery_Capacity,2402M);
}
(4)现在我们知道了电量数据应该发送在哪个信道上了,但是,又需要考虑一个问题。这个数据到底是发送给谁呢?你总不可能一个设备占用一个信道吧,上面也说了,
BLE
存在自适应跳频技术会不断调整信道以达到信号的抗干扰能力。于是这里又需要引入另外一个概念 —— 访问地址(access address
)。访问地址(access address
)用于表明数据是发送给谁的,其中,0x8E89BED6这个访问地址(access address
)比较特殊,它表示要发给周边所有设备,即广播。如果想让蓝牙耳机数据只被自己的手机收到,自己手机的数据只被自己的蓝牙耳机收到,那么就需要生成一个独特的随机访问地址(access address
),用于标识设备之间的连接。接下来,我将以广播和连接两种方式进行说明各层的作用。
广播方式
(1)在广播模式下,蓝牙耳机被称之为广播者(
advertiser
),手机被称为 扫描者(scanner
) 或者 观察者(observer
)。因为是广播,所以需要加上广播地址,因此蓝牙耳机的LL
层的API
实际上就应该是如下了:
void Battery_Capacity_Send(int Battery_Capacity)
{
//假设电量数据固定在2402M信道频率上进行广播
/* Battery_Capacity : 电量信息
* 2402M : 数据发送信道固定为2402M
* 0x8E89BED6 : 表示进行广播
*/
LL_Send(Battery_Capacity,2402M,0x8E89BED6);
}
(2)但是,手机此时能够同时接收到多个设备的广播信息,为了确保收到的广播信息是源自于蓝牙耳机,此时还应当加上蓝牙耳机的访问地址(
access address
)。那么此刻LL
层的API
应当如下:
void Battery_Capacity_Send(int Battery_Capacity)
{
//假设电量数据固定在2402M信道频率上进行广播
/* Battery_Capacity : 电量信息
* 2402M : 数据发送信道固定为2402M
* 0x8E89BED6 : 表示进行广播
* 0xE1022AAB753B : 蓝牙耳机的访问地址(access address)
*/
LL_Send(Battery_Capacity,2402M,0x8E89BED6,0xE1022AAB753B);
}
(3)数据发送似乎没有问题了,但是我们的LL层还需要考虑到数据在空中传播过程中没有被干扰,为此引入了
CRC24
对数据包进行检验 (假设为0xB2C78E
) 。那么此刻LL
层的API
应当如下:
void Battery_Capacity_Send(int Battery_Capacity)
{
//假设电量数据固定在2402M信道频率上进行广播
/* Battery_Capacity : 电量信息
* 2402M : 数据发送信道固定为2402M
* 0x8E89BED6 : 表示进行广播
* 0xE1022AAB753B : 蓝牙耳机的访问地址(access address)
* 0xB2C78E : CRC24校验
*/
LL_Send(Battery_Capacity,2402M,0x8E89BED6,0xE1022AAB753B,0xB2C78E);
}
(4)同时为了调制解调电路工作更高效,每一个数据包的最前面会加上1个字节的
preamble
(前导帧),preamble
一般为0x55
或者0xAA
,这里取0xAA。那么此刻LL
层的API
应当如下:
void Battery_Capacity_Send(int Battery_Capacity)
{
//假设电量数据固定在2402M信道频率上进行广播
/* 0xAA : 前导帧
* Battery_Capacity : 电量信息
* 2402M : 数据发送信道固定为2402M
* 0x8E89BED6 : 表示进行广播
* 0xE1022AAB753B : 蓝牙耳机的访问地址(access address)
* 0xB2C78E : CRC24校验
*/
LL_Send(0xAA,Battery_Capacity,2402M,0x8E89BED6,0xE1022AAB753B,0xB2C78E);
}
(5)通过上面的分析我们即可得出数据的空中包如下(空中包用小端模式表示!):
(6)通过上面的封装,似乎就万事大吉了。但是,如果实测之后会发现,还有很多问题。
<1>当手机收到AAD6BE898E3B75AB2A02E1538EC7B2
这串数据的时候,它如何知道那个才是自己想要的电量数据呢?因此,我们这里需要在访问地址(access address
)后面加入两个字段Advertising Header
和长度字节。Advertising Header
用来表示数据包的LL
类型,包括可连接的无定向广告(ADV_IND
)、可连接的定向广告(ADV_DIRECT_IND
)、不可连接的无定向广告(ADV_NONCONN_IND
)、可扫描的无定向广告(ADV_SCAN_IND
)等。长度字节用来指明有效载荷(payload
)的长度。
<2>为了保持低功耗,蓝牙设备不会一直打开射频窗口,因此手机应该什么时候开启设备窗口接收蓝牙耳机的数据呢?
- Case1:当蓝牙耳机发送数据包的时候,手机将接收窗口关闭,那么此时将无法成功通讯。
- Case2:手机打开接收窗口,但是蓝牙耳机没有发送数据包,显然此时也将无法成功通讯。
- Case3:只有当手机打开接收窗口,同时蓝牙耳机发送数据包,此时才能成功通讯。
综上所述:
LL
层还必须定义通信时序,并且加入Advertising Header
和长度字节用于表示有效数据位置。
(7)通过上面的封装,此时手机能够收到有效数据
0x53
了。但是,各位再思考思考,蓝牙耳机总不可能只给手机发送一个电量信息吧,还有其他信息,例如音频信息。LL
层说了,它只负责收发数据,至于怎么处理,不归它来负责。因此,现在接力棒来到了GAP
层了。
(8)GAP
层引入了LTV(Length-Type-Value)
结构来定义数据,比如020105
,02
-长度,01
-类型(强制字段,表示广播flag
,广播包必须包含该字段),05
-值。
(9)GAP
层并没有定义电量信息类型数据,因此Type
我们需要设置为0xFF
,表示供应商自定义数据类型即04FF590053
,其中04
表示长度,FF
表示数据类型(自定义数据),0x0059
是供应商ID
(自定义数据中的强制字段),0x53
就是我们的数据(设备双方约定0x53
就是表示电量)。
(10)因此,最终数据包变成了下图所示:
<1>AA – 前导帧(preamble)
<2>D6BE898E – 访问地址(access address),表示进行广播
<3>60 – LL帧头字段(LL header),表示非连接无向广告事件
<4>0E – 有效数据包长度(payload length)
<5>3B75AB2A02E1 – 广播者设备地址(advertiser address),即蓝牙耳机地址
<6>02010504FF590053 – 广播数据
- 020105 : 强制字段,必须包含,表示设备处于LE Limited Discoverable Mode,并且不支持BR/EDR。
- 04FF590053 :
04
表示长度,FF
表示自定义数据,0x0059
是供应商ID
(自定义数据中的强制字段),0x53
就是我们的数据<7>8EC7B2 – CRC24值
(10)以C代码方式理解如下:
void GAP_Send(int LTV1, int LTV2,int Battery_Capacity)
{
/* 2402M : 数据发送信道固定为2402M
* 0xAA : 前导帧
* 0xD6BE898E : 表示进行广播
* 0x60 : 表示非连接无向广告事件
* 0x0E : 有效数据包长度(payload length)
* 0x3B75AB2A02E1 : 蓝牙耳机地址
* LTV1 : 广播数据中的强制字段,表示设备处于LE Limited Discoverable Mode,并且不支持BR/EDR。
* LTV2 : 厂商自定义数据,表示电量
* Battery_Capacity : 电量信息
* 0xB2C78E : CRC24校验
*/
LL_Send(2402M,0xAA,0xD6BE898E,0x60,0x0E,0x3B75AB2A02E1
LTV1,LTV2,Battery_Capacity,0xB2C78E);
}
void Battery_Capacity_Send(int Battery_Capacity)
{
/* 020105 : 强制字段,必须包含,表示设备处于LE Limited Discoverable Mode,并且不支持BR/EDR。
* 04FF5900 : 04表示长度,FF表示自定义数据,0x0059是供应商ID
* Battery_Capacity : 电量信息
*/
GAP_Send(020105,04FF5900,Battery_Capacity);
}
(11)总结:
- 在广播方式中,
LL
层主要负责知道通讯双方地址,控制数据的状态信息,确保数据无误的上传到HOST
层。GAP
层得到LL
层的数据,并将数据封装成相应的含义。(12)现在我们再回过头来看看上面的这个体系结构,是不是就能够理解
GAP
层为何会贯穿整个HOST
层了呢?原因很简单,如果进行广播,只需要LL
层和GAP
层即可,因此GAP
层会贯穿整HOST
层。
连接方式
(1)通过上面广播的例子,我们理解了
GAP
层和LL
层作用。现在我们看看连接方式是怎么样的呢。首先,我们需要搞明白,手机和蓝牙耳机的连接(connection
)是什么意思。
(2)以有线通讯为例,例如手机通过数据线连接电脑进行通讯,这个过程叫做连接。数据线将两个设备相连,实际上是让2个设备有共同的通信媒介,并让两者时钟进行同步。蓝牙连接也是同理,手机和蓝牙耳机建立蓝牙连接,就是让手机和蓝牙耳机两者一对一的同步成功。其具体包含以下方面:
- 手机和蓝牙耳机对接下来要使用的物理信道达成一致。
- 双方建立一个共同的时间锚点,也就是说,把双方的时间原点变成同一个点。
- 两者时钟同步成功,即双方都知道对方什么时候发送数据包什么时候接收数据包。
- 连接成功后,手机和蓝牙耳机通信流程如下所示:
(3)一旦手机(Master) 和蓝牙耳机(Slave) 连接成功,手机(Master) 将会以
CI(connection interval)
这个间隔向蓝牙耳机(Slave) 发送数据包,而蓝牙耳机(Slave) 也会周期性以CI(connection interval)
为间隔打开射频窗口来接收手机(Master) 的数据包。同时,按照蓝牙规范要求,当蓝牙耳机(Slave) 收到手机(Master) 的数据包150us
后,蓝牙耳机(Slave) 需要从发送状态切换为接收状态,把自己的数据发送给手机(Master) 。同理,手机(Master) 在发送完数据包150us
后,需要从发送状态转换为接收状态。由此可见,连接状态下,手机(Master) 和蓝牙耳机(Slave) 的射频发送和接收窗口都是周期性地有计划地开和关,而且开的时间非常短,从而大大降低系统功耗并大大提高系统效率。
(4)明白了连接状态下手机(Master) 和蓝牙耳机(Slave) 的收发方式,那么我们就来看看数据包如何发送。依旧是以蓝牙耳机(Slave) 将电量状态83%
(十六进制表示为0x53
)发送给手机(Master) 。
- 应用层:只需要调用
Battery_Capacity_Send(0x53)
即可将电量信息发送出去。- GATT层:蓝牙耳机(Slave) 数据
0x53
发出来了,手机(Master) 不知道这个0x53
是啥意思啊。于是GATT
层就需要定义数据的类型。例如这里创建一个Flag
为0x0013
表示电量这种数据类型,这样GATT
层把数据打包成130053
(小端模式!)- ATT层:蓝牙耳机(Slave) 把电量数据
0x53
发送给手机(Master) 了,但是这样又存在一个问题。手机(Master) 怎么知道这个数据到底是用来干嘛的呢?比如这个电量数据可以是将手机(Master) 的电量修改为0x53
,或者是只是需要读到这个数据就可以。ATT
层有读/写/notify
/indicate
等等通讯命令,我们这里选择notify
命令0x1B
,表示发送数据更新,不需要回复。这样数据包变成了:1B130053
(小端模式!)- L2CAP层:数据包封装好了之后,问题是现在这个数据包发到哪个逻辑通道里面。这里我们指定逻辑通道编号
0x0004
(表示ATT
命令),最后还需要加上ATT
数据长度0x0004
加在包头,最终数据就变为:040004001B130053
。- LL层:这一层要干的活上面已经说过了,因此不做赘述。
(5)最终数据包长这样:
- AA : 前导帧(preamble)
- 0x50655DAB : 访问地址(access address),即手机地址
- 1E : LL帧头字段(LL header)
- 08 : 有效数据包长度(payload length)
- 04000400 : ATT数据长度,以及L2CAP通道编号
- 1B : notify command
- 0013 : 电量数据handle
- 53 : 真正要发送的电量数据
- F650D5 : CRC24值
参考
(1)红旭无线:BLE基础知识
(2)C站:【低功耗蓝牙】① 蓝牙广播数据格式分析
(3)谷雨文档中心:BLE技术揭秘
(4)TI官方蓝牙BLE 低功耗技术教程:蓝牙低功耗技术及其特点
(5)C站:蓝牙4.0、经典蓝牙、BT、BLE的关系与区别
(6)博客园:深入浅出低功耗蓝牙(BLE)协议栈
(7)博客园:三种蓝牙架构实现方案(蓝牙协议栈方案)
(8)知乎:(通信算法系列)蓝牙BLE物理层算法详解—GFSK调制与解调(1)
(9)简书:BLE 信道和自适应跳频
(10)C站:一文让你彻底了解市面蓝牙架构,无忧蓝牙产品选型
(11)博客园:详解BLE空口包格式—兼BLE Link layer协议解析
(12)[艾克姆科技教程]nRF52832开发指南.pdf
(13)低功耗蓝牙开发权威指南.pdf
(14)刘权-BLE4.0低功耗蓝牙协议总结.pdf