芯片介绍
xxAA:片内 Flash 512K字节,400KB 的Flash可以用于应用程序,片内RAM 64K
字节。xxAB:片内 Flash 256K字节,超过100KB 的 Flash可以用于应用程序,片内RAM
32K字节。
蓝牙版本
- 蓝牙 4.2 提升了 BLE 数据传输速度,支持长包传输,单个数据包最大可传输 255 个字
节。同时改善了隐私保护程度。 - 广播扩展:蓝牙BLE 有3个广播信道,之前的蓝牙BLE 版本中广播载荷通过全部
三个广播通道进行发送,蓝牙 5.0中这三个通道仅用于发送指针,显示发送载荷的
时间和地点,广播载荷仅在一个数据通道上传输一次。
BLE体系结构

备注:链路层下面还有个物理层。
GAP:配置怎么来访问BLE的,包括配置广播的名字,连接首选项参数。GAP 层负责处理设备的接入方式和过程,包括设备发现,链路建立,链路终止以及实现绑定。
GATT:配置文件,建立在ATT上,通过ATT定义如何发现、读写服务、特征、描述符的方法。GATT层定义了服务器和客户端。
ATT:服务属性、特征,规定怎么去服务器端读属性,供给客户端进行读写。
SM:是否设置配对密码等安全相关的
以下底层:蓝牙协议栈内部管理。
LL:负责广播、扫描、建立连接和维护连接,确保数据的发送接收准确。本层有一个状态机。
注意:
1.主机和从机是链路层的叫法,服务器和客户端是GATT层的叫法,主机可以是服务器也可以是客户端,从机也一样。
2.外围设备、中心设备是GAP层的叫法。
应用层定义了三种种类型:特征(characteristic)、服务(service)和配置文件(profile)。
1.特征:以通用唯一识别码(UUID)作为表示的一个小块数据,可以被重复使用,不涉及行为。当计算机遇到一个从未接触过的特征时,计算机可以去更新这个特征的读取规则。
2.服务:人类可读的一组特征及其相关的行为规范,只定义了位于服务器上的相关特性和行为,而不定义客户端的行为。服务有两种类型,首要服务和次要服务。次要服务是那些协助主要
业务或其他次要服务的服务。3.配置文件:是用例或应用的最终体现。配置文件描述了如何发现和连接设备,配置文件还描述的客户端的行为,用于发现服务和服务特性。

1.扫描到白名单后,进入就绪态,在进入发起态,再变为连接态(不是扫描态直接就进入发起态)。
2.只有连接太会用到数据信道,但是如果是广播扩展广播包应该也是会用到数据信道的。
3.BLE芯片上电,链路层首先一定是进入就绪态,然后在进入到广播或者是扫描态。
4.这是是LL层的状态机,不是整个协议栈的状态机。
广播
- 每次广播的数据都会在37、38、39信道一次发送,这三个专门用于广播的信道中心频率至少相差24MHZ,可以避免三个信道同时被干扰的概率。
- BLE广播间隔:范围在20ms-10.24S,链路层会在每次广播时间期间产生一个随机广播延时时间(0ms-10ms)。
扫描事件
- 扫描窗口(scan window):一次扫描进行的时间宽度。
- 扫描间隔(scan interval):两个连续的扫描窗口的起始时间之间的时间差,包括扫描休息的时间和扫描进行的时间。
连接事件
- 主从设备互相发送数据的过程。
- 所有的数据交换都是通过连接事件完成。
- 0-36通道
- 无论有无数据,连接事件都在周而复始的进行,直到一方停止响应。
- 主机与从机可在单次连接事件进行多次数据传输。
连接参数
连接间隔:必须是 1.25ms 的倍数,范围是从最小值 6(7.5ms)- 3200(4.0s)
从机延迟:可以选择跳过连接事件,并保持睡眠,这一决定取决于外围设备。
监督超时:这是两个成功的连接事件之间间隔的最大值。如果超过这个时间还未出现成
功的连接事件,那么设备将会考虑失去连接,返回一个未连接状态。10ms 的步进(10ms 的倍数),10(100ms)- 3200(32.0s)
profile
profile是 Service 的集合,它是预定义的,并不是实际存在于设备中。
Characteristic
特征是具有特定意义的数值,如心率、温度值等等。BLE 主从机之间的数据传输实际
传输的就是特征值。
UUID
UUID(Universally Unique Identifier)是一个 128位的数字,用来标志属性的类型。Service
和Characteristic都是一种属性,都需要一个唯一的UUID 来标识。
GAP
模式(Mode):模式描述是设备的工作状态,当一个设备被配置为按照某种方式操作一
段较长的时间时,称为模式。如广播模式,表示设备正处于广播状态,一般会持续很长
时间。规程(Procedure):规程描述的是在有限的时间内进行特定的操作,如连接参数更新规
程,它是在较短的时间内执行了连接参数更新的操作。GAP角色:广播者、观察者、外围设备、中心设备。
广播者:广播发送者,不是可连接的设备。
观察者:扫描广播,不能够启动连接。
外围设备:广播发送者,是可连接的设备,连接后成为从设备。
中心设备:扫描广播启动连接,连接后成为主设备。
GAP服务:设备名字、外观特征、外围设备首选连接参数、中心设备地址解析。
Device Name特征
Appearance特征 :蓝牙小图标
PPCP特征 :外围设备首选连接参数,在主从连接后外围设备发送给中心设备
Central Address Resolution特征 ,0 该设备不支持地址解析。1 该设备支持地址解析。
Resolvable Private Address Only特征 ,检查对端设备是否仅在绑定后使用可解析私有地址(RPA)
GATT
- GATT 程序模块的作用是用于协商和跟踪 GATT 连接参数和更新数据长度
- 特征值的排队写入nrf_ble_qwr_init()
从机更新连接参数
主从机奖励连接后,从机发起更新连接参数
从机和主机刚建立连接时,会使用“快速”的连接参数以达到迅速交换信息的目的
主机发起的叫请求,从机只有接受断开连接。从机发起的叫协商,主机可以接收或者拒绝。
广播报文结构
字节序:大多数多字节域是从低字节开始传输的。注意,并不是所有的多字节域都是从
低字节开始传输的。比特序:各个字节传输时,每个字节都是从低位开始。
设备地址:
1.静态地址(可使用芯片固化的,或者自己在上电时设置),最高两位必须为1
2.不可解析私有地址:周期变化的地址,最高2bit必须为0。
3.可解析设备地址:最高2bit必须是0和1。
Flags :
1.有限可发现模式。
- 一般可发现模式。
- 不支持BR/EDR。
- 设备同时支持LE 和BR/EDR(控制器)。
- 设备同时支持LE 和BR/EDR(主机)。
一个处于有限可发现模式的设备正在广播,那么他一定是刚被用户操作过并且极希
望被连接。
发射功率等级
发射功率等级可以用来计算路径损耗,计算公式如下:
pathloss = Tx Power Level – RSSI。例:发射功率+4db,扫描到的RSSI是-40db,则路径损耗为:4 - (-40)= 44db
从机广播自己的发射功率,主机可以结合RSSI和从机的发射工具计算路径损耗,判断哪个设备离自己近。
- UUID分为标准的 UUID和厂商自定义的 UUID
UUID全部是128位的,UUID有个128位的基数,通过将16位的UUID和UUID基数合并都到最终的UUID。

使用16位的uuid目的是为了,BLE传输更少的数据,提高效率。
- uuid类型分类
0x1800 ~ 0x26FF:用作服务类通用唯一识别码。
0x2700 ~ 0x27FF:用于标识计量单位。
0x2800 ~ 0x28FF:用于区分属性类型。
0x2900 ~ 0x29FF:用作特征描述。
0x2A00 ~ 0x7FFF:用于区分特征类型。
广播服务数据(例如电池电量)
广播连接范围,用于给中心设备做参考
**广播厂商自定义数据 **
APP定时器
1 | typedef struct |
profile
标准配置文件和自定义配置文件

Service(服务)
1.服务是一组特征和通过它们所公开的行为的集合,一个服务可以包含多个特征。如心率服务包含Heart Rate measurement、 Body Sensor Location和Heart Rate Control Point这些特征。
2.服务也和 Profile 一样,分为标准服务和自定义服务。
3.服务存在于从机中,每个服务代表从机的一个能力。
4.porfile规定必须包含哪些服务,哪些服务是可选的。

心率porfile必须包含:心率服务、设备信息服务,其他的一些服务是可选的。
Characteristic(特征)
特征包含三个部分:声明、数值和描述符,其中声明和数值是必不可少的,而描述符可
以是一个或多个。

对于特征来说,要建立一个特征,首先要进行声明,如下图所示:
- 特征性质
特征性质是一个 8位字段


- 特征数值
特征数值是一个属性,特征性质字段给出了特征数值属性的访问权限,如读/写等等,
通过特征性质字段的描述,确定了特征数值属性可以执行的操作类型。
- 描述符
特征的描述符大多数描述符是可选的,一个特征可以包含 0 到多个描述符,需要注意的
是如果使用了通知(notify)或指示(indicate),必须要有客户端特征配置描述符(Client
Characteristic Configuration Descriptor,CCCD),CCCD的UUID是0x2902。
- 角色
心率 Profile 定义了如下图所示的两类角色:心率传感器和集中器(Collector),心率传
感器是测量心率和其它信息的设备,集中器是接收心率传感器心率测量值和其它信息的设备。
1.心率传感器应是 GATT服务器。
2.集中器应是GATT客户端。

- 数据传输
BLE的数据传输是双向的:从机主动向主机发起数据传输和主机向从机发起数据传输。
从机主动发起
从机如果想主动发起数据传输,只能通过两种方式:通知(Notify)和指示(Indicate)
1.通知(Notify) :从机发送通知后,不会关心主机有没有接收到,通知属于不可靠消息。
通知需要使用客户端配置描述符(CCCD)配置(CCCD使能/关闭通知)。
2.指示(Indicate) :从机发送指示后,必须得到主机的应答才能发送下一条指示,指示属
于可靠消息。指示同样需要使用客户端配置描述符(CCCD)配置(CCCD 使能/关闭指示)。
主机主动发起
主机如果想主动发起数据传输,也有两种方式:读和写,通过读和写,主机可以读/写
特征值和特征描述符。
1.读(Read) :主机通过“读”可以读取从机的特征值和特征描述符,从而获取从机的数
据。
2.写(Write) :主机通过“写”可以写入从机的特征值和特征描述符,从而将数据发送给
从机。

主机通过读写从机的特征描述符来使能/关闭是否接收主机发来的通知或指示。

- 心率测量特征通知的数据格式

profile小结:
1.profile规定了两个角色:收集器(客户端)、传感器(服务端),传感器包含一个或多个服务,服务里面又包含特征、描述符。
2.收集器可以读写传感器的特征值和描述符。
3.传感器可以通过通知或者指示,主动给收集器发送数据。
4.收集器可以通过读写特征的描述符,决定是否被动接收传感器主动发来的特征值。
5.多有描述符的uuid都为固定的0x2902。
主机:客户端、收集器。
从机:服务端、传感器。
通知是不可靠协议,不需要主机应答。指示是可靠协议,需要主机应答,才进入下一次指示。
相关API函数:
服务添加到属性表 :
1 | uint32_t sd_ble_gatts_service_add ( |

添加特征 :
1 | uint32_t characteristic_add ( |

句柄就是一个uit16_t的值。
- 添加特征主要是填充特征属性

特征值的长度:可变长度。特征值的长度可以设置为可变长度和固定长度,对于可变长度,需要设置最大长度 max_len、初始长度 init_len和可变长度标志 is_var_len(设置为true)。对于固定长度,将最大长度 max_len 和初始长度init_len设置为一样,可变长度标志is_var_len 设置为false即可。
- 添加gatt服务主要包括属性还有回调函数(方法)

服务中提供了一个回调函数,在相应事件来到时,就可以扩展很多自己要做的事件了。
- 发送数据

服务或者特征都是通过在创建时协议栈分配一个句柄(uint_16),发送数据的时候带上这个句柄,主机接收到数据就知道是哪个特征来的数据了。
- 特征值什么时候需要注册事件监视者?
1.心率服务需要发送和接收主机数据,需要知道当前主从是否已连接,所以心率服务要注册事件监视者(提供一个回调给协议栈)。调用函数:
1 | BLE_HRS_DEF(m_hrs); // 创建心率服务结构体,会注册一个回调给协议栈(注册监视者):void ble_hrs_on_ble_evt(ble_evt_t const *p_ble_evt, void *p_context) |
心率服务有读通知比较复杂,会创建一个结构体来标识服务。
2.设备信息服务只用响应主机的读,所以它不用注册事件监视者。只用创建自己的特征就行。调用函数:
1 | static uint16_t service_handle; |
设备信息服务只包含了一个只读的特征,对于这种简单的服务,不需要专门定义一个结构体来标志服务,只需要定义一个服务句柄变量来标志服务即可。
- 服务添加到属性表
服务通过协议栈 API 函数 sd_ble_gatts_service_add()加入到属性表,服务加入成功后,协议栈会给服务分配 16 位的句柄,该句柄会通过函数的输出参数保存到之前定义的“service_handle”变量中,之后,设备信息服务句柄即可用来标志设备信息服务。
- 添加特征
1 | uint32_t characteristic_add(uint16_t service_handle, // 添加服务时协议栈给服务分配的句柄 |
发送数据和传感器接触状态更新
BLE用户部分只有发送函数(通知或指示),没有接收函数,数据的接收是协议栈完成,协议栈接收到数据会向用户层提交数据内容相应的事件。
- 心率服务注册监视者的具体作用


实现一个自定义 profile
1.掌握如何实现长包传输(单次传输最大长度 247个字节,有效载荷 244个字节)。
2.自定义服务需要向Softdevice 写入128位的UUID 基数。
3.广播中如何增加扫描响应?为什么要增加扫描响应?

- 以128位为uuid基数和16位uuid合成新的uuid

对于广播中包含自定义服务 UUID,除了注意数据长度之外,还需要注意广播初始化中会用到自定义的UUID 的基数,而自定义UUID 的基数在是服务初始化时写入到 Softdevice的,因此服务初始化必须在广播初始化之前执行。
- 添加自定义profile的流程

1.自定义UUID profile流程和标志uuid流程基本一样:首先添加服务,再添加服务下的各个特征。只是在最开始需要将128位的uuid注册到协议栈,并设置宏告诉协议栈自定义uuid的个数。
2.注册观察者,回调函数中接收 BLE_GATTS_EVT_WRITE 写事件和 BLE_GATTS_EVT_HVN_TX_COMPLETE 发送完成事件(主机写事件会携带收到的数据上来)。
3.在以上两个事件中基础下,标记自己的业务事件,传递数据,拿到数据并做自己相关的业务处理。

- 将128位的uuid放到扫描响应包中广播
因为初始化广播的时候,广播扫描响应包要广播128位的自定义uuid,所以这时自定义uuid必须准备好(即先初始化服务准备好自定义uuid,再初始化广播,有先后顺序)。

实现长包传输
1.蓝牙4.2之前,BLE 的MTU(Maximum Transmission Unit,最大传输单元)为23 个字节,这 23 个字节中包含了 1 个字节的操作码(op code)和 2 个字节的属性句柄(attribute handle) ,因此一次最多传输 20个字节。
2.从蓝牙 4.2开始,BLE 支持长包传输,MTU 扩展到了247个字节,除去 op code和attribute handle,一次可传输的最大长度为 244个字节。
3.客户端和服务器是通过协商来交换各自支持的 MTU 的长度的,这个过程称为 MTU 交换(Exchange MTU),通过MTU交换,客户端和服务器具备了自适应MTU 的功能,但是注册每个建立的连接只能在连接建立时交换一次,而不能实时通过 MTU交换协商数据传输长度。
若服务器和客户端连接后没有进行MTU交换操作,默认MTU等于23字节。
- MTU交换步骤
1.客户端发送 MTU交换请求

2.服务器发送 MTU 交换响应

MTU 交换之后,客户端和服务器应将 MTU 设置为交换的最小值,并且客户端和服务器的MTU 应设置为相同的值,以确保客户端可以正确检测长属性读取的最终数据包。
- MTU交换流程


- 运行结果

- 蓝牙协议栈小结
从机常用API:
1 | // 请求使能 SoftDevice,该函数会根据 sdk_config.h 文件中低频时钟的设置来配置低频时钟 |
Flash 存储 FS

- FS的使用场景

FS优点:
- 简单明了,可以直接在指定的空闲 Flash空间上存储数据。
- 数据是“透明存储”的,数据的读写很直观,开发者容易理解。
- 裸机和使能了SoftDevice的情况下均可使用。
FS缺点:
- 用户需要自己指定数据在 Flash 中的存储位置,因此需要用户自己确定 Flash 的空闲空间。
- 数据是“透明存储”的,没有以记录的方式存储,也没有更新和检索功能,不便于管理。
- FS 没有对 Flash 擦写做平衡管理,而 nRF52832 的页面最大只能擦写 10000 次,因此FS 不能适应擦写频繁的应用。
- FS读写流程

- 本文作者: 龙兄嵌入式
- 本文链接: https://hexo.880755.xyz/2022/04/19/test/2022-04-19--蓝牙nRF52832/
