Bluetooth BR/EDR和BLE的区别.md
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bluetooth BR/EDR和BLE的区别.md相关的知识,希望对你有一定的参考价值。
参考技术A (资料来自网络搜集)Bluetooth BR / EDR(蓝牙基本速率/增强数据速率)和BLE是蓝牙核心规范中的两个主要蓝牙技术,它们之间存在以下几个主要区别。
物理通道
所有无线电通信均发生在预定义的频道上,并且BLE使用的信道与Bluetooth BR / EDR略有不同,特别是BLE 引入了广播信道。
BR / EDR:79个频道,频道索引为0〜78,覆盖范围2400〜2483.5MHz
BLE:40个频道,频道索引为0〜39,覆盖范围为2400〜2483.5MHz,其中频道37、38和39为广播频道(ble 5.2 允许使用其他的频道进行第二广播)
能量消耗
低功耗是BLE的一大亮点。仅使用纽扣电池,BLE设备即可运行数月甚至数年。蓝牙智能的灵活配置还可以使应用程序更好地管理连接间隔(连接间隔),从而优化接收器的占空比。对于蓝牙BR / EDR,由于其较高的数据吞吐量,因此功耗会相应增加。
拓扑结构
BR / EDR支持星形拓扑的Piconet,也支持Scatternet的Piconet。在Scatternet中,每个微微网都有一个主设备,而从属设备可以基于时分复用参与不同的微微网。
BLE 4.1版本支持“双模式”,该模式允许BLE设备同时扮演“中央”和“外围设备”两个角色。支持中心角色的设备可以启动与外围设备的连接,而支持中心角色的设备也可以用作外围设备以连接到其他中心设备。将来,它还将支持BluetoothSmart Mesh(5.2已经支持)
开发方式
BLE开发非常灵活,开发人员可以借助蓝牙技术联盟使用的配置文件更灵活地自定义应用程序。无论开发人员想要构建哪种应用程序场景,灵活的配置文件定义都可以满足技术要求。
BR / EDR技术相对成熟,并且其开发是模块化的,因此通常只需要将模块集成到产品中即可。例如,在开发Beacon应用程序时应选择BLE,因为BR / EDR不支持Beacon应用程序
配对
配对对于蓝牙BR / EDR是必需的,但对于BLE能则是选择性的。
简单的BLE应用程序可能不需要配对。例如 ibeacon 等应用
数据吞吐量
BLE吞吐量约为1 Mbps(如规格中所述),但还取决于应用场景。
蓝牙BR / EDR吞吐量超过2Mbps,适用于高质量音频比特流或其他需要维持更高带宽连接的应用。
Profile
通用属性配置文件(GATT)可以由应用程序或其他配置文件调用,以允许客户端与服务器进行交互。当前,有许多使用GATT构建的配置文件。
联网能力
蓝牙技术联盟提供了三种基于BLE 的联网能力,RESTFul API,HTTP代理服务(HPS)和互联网协议支持配置文件(IPSP)的Internet访问方式。他们都需要网关设备进行连接。网关设备可以是任何可以访问Internet的设备,例如路由器,机顶盒,甚至是家里闲置的智能手机。 BLE的Internet功能使物联网更加可靠且易于实现。
博客大赛蓝牙BLE详解
一、什么是蓝牙?
蓝牙是一种短距的无线通讯技术,可实现固定设备、移动设备之间的数据交换。一般将蓝牙3.0之前的BR/EDR蓝牙称为传统蓝牙,而将蓝牙4.0规范下的LE蓝牙称为低功耗蓝牙。
很多人对蓝牙的认识还很局限于手机领域,其实蓝牙的应用已经远远不止于此。过去几年里,蓝牙的增长量就达到了80%,当然,低功耗蓝牙的出现也起到关键的作用,相信未来蓝牙会开创一个可交互的物联世界。
蓝牙4.0标准包括传统蓝牙模块部分和低功耗蓝牙模块部分,是一个双模标准。低功耗蓝牙也是建立在传统蓝牙基础之上发展起来的,并区别于传统模块,最大的特点就是成本和功耗降低,应用于实时性要求比较高。
BLE(Bluetooh Low Energy)蓝牙低能耗技术是短距离、低成本、可互操作性的无线技术,它利用许多智能手段最大限度地降低功耗。
BLE技术的工作模式非常适合用于从微型无线传感器(每半秒交换一次数据)或使用完全异步通信的遥控器等其它外设传送数据。这些设备发送的数据量非常少(通常几个字节),而且发送次数也很少(例如每秒几次到每分钟一次,甚至更少)。
二、BLE协议栈的结构和配置
1、协议有两个部分组成:Controller和Host
2、Profiles和应用总是基于GAP和GATT之上
3、在单芯片方案中,Controller和Host,profiles,和应用层都在同一片芯片中
4、在网络控制器模式中,Host和Controller是在一起运行的,但是应用和profiles在另外一个器件上,比如PC或者其他微控制器,可以通过UART,USB进行操作
5、在双芯片模式中,Controller运行在一个控制器,而应用层,profiles和Host是运行在另外一个控制器上
三、BLE协议栈各层功能机制
如上图所述,要实现一个BLE应用,首先需要一个支持BLE射频的芯片,然后还需要提供一个与此芯片配套的BLE协议栈,最后在协议栈上开发自己的应用。可以看出BLE协议栈是连接芯片和应用的桥梁,是实现整个BLE应用的关键。那BLE协议栈具体包含哪些功能呢?简单来说,BLE协议栈主要用来对你的应用数据进行层层封包,以生成一个满足BLE协议的空中数据包,也就是说,把应用数据包裹在一系列的帧头(header)和帧尾(tail)中。具体来说,BLE协议栈主要由如下几部分组成:
-
PHY层(Physical layer物理层)。
PHY层用来指定BLE所用的无线频段,调制解调方式和方法等。PHY层做得好不好,直接决定整个BLE芯片的功耗,灵敏度以及selectivity等射频指标。 -
LL层(Link Layer链路层)。
LL层是整个BLE协议栈的核心,也是BLE协议栈的难点和重点。像Nordic的BLE协议栈能同时支持20个link(连接),就是LL层的功劳。LL层要做的事情非常多,比如具体选择哪个射频通道进行通信,怎么识别空中数据包,具体在哪个时间点把数据包发送出去,怎么保证数据的完整性,ACK如何接收,如何进行重传,以及如何对链路进行管理和控制等等。LL层只负责把数据发出去或者收回来,对数据进行怎样的解析则交给上面的GAP或者ATT。 -
HCI(Host controller interface)。
HCI是可选的,HCI主要用于2颗芯片实现BLE协议栈的场合,用来规范两者之间的通信协议和通信命令等。 -
GAP层(Generic access profile)。
GAP是对LL层payload(有效数据包)如何进行解析的两种方式中的一种,而且是最简单的那一种。GAP简单的对LL payload进行一些规范和定义,因此GAP能实现的功能极其有限。GAP目前主要用来进行广播,扫描和发起连接等。 -
L2CAP层(Logic link control and adaptation protocol)。
L2CAP对LL进行了一次简单封装,LL只关心传输的数据本身,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无线通信产品。
四、BLE蓝牙模块主要应用领域
- 移动扩展设备
- 汽车电子设备
- 健康医疗用品:心跳带、血压计等
- 定位应用:室内定位、井下定位等
- 近距离数据采集:无线抄表、无线遥测等
- 数据传输:智能家居室内控制、蓝牙调光、打印机等
五、 BLE协议栈详解
协议概述
所谓协议,即将指定的字节按照一定的顺序排列起来,以便他人使用自己的设备时,能通过该协议同其他设备进行通信。协议一特点,就是有固定的帧格式,通过该格式发送,接收者通过解读帧格式,进而得到新息内容;
BLE连接过程
一般通信协议,一类通信是直接发生数据,当设备接送到数据时,直接对数据进行解析,当接受到的数据合法时,即为有效数据,该类型的通信协议,主要用在有线通信协议中,比如Modbus,Can通常采用的即为该类型的通信方式。
另一类通信协议,则需要新建立连接,当双方连接建立成功了方可通信,例如TCP、BLE;BLE协议在需要进行通信时,即需要向外发送广播信号,告诉接收者,即将和它进行通信,接受者接收到广播内容后,确认是与自己通信,于是向广播者发送一响应信息,这样当广播者和接受者都有了对方的身份信息时,即表示双方连接成功。
因此,在连接过程中,必定有相应的广播帧格式。在BLE通信过程中,假设设备A需要连其他设备假设为B,则A需要不断地发送广播信号(此过程一般有一个时间间隔,在没发送广播数据时间内,芯片处于低功耗状态),每发送一次广播包,称之为一次广播事件。
前导:
是一个8比特的交替序列
接入地址的第一个比特为0:01010101
接入地址的第一个比特为1:10101010
接入地址:广播帧为固定地址:0x8E89BED6(低字节在前)
广播报文的报头:
包含4bit广播报文类型、2bit保留位、1bit发送地址类型和1bit接收地址类型。
广播报文类型:
发送地址类型:
0: 公共地址
1:随机地址
长度:广播报文的长度域包含8个比特,有效值的范围是6~37
数据: 广播者地址(6个字节)+广播数据(31个字节)
校验: 3个字节,为CRC校验。
广播数据: 分为有效数据和无效数据
有效数据部分:
包含N个AD Structure,每个AD Structure由Length,AD Type和AD Data组成。其中:
Length: AD Type和AD Data的长度。
AD Type: 指示AD Data数据的含义。
详见https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/
六、 BLE连接建立过程
1. BLE广播与扫描
设备B不断发送广播信号给手机(Observer),如果手机不开启扫描窗口,手机是收不到设备B的广播的,如下图所示,不仅手机要开启射频接收窗口,而且只有手机的射频接收窗口跟广播发送的发射窗口匹配成功,而且广播射频通道和手机扫描射频通道是同一个通道,手机才能收到设备B的广播信号。也就是说,如果设备B在37通道发送广播包,而手机在扫描38通道,那么即使他们俩的射频窗口匹配,两者也是无法进行通信的。由于这种匹配成功是一个概率事件,因此手机扫到设备B也是一个概率事件,也就是说,手机有时会很快扫到设备B,比如只需要一个广播事件,手机有时又会很慢才能扫到设备B,比如需要10个广播事件甚至更多。
2. 建立连接(connection establishment)
根据蓝牙spec规定,advertiser发送完一个广播包之后150us(T_IFS),advertiser必须开启一段时间的射频Rx窗口,以接收来自observer的数据包。Observer就可以在这段时间里给advertiser发送连接请求。如下图所示,手机在第三个广播事件的时候扫到了设备B,并发出了连接请求CONN_REQ(CONN_REQ又称为CONNECT_IND)。
如图所示,手机在收到A1广播包ADV_IND后,以此为初始锚点(这个锚点不是连接的锚点),T_IFS时间后给Advertiser发送一个connection request命令,即A2数据包,告诉advertiser我将要过来连你,请做好准备。Advertiser根据connect_req命令信息做好接收准备,connect_req包含如下关键信息:
- Transmit window offset,定义如上图示
- Transmit window size,定义如上图所示
-
connect_req数据包完整定义如下所示
connect_req其实是在告诉advertiser,手机将在Transmit Window期间发送第一个同步包(P1)给你,请在这段时间里把你的射频接收窗口打开。设备B收到P1后,T_IFS时间后将给手机回复数据包P2(ACK包)。
一旦手机收到数据包P2,连接即可认为建立成功。当然,实际情况会比较复杂,手机有可能收不到P2,这个时候手机将持续发送同步包直到超时时间(supervision timeout)到,在此期间只要设备B回过一次ACK包,连接即算成功。所以一旦P1包发出,主机(手机)即认为连接成功,而不管有没有收到设备的ACK包。
这也是为什么在Android或者iOS系统中,应用经常收到连接成功的回调事件(该回调事件就是基于P1包有没有发出,只要P1包发出,手机即认为连接成功,而不管有没有收到设备的ACK包),但实际上手机和设备并没有成功建立连接。
后续手机将以P1为锚点(原点),Connection Interval为周期,周期性地给设备B发送数据包(Packet),Packet除了充当数据传送功能,它还有如下两个非常重要的功能:
同步手机和设备的时钟,也就是说,设备每收到手机发来的一个包,都会把自己的时序原点重新设置,以跟手机同步。
告诉设备你现在可以传数据给我了。连接成功后,BLE通信将变成主从模式,因此把连接发起者(手机)称为Master或者Central,把被连接者(之前的Advertiser)称为Slave或者Peripheral。
BLE通信之所以为主从模式,是因为Slave不能“随性”给Master发信息,它只有等到Master给它发了一个packet后,然后才能在规定的时间把自己的数据回传给Master。
3. 连接失败
有如下几种典型的连接失败情况:
-
如步骤2图所示,如果slave在transmit window期间没有收到master发过来的P1,那么连接将会失败。此时应该排查master那边的问题,看看master为什么没有在约定的时间把P1发出来。
-
如果master在transmit window期间把P1发出来了,也就是说master按照connect_req约定的时序把P1发出来了,但slave没有把P2回过去或者没有在超时时间内把P2回过去,那么连接也会失败。此时应该排查slave这边的问题,看一看slave为什么没有把P2回过去
-
如果master把P1发出来了,slave也把P2回过去了,此时主机或者从机还是报连接失败,这种情况有可能是软件有问题,需要仔细排查master或者slave的软件。
- 还有一种比较常见的连接失败情况:空中射频干扰太大。此时应该找一个干净的环境,比如屏蔽室,排除干扰后再去测试连接是否正常。
七、帧格式
1、数据链路层报文结构
报文的基础是数据链路层的报文,其它报文都是从此展开的,BLE数据链路层数据格式如下:
在最新的core spec 5.2中,有1M PHY和2M的PHY,对应前导符变为1-2个字节。前导符用于频率同步、时序评估和自动增益控制训练。前导符第一bit应该与接入地址的LSB相同。
字段解析:
发送地址( TXADD)和接收地址( RXADD): 当此位为“1”时表示 Random Add(随机地址),当此位为“0”时表示 Public Add(公共地址)。 这个地址指的是数据净荷中最初的几个地址字节。
净荷长度: 这个长度是指在 PDU 中的数据除去报头和长度之外的有效净荷数 据长度。
2、 广播通道与数据通道 PDU 区别:
广播通道的 PDU 格式:
字段解析:
发送地址( TXADD)和接收地址( RXADD): 当此位为“1”时表示 Random Add (随机地址),当此位为“0”时表示 Public Add(公共地址)。 这个地址指的是数据净荷中最初的几个地址字节。
净荷长度: 这个长度是指在 PDU 中的数据除去报头和长度之外的有效净荷数据长度。
数据通道的 PDU 格式:
字段释义:
LLID:
表示此包数据是 LL Date PDU 还是 LL Control PDU
MIC( Message Integrity Check):
信息完整性检测。涉及到加密操作,上图中是用虚线表示的,并不是一定要有此项。
MD:
这个标志位是用来通知对方设备自己还有其他数据准备发送。0 表示没有更多数据发送, 1 表示有更多数据准备发送。这样,只要还有数据需要发送,连接事件会自动扩展。一旦不再有数据发送,连接事件立即关闭。
Note:如何区分是确定包、新包还是重发包?
SN:
只有一个 bit 位,所以值是在 0 和 1 之间进行切换。如果序列号与之前的一样,则为重传报文,如果序列号和之间的不同,则为新报文。
NESN:
预期序列号,它是接收方希望接到的下一包的序列号,也就是数据包的确认标志。当设备接收到序列(SN)为 0 的报文后,在发送给对方的数据包中,应将 NESN 设为 1,这样对方接收到这个包后,会发送一个新的数据包过来,否则就会重发上一次序列号为 0 的包。这个标志可以用来判断数据包是否被正确接收还是需要重传。
3、 BLE 报文格式
4、 AD Structure 解析
(1) AD type
(2) AD data 简述
Flags:
SERVICE:
Local Name:
TX Power Level:
八、代码示例
本例以OSAL下BLE代码为例做讲解。
什么是OSAL?
OSAL为:Operating System Abstraction Layer,即操作系统抽象层,支持多任务运行,它并不是一个传统意义上的操作系统,但是实现了部分类似操作系统的功能。
OSAL概念是由TI公司在ZIGBEE协议栈引入,他的意思是”模拟操作系统”,此OS,并非一个真正的OS,而是模拟OS的一些方法为广大编程者提供一种写MCU程序的方法。当有一个事件发生的时候,OSAL负责将此事件分配给能够处理此事件的任务,然后此任务判断事件的类型,调用相应的事件处理程序进行处理。
实验平台
代码解析
- int main(void)
int main(void)
{
/* Initialize hardware */
HAL_BOARD_INIT();//初始化时钟和使能缓存预取模式
// Initialize board I/O
InitBoard( OB_COLD );//冷启动,关闭了led灯与中断,避免接下来的各种初始化受干扰
/* Initialze the HAL driver */
HalDriverInit();//各种驱动的初始化、如按键、lcd、adc、usb、uart等8
/* Initialize NV system */
osal_snv_init();//snv 内部用于保存配对数据或你的用户自定义数据的一段flash,4kB空间
/* Initialize LL */
/* Initialize the operating system */
osal_init_system();//oasl 操作系统初始化, 包含内存分配、消息队列、定时器、电源管理和任务等
/* Enable interrupts */
HAL_ENABLE_INTERRUPTS();// 开启全局中断
// Final board initialization
InitBoard( OB_READY ); //设置标志标示系统初始化完毕
}
- osal_init_system()
uint8 osal_init_system( void )
{
// Initialize the Memory Allocation System
osal_mem_init();//初始化内存分配系统
// Initialize the message queue
osal_qHead = NULL;//初始化消息队列
// Initialize the timers
osalTimerInit();//初始化定时器
// Initialize the Power Management System
osal_pwrmgr_init();//初始化电源管理系统
// Initialize the system tasks.
osalInitTasks();//初始化系统任务, 这一个任务初始花非常关键
// Setup efficient search for the first free block of heap.
osal_mem_kick();
return ( SUCCESS );
}
- osalInitTasks()
void osalInitTasks( void )
{
/* L2CAP Task */
L2CAP_Init( taskID++ );
/* GAP Task */
GAP_Init( taskID++ );
/* GATT Task */
GATT_Init( taskID++ );
/* SM Task */
SM_Init( taskID++ );
/* Profiles */
GAPRole_Init( taskID++ ); //链路角色初始化
GAPBondMgr_Init( taskID++ ); //链路绑定初始化
GATTServApp_Init( taskID++ );
/* Application */
SimpleBLEPeripheral_Init( taskID );
}
- GAPRole_Init( taskID++ )
void GAPRole_Init( uint8 task_id )
{
gapRole_TaskID = task_id; //定义任务地址
gapRole_state = GAPROLE_INIT; //链路状态设置为GAPROLE_INIT
gapRole_ConnectionHandle = INVALID_CONNHANDLE; //设置链路连接句柄为0xFFFF
GAP_RegisterForHCIMsgs( gapRole_TaskID );//注册控制接口的任务ID
// Initialize the Profile Advertising and Connection Parameters
gapRole_profileRole = GAP_PROFILE_PERIPHERAL; //链路配置角色为从机
VOID osal_memset( gapRole_IRK, 0, KEYLEN ); //密钥缓冲器清零
VOID osal_memset( gapRole_SRK, 0, KEYLEN );
gapRole_signCounter = 0; //密钥计数标志位清零
gapRole_AdvEventType = GAP_ADTYPE_ADV_IND; //广播类型为可连接无定向广播
gapRole_AdvDirectType = ADDRTYPE_PUBLIC; //广播方式为通过广播(可被发现扫描连接)
gapRole_AdvChanMap = GAP_ADVCHAN_ALL ; //广播所有通道37、38、39
gapRole_AdvFilterPolicy = GAP_FILTER_POLICY_ALL; //允许扫描,允许连接
// Restore Items from NV
VOID osal_snv_read( BLE_NVID_IRK, KEYLEN, gapRole_IRK ); //读出存储的密钥和密钥计数标志位
VOID osal_snv_read( BLE_NVID_CSRK, KEYLEN, gapRole_SRK );
VOID osal_snv_read( BLE_NVID_SIGNCOUNTER, sizeof( uint32 ), &gapRole_signCounter );
}
- SimpleBLEPeripheral_Init
//初始化完成后
void SimpleBLEPeripheral_Init( uint8 task_id )
{
osal_set_event( simpleBLEPeripheral_TaskID, SBP_START_DEVICE_EVT ); //启动设备开始事件
}
- SimpleBLEPeripheral_ProcessEvent
uint16 SimpleBLEPeripheral_ProcessEvent( uint8 task_id, uint16 events )
{
if ( events & SBP_START_DEVICE_EVT )// 初始化后就执行这个啦
{
// Start the Device
VOID GAPRole_StartDevice( &simpleBLEPeripheral_PeripheralCBs ); //配置链路事件通知回调函数
// Start Bond Manager
VOID GAPBondMgr_Register( &simpleBLEPeripheral_BondMgrCBs ); //配置配对消息回调函数
// Set timer for first periodic event
osal_start_timerEx( simpleBLEPeripheral_TaskID, POWER_DETECT_EVT, DetectPowerPeriod );
return ( events ^ SBP_START_DEVICE_EVT );
}
}
- GAPRole_StartDevice
Status_t GAPRole_StartDevice( gapRolesCBs_t *pAppCallbacks )
{
if ( gapRole_state == GAPROLE_INIT ) //如果链路状态是初始化状态
{
// Clear all of the Application callbacks
if ( pAppCallbacks )
{
pGapRoles_AppCGs = pAppCallbacks;//设置回调函数
}
// Start the GAP
gapRole_SetupGAP();//开始建立链路
return ( SUCCESS );
}
else //否则返回已经在请求模式状态
{
return ( bleAlreadyInRequestedMode );
}
}
- gapRole_SetupGAP
static void gapRole_SetupGAP( void )
{
VOID GAP_DeviceInit( gapRole_TaskID,
gapRole_profileRole, 0,
gapRole_IRK, gapRole_SRK,
&gapRole_signCounter );
}
- GAP_DeviceInit
bStatus_t GAP_DeviceInit( uint8 taskID,
uint8 profileRole,
uint8 maxScanResponses,
uint8 *pIRK,
uint8 *pSRK,
uint32 *pSignCounter )
{
// Setup the device configuration parameters
stat = GAP_ParamsInit( taskID, profileRole ); //设置设备配置参数
#if ( HOST_CONFIG & ( CENTRAL_CFG | PERIPHERAL_CFG ) )
{
GAP_SecParamsInit( pIRK, pSRK, pSignCounter );
}
#endif
#if ( HOST_CONFIG & ( PERIPHERAL_CFG | BROADCASTER_CFG ) )
{
// Initialize GAP Peripheral Device Manager
VOID GAP_PeriDevMgrInit(); //初始化从机设备管理
#if ( HOST_CONFIG & PERIPHERAL_CFG )
{
// Initialize SM Responder
VOID SM_ResponderInit(); //回应者初始化
}
#endif
}
#endif
}
-
当GAP_DeviceInit初始化完成后,将产生GAP_DEVICE_INIT_DONE_EVENT事件;
uint16 GAPRole_ProcessEvent( uint8 task_id, uint16 events ) //链路处理事件 -
static void gapRole_ProcessOSALMsg( osal_event_hdr_t *pMsg ) //链路系统消息事件
- gapRole_ProcessGAPMsg
static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg ) //链路处理连接消息
{
uint8 notify = FALSE; // State changed notify the app? (default no)
switch ( pMsg->opcode )
{
case GAP_DEVICE_INIT_DONE_EVENT: //当GAP_DeviceInit初始化完成后,将产生此事件
{
gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *)pMsg;
bStatus_t stat = pPkt->hdr.status;
if ( stat == SUCCESS )
{
// Save off the generated keys
VOID osal_snv_write( BLE_NVID_IRK, KEYLEN, gapRole_IRK );//保存生成的密钥
VOID osal_snv_write( BLE_NVID_CSRK, KEYLEN, gapRole_SRK );
// Save off the information
VOID osal_memcpy( gapRole_bdAddr, pPkt->devAddr, B_ADDR_LEN );//保存设备地址
gapRole_state = GAPROLE_STARTED; //链路开始
// Update the advertising data
stat = GAP_UpdateAdvertisingData( gapRole_TaskID,//更新广播数据
TRUE, gapRole_AdvertDataLen, gapRole_AdvertData );
}
notify = TRUE; //通知回调函数链路的状态
}
break;
if ( notify == TRUE )
{
// Notify the application with the new state change
if ( pGapRoles_AppCGs && pGapRoles_AppCGs->pfnStateChange ) //判断是否设置了回调函数
{
pGapRoles_AppCGs->pfnStateChange( gapRole_state );//调用设置的回调函数,通知gapRole_state当前状态
}
}
}
-
stat=GAP_UpdateAdvertisingData( gapRole_TaskID,TRUE, gapRole_AdvertDataLen, gapRole_AdvertData );//更新广播数据后,将产生GAP_ADV_DATA_UPDATE_DONE_EVENT事件;
- gapRole_ProcessGAPMsg
static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg ) //链路处理连接消息
{
uint8 notify = FALSE; // State changed notify the app? (default no)
switch ( pMsg->opcode )
{
case GAP_ADV_DATA_UPDATE_DONE_EVENT:
{
gapAdvDataUpdateEvent_t *pPkt = (gapAdvDataUpdateEvent_t *)pMsg;
if ( pPkt->hdr.status == SUCCESS )
{
if ( pPkt->adType )
{
// Setup the Response Data
pPkt->hdr.status = GAP_UpdateAdvertisingData( gapRole_TaskID,
FALSE, gapRole_ScanRspDataLen, gapRole_ScanRspData );//更新扫描回应数据
}
else
{
// Start advertising
VOID osal_set_event( gapRole_TaskID, START_ADVERTISING_EVT ); //启动广播事件
}
}
if ( pPkt->hdr.status != SUCCESS ) //如果不成功将通知回调函数,否则不通知
{
// Set into Error state
gapRole_state = GAPROLE_ERROR;
notify = TRUE;
}
}
break;
- gapRole_ProcessGAPMsg
static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg ) //链路处理连接消息
{
uint8 notify = FALSE; // State changed notify the app? (default no)
switch ( pMsg->opcode )
{
case GAP_ADV_DATA_UPDATE_DONE_EVENT:
{
gapAdvDataUpdateEvent_t *pPkt = (gapAdvDataUpdateEvent_t *)pMsg;
if ( pPkt->hdr.status == SUCCESS )
{
if ( pPkt->adType )
{
// Setup the Response Data
pPkt->hdr.status = GAP_UpdateAdvertisingData( gapRole_TaskID,
FALSE, gapRole_ScanRspDataLen, gapRole_ScanRspData );//更新扫描回应数据
}
else
{
// Start advertising
VOID osal_set_event( gapRole_TaskID, START_ADVERTISING_EVT ); //启动广播事件
}
}
if ( pPkt->hdr.status != SUCCESS ) //如果不成功将通知回调函数,否则不通知
{
// Set into Error state
gapRole_state = GAPROLE_ERROR;
notify = TRUE;
}
}
break;
- 执行广播事件
uint16 GAPRole_ProcessEvent( uint8 task_id, uint16 events )
{
VOID task_id; // OSAL required parameter that isn\'t used in this function
if ( events & START_ADVERTISING_EVT )
{
if ( gapRole_AdvEnabled )
{
gapAdvertisingParams_t params;
// Setup advertisement parameters
params.eventType = gapRole_AdvEventType; //GAP_ADTYPE_ADV_IND; 广播类型为可连接无定向广播
params.initiatorAddrType = gapRole_AdvDirectType; //ADDRTYPE_PUBLIC; 广播方式为通用广播
VOID osal_memcpy( params.initiatorAddr, gapRole_AdvDirectAddr, B_ADDR_LEN ); //发起者地址配置
params.channelMap = gapRole_AdvChanMap; //广播通道配置:广播所有通道37、38、39
params.filterPolicy = gapRole_AdvFilterPolicy;//过滤策略GAP_FILTER_POLICY_ALL;允许扫描,允许连接
if ( GAP_MakeDiscoverable( gapRole_TaskID, ¶ms ) != SUCCESS ) //配置广播参数,并产生一个GAP_MakeDiscoverable 消息事件
{
gapRole_state = GAPROLE_ERROR;//如果不成功将通知回调函数-链路错误
// Notify the application with the new state change
if ( pGapRoles_AppCGs && pGapRoles_AppCGs->pfnStateChange )
{
pGapRoles_AppCGs->pfnStateChange( gapRole_state );
}
}
}
return ( events ^ START_ADVERTISING_EVT );
}
- 处理GAP_MakeDiscoverable消息事件
static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg ) //链路处理连接消息
{
uint8 notify = FALSE; // State changed notify the app? (default no)
switch ( pMsg->opcode )
{
case GAP_MAKE_DISCOVERABLE_DONE_EVENT: //使能可被发现完成事件即开始广播了
case GAP_END_DISCOVERABLE_DONE_EVENT: //结束可被发现完成事件即停止广播了
{
gapMakeDiscoverableRspEvent_t *pPkt = (gapMakeDiscoverableRspEvent_t *)pMsg;
if ( pPkt->hdr.status == SUCCESS )
{
if ( pMsg->opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT )
{
gapRole_state = GAPROLE_ADVERTISING; //设置当前链路状态
}
else // GAP_END_DISCOVERABLE_DONE_EVENT//结束可被发现完成事件即停止广播了
{
if ( gapRole_AdvertOffTime != 0 ) //如果gapRole_AdvertOffTime等于0,将不再广播,否则启动定时广播件
{
if ( ( gapRole_AdvEnabled ) )//如果使能广播
{
VOID osal_start_timerEx( gapRole_TaskID, START_ADVERTISING_EVT, gapRole_AdvertOffTime );//启动周期广播事件
}
}
else
{
// Since gapRole_AdvertOffTime is set to 0, the device should not
// automatically become discoverable again after a period of time.
// Set enabler to FALSE; device will become discoverable again when
// this value gets set to TRUE
gapRole_AdvEnabled = FALSE;
}
// In the Advertising Off period
gapRole_state = GAPROLE_WAITING;//如果GAP_END_DISCOVERABLE_DONE_EVENT,链路当前状态为等待状态
}
}
else
{
gapRole_state = GAPROLE_ERROR;
}
notify = TRUE;//通知回调函数
}
break;
if ( notify == TRUE )
{
// Notify the application with the new state change
if ( pGapRoles_AppCGs && pGapRoles_AppCGs->pfnStateChange ) //判断是否设置了回调函数
{
pGapRoles_AppCGs->pfnStateChange( gapRole_state );//调用设置的回调函数,通知gapRole_state当前状态
}
}
-
这时候底层已经使能硬件在广播了,要么广播超时产生一个GAP_END_DISCOVERABLE_DONE_EVENT消息,要么被连接事件 GAP_LINK_ESTABLISHED_EVENT;
- 广播超时产生一个GAP_END_DISCOVERABLE_DONE_EVENT消息
static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg ) //链路处理连接消息
{
uint8 notify = FALSE; // State changed notify the app? (default no)
switch ( pMsg->opcode )
{
case GAP_MAKE_DISCOVERABLE_DONE_EVENT: //使能可被发现完成事件即开始广播了
case GAP_END_DISCOVERABLE_DONE_EVENT: //结束可被发现完成事件即停止广播了
{
gapMakeDiscoverableRspEvent_t *pPkt = (gapMakeDiscoverableRspEvent_t *)pMsg;
if ( pPkt->hdr.status == SUCCESS )
{
if ( pMsg->opcode == GAP_MAKE_DISCOVERABLE_DONE_EVENT )
{
gapRole_state = GAPROLE_ADVERTISING; //设置当前链路状态
}
else // GAP_END_DISCOVERABLE_DONE_EVENT//结束可被发现完成事件即停止广播了
{
if ( gapRole_AdvertOffTime != 0 ) //如果gapRole_AdvertOffTime不等于0,启动定时广播事件,否则将关闭广播
{
if ( ( gapRole_AdvEnabled ) )//如果使能广播
{
VOID osal_start_timerEx( gapRole_TaskID, START_ADVERTISING_EVT, gapRole_AdvertOffTime );//启动周期广播事件
}
}
else
{
// Since gapRole_AdvertOffTime is set to 0, the device should not
// automatically become discoverable again after a period of time.
// Set enabler to FALSE; device will become discoverable again when
// this value gets set to TRUE
gapRole_AdvEnabled = FALSE; //关闭广播
}
// In the Advertising Off period
gapRole_state = GAPROLE_WAITING;//如果GAP_END_DISCOVERABLE_DONE_EVENT,链路当前状态为等待状态,或不再广播或等待周期广播
}
}
else
{
gapRole_state = GAPROLE_ERROR;
}
notify = TRUE;//通知回调函数
}
break;
if ( notify == TRUE )
{
// Notify the application with the new state change
if ( pGapRoles_AppCGs && pGapRoles_AppCGs->pfnStateChange ) //判断是否设置了回调函数
{
pGapRoles_AppCGs->pfnStateChange( gapRole_state );//调用设置的回调函数,通知gapRole_state当前状态
}
}
- 广播时产生一个GAP_LINK_ESTABLISHED_EVENT消息
static void gapRole_ProcessGAPMsg( gapEventHdr_t *pMsg ) //链路处理连接消息
{
uint8 notify = FALSE; // State changed notify the app? (default no)
switch ( pMsg->opcode )
{
case GAP_LINK_ESTABLISHED_EVENT:
{
gapEstLinkReqEvent_t *pPkt = (gapEstLinkReqEvent_t *)pMsg;
if ( pPkt->hdr.status == SUCCESS )
{
VOID osal_memcpy( gapRole_ConnectedDevAddr, pPkt->devAddr, B_ADDR_LEN );//保存主机的地址
gapRole_ConnectionHandle = pPkt->connectionHandle; //保存主机连接句柄
gapRole_state = GAPROLE_CONNECTED; //通知链路状态:连接成功
notify = TRUE;
}
}
}
以上是关于Bluetooth BR/EDR和BLE的区别.md的主要内容,如果未能解决你的问题,请参考以下文章