蓝牙BLE(协议栈、OSAL、蓝牙APP工具)
目录
-
蓝牙设备配对及连接
-
Bluetooth 4.0 (BLE)
-
信道 (RF Channel)
-
Bluetooth LE协议栈层次划分
-
- PHY 层 (Physical layer, 物理层)
- LL 层 (Link Layer, 链路层)
- HCI 层 (Host Controller Interface, 主机控制接口)
- L2CAP逻辑链路控制与自适应协议
- SMP安全管理协议
- GAP状态 (待机状态, 广播状态, 扫描状态, 连接状态, 主从状态) ———— 用于连接
- GAP连接请求参数
- GAP角色任务(参数配置API、广播内容、连接间隔设置、断开蓝牙操作及参数更新)
- GAP bond manager: GAP Bond Mgr(用于实现连接安全初始化)
- PHY 层 (Physical layer, 物理层)
-
属性协议层(Attribute protocol)
-
Service/Characteristics的UUID主从机通信机制 —— 用于实现通信(Service/Characteristic的UUID主从机通信)
-
顶层配置文件中的参数设置(顶层profile配置文件)
-
库文件中的功能模块(Lib)
-
外设驱动程序(外设驱动)
-
数据传输机制在蓝牙主机与从机间得以实现
-
该款芯片基于蓝牙5.0协议实现了数据远程采集功能
-
BLE开发环境搭建过程涉及哪些关键步骤?
- 系统启动阶段中对TASK初始化的过程主要涉及调用osal_start_system函数并执行初始化任务。
- 事件处理机制采用位运算方法,并规定每个events占用两个字节的空间存储最多16个事件的状态信息。在启动过程中(osal_start_system函数被调用),系统会自动运行相应的处理逻辑。
-
OSAL相关的API功能介绍
-
为处理特定事件设计的OSAL功能模块
-
负责内存管理和分配的OSAL核心组件
-
用于实现数据传输和通信的OSAL接口
-
其他支持系统功能调用的高级API接口
-
基于OSAL的UART实现研究
-
基于OSAL的主From机通信机制实现(增加功能特性)
-
实验观察现象
- 如何通过开机实现广播功能
- 主From机如何自动完成扫描过程
- 主From机如何进行设备接入操作
- 主From端串口数据接收与解析流程
- 主From端蓝牙属性读取及数据解析流程
- 主From端串口数据接收与传输流程
-
基于OSAL协议实现多特征值获取的句柄实验
-
基于OSAL协议的BLE技术进行了自动设备重连的测试与分析
-
实现了BLE设备绑定与配对功能的技术探讨
-
OSAL操作系统内BLE系统实现路径研究
-
对蓝牙协议栈运行过程中常见问题进行总结分析(主从模式、MAC地址规划、接收灵敏度)
-
IOS平台下的iBeacon协议实现及特性研究(UUID、Major、Minor、RSSI)
-
使用nrf Connect开发支持蓝牙功能的应用程序
-
以AT指令为基础编写操作手册的设计思路——Jdy-10M蓝牙模块使用指南
-
针对蓝牙数据传输格式设计了直观易懂的教学视频
-
探讨无线局域网中BLE报文结构特性及其传输机制
-
分析BLE连接建立及断开失效的根本原因机制研究
-
蓝牙官方资料下载地址(官方)
参考文献包括:
- 芯海厂家发布的产品《CST92F25 SDK开发指南V1.2》中涉及蓝牙BLE 5.0技术。
- 针对物联网领域的蓝牙4.0 BLE开发入门课程(视频教程)。
您可以通过以下链接获取详细信息:
https://www.bilibili.com/video/BV1qZ4y1W7dz?p=3&spm_id_from=pageDriver
配套学习资料包括《蓝牙4.0BLE开发完全手册》,该手册深入探讨物联网开发的实战技巧(共280页),并附带高清书签版电子文件(大小约31.5M)。
参考:BLE协议栈入门一(基本概念)
参考:浅谈BLE协议栈
参考:BLE协议栈详解
蓝牙配对和绑定
https://www.cnblogs.com/iini/p/12801242.html
蓝牙4.0 BLE
BLE别称为Bluetooth Low Energy(BLE),属于低功耗蓝牙技术的一种,在无线数据传输领域具有重要地位。从应用层面划分,蓝牙设备主要可分为普通蓝牙设备、智能蓝牙设备以及智能准备型蓝牙设备三大类。
- Bluetooth设备属于经典蓝牙技术设备(如无线耳机等),主要包含BR、EDR与AMP等多种技术参数;
- Bluetooth Smart系列则是以低功耗著称的BLE设备(如便携式温度测量仪等);
- Bluetooth Smart Ready系列则兼具传统与创新特性,在支持标准的同时提供高能效功能(如智能手机等)。
这三种设备的区别和联系如下图(箭头表示可以连接):


新蓝牙技术标准促进了IPv6协议在蓝牙领域的普及和发展,在这一框架下, 蓝牙4.2设备可直接连接至IPv6和6LoWPAN网络, 实现互联功能。
既然是无线芯片呢,其组成由软件和硬件构成。
- 软件部分基于 BLE 协议栈
- 硬件组成部分包括 RF PHY(无线通信收发模块)、Modem(调制解码模块)以及 Baseband(基带芯片组)构成
BLE的规范体系必须按照Core Spec进行定制或优化,并且Core Spec涵盖了RF模块、Modem组件以及Air Interface设计,并提供了数据编码与解码功能以及相关的软件协议规范。
信道(RF Channel)
蓝牙技术 Bluetooth 在全球标准组织 GSM 定义了其工作频率为 2.4 GHz,并采用四十个独立的无线电频谱通道将整个频段划分为多个子频道以提高效率。每个子频道能够覆盖范围为两米的空间区域,并且在同一时间段内仅能在一个无线电频谱通道中进行数据传输或接收操作。
40个RF信道上,并非平等对待;SIG将物理信道转换为一个被称为Channel Index的概念(实际上就是简单的对应关系):

物理信道从 0 - 39 进行编号。Index广播信道 :PHY Ch0 对应 37,PHY Ch12 对应 38,PHY 39 对应 39。

在BLE 4.2时代,在Ch Index的37/38/39通道上仅允许Advertising、Scanning及Initiating状态的数据收发操作。 Advertising操作旨在使其他BLE设备识别并发现当前设备; Scanning过程则用于探测其他可连接的设备;为了有效避免诸如WIFI等潜在干扰源,在与WIFI频段分离的专用通道上实现了 Advertising、Scanning及Initiating数据的接收与发送。

除了编号为 37、38 和 39 的这些频段外,在 Connection 状态下采用了 另外的 37 个通信信道通信信道 ,并利用跳频技术(Hopping)以降低数据干扰,并提高系统的可靠性。
BLE协议栈分层
- 协议:如同交流工具,在通信领域中扮演着基础角色的作用。例如,在不同设备之间建立通信连接时需要遵循统一的规则。
- 协议栈:即用于实现各种通信协议所需的代码模块或函数库。
- BLE 协议栈:整合蓝牙技术中各子层的具体通信协议,并以函数库的形式体现出来,并提供应用层的 API 供用户调用。
下图是协议栈的结构分层:
Bluetooth协议可分为主机(host)与控制器(controller)两部分。其中主机是真正意义的蓝牙协议主体部分;而控制器则对应于蓝牙的底层架构或基带芯片功能模块。
配置文件(Profiles)以及应用均建立在GAP与GATT的基础上进行构建。
在单一芯片方案架构中设计时,在同一片 silicon 集成电路中整合了控制器(controller)、主机(host)、配置文件(Profiles)以及应用层组件功能模块。


视频内容中提到的CC2540蓝牙BLE SOC芯片能够通过单芯片实现协议栈结构的全部组件,方便地构建应用程序。

PHY层(Physical layer 物理层)
该层用于配置BLE使用的无线频段(2.4 GHz),以及相关的调制解码技术与跳频策略。该层性能优越则会显著影响整个BLE芯片的功耗, 灵敏度以及selectivity等多个射频指标。
LL层(Link Layer 链路层)
主要涉及射频(RF)射频控制技术,在链路层层面负责规范协议栈中最基础的相关问题描述。具体而言,在协议栈架构中包含了最基础的状态机定义、数据包格式规范以及广播通信机制的建立等关键要素。
LL层在整个BLE协议栈中扮演核心角色同时也是该协议栈的技术难点与技术重点。例如Nordic BLE协议栈能够同时支持多达20个连接(Link)这归功于LL层的设计。
在实现BLE通信的过程中需要完成一系列复杂的功能包括但不限于:比如在通信过程中需要决定使用哪些射频通道来进行信息传递进而捕获并解析无线数据包;明确发送的时间节点以及对应的ACK应答信号的有效捕获与处理;配置重传机制的有效执行过程;包括链路管理与状态调控的关键步骤。
在这些任务中仅专注于完成收发操作而将具体的信号解析工作交由GAP或GATT模块完成。
LL层的五种RF射频状态如下:
- 电源待机模式
- 广播信号发射阶段
- 数据扫描操作阶段
- 连接请求发起阶段
- 成功建立通信连接的状态
(1)Advertising、Scanning
在广播状态下尚未建立任何连接,在此状态下仅实现单纯的数据发送功能。
采用扫描状态的设备仅负责接收来自广播者的数据传输。(2)Initiating、Connected
在进入发起连接状态时需要由发起者主动向广播者发送请求以响应相关广播信息。
发起者通常是由特定设备根据内部逻辑决定并触发该操作。当广播系统接收到该请求后则会切换至Connected连接状态。
此时处于主设备端的设备将执行接受任务并完成与从设备的通信流程。
HCI层(Host controller interface 主机控制接口层)
在HCI层通信层中, host和controller通过统一的标准接口进行信息传递。该层不仅支持软件API实现,还可以通过硬件接口如uart、spi、usb进行控制
HCI是一种选择(具体请参考文章:三种蓝牙架构实现方案(蓝牙协议栈方 案)),在两个芯片的情况下规范两片芯片之间的通信协议和命令。
HCI 是一种选择(具体请参考文章: 三种蓝牙架构实现方案(蓝牙协议栈方 案)),在两个芯片的情况下规范两片芯片之间的通信协议和命令。
L2CAP层(Logic link control and adaptation protocol:逻辑链路控制与自适应协议)
L2CAP 实现面向上层的数据封装服务 。层类似于快递系统,在其顶部收集并打包数据资源,并通过这种方式实现单端到多端的数据传输。
L2CAP对LL进行了一个简单的封装操作,在LL仅专注于传输的数据内容这一前提下,L2CAP则需要判断当前的通信通道类型是加密型还是非加密型,同时还需要管理通信端到端的间隔时间
BLE 蓝牙协议中的L2CAP 层负责数据的分割与重组,在底层无线电通信中实现较大的报文传输。该协议决定了最大传输单元(MTU)尺寸,并支持从^{1} ^{2} 的MTU大小范围。
SMP层(Secure manager protocol 安全管理协议)
SM 层安全服务层,提供配对和密钥 的分发,实现安全连接和数据交换。
SMP负责处理BLE连接中的加密技术和安全机制。为了确保通信过程中的安全性而不影响用户体验(即不会降低用户体验),这些都是SMP必须处理的关键环节。
GAP层(Generic access profile 通用访问配置文件) ———— 用于连接
主要与应用程序及profile配置文件建立通信的接口负责处理设备发现及相应的连接建立等服务。此外还负责对上级提供的应用接口进行管理,并对下层完成必要的配置工作。
GAP是LL层payload(有效数据包)解析方式中的一种,并且是最简单的方式。
GAP仅实现了极为有限的功能特性。目前其应用领域集中于广播、扫描以及发起连接操作,并以此控制LL层中的五种基本RF状态切换。
BLE协议栈GAP层负责实现蓝牙通信功能。该规范规范了设备的访问模式及操作流程。具体包括但不限于以下步骤:设备发现、建立通信连接、断开连接、初始化安全特性以及设备配置等。
GAP 状态(待机、广播、扫描、连接、主从)
GAP 层的蓝牙状态切换图如图5.3所示,每个状态描述如下:
-
1)待机状态(Standby)
设备没有传输和发送数据,并且没有连接到任何设备 -
2)广播状态(Advertiser)
周期性广播状态 -
3)扫描状态(Scanner)
主动地寻找正在广播的设备 -
4)发起连接状态(Initiator)
主动向某个设备发起连接 -
5)已连接,主机状态(Master)
-
6)已连接,从机状态(Slave)

BLE 连接请求参数(连接间隔、广播间隔、从机潜伏、监督超时)
BLE 连接参数包含如下:
在蓝牙通信过程中,在连接时段内所有数据传输都会发生。系统会定期触发这些连接事件(connection event),两个相连的连接事件之间的时间间隔即为整个系统的连接间隔(connection interval)。该芯片支持的最大传输包数量为8 个,在一个典型的周期性重复模式下, 每个相连的周期段(period)最多可以接收8个报文块(packet)。图5.4展示了这个过程的时间线图。在这个系统中, 连接间隔设置在1.25毫秒,并且时间范围是从7.5毫秒到4秒钟不等
为了避免数据传输中断,在之前的项目中所采用的是某一品牌型号的蓝牙模块,在主控芯片的串口端发包周期方面必须设置为不低于50ms以确保数据帧丢失不会发生

- 当Connection Interval被缩短时,在每个周期内Master与Slave之间的通信变得更加频繁地进行。这不仅提升了数据吞吐量(即每单位时间传输的数据量),还减少了数据发送的时间(即从发送到接收的时间),然而这种做法会导致电路功耗的增加。
- 当Connection Interval被延长时,在每个周期内Master与Slave之间的通信频率降低,并且数据吞吐量随之下降。此外,在等待响应方面所花费的时间也会相应增加(即总处理时间变长)。不过这种情况下电路功耗得到了有效的控制(即降低)。
- 如果Slave系统的Latency能够被减少至零或进一步优化,则在每次连接事件中都需要向Master发送响应包以确认操作完成。然而这会导致电路功耗上升(因为更多的操作需要能量支持),但同时也能提高整体的数据传输效率。
- 增大Slave系统的Latency会进一步降低电路功耗(即节省能源),但是这也意味着数据传输的速度会减慢(即处理速度变慢)。
相关博文:
蓝牙 BLE连接参数 连接间隔讲解
BLE 连接间隔最大值和最小值的问题
BLE连接参数设置要点
BLE连接参数 (Connect Parameters)规范
当_SLAVE_不发送数据时,_会被允许跳过_此_事件.从机潜伏值(Slave_Latency)_是指,_在这种情况下,_最多可以被跳过的连续事件数量._在_Master 发送数据包的过程中,_如果_SLAVE_不返回响应,_Master_将会在此之后继续重发该数据包,_直到_SLAVE_返回确认.

3)监督超时:
当主机在每次连接事件中发送信号时,在接收方(即从机)做出相应回应。若长时间未收到对方的数据包,则蓝牙连接会中断。该监督超时的时间单位设定为10毫秒,并且具体范围是100毫秒至32秒之间。
在此协议中,默认情况下连接参数由主机持续维护并定期更新;然而,在这种情况下从机无法自行修改或更新这些参数。不过,在某些特定条件下(即当从机主动发起请求),它仍然可以向主机提交新的连接参数要求。具体来说,在这种情况下从机将期望值发送给主机;随后根据当前状态决定是否接受新的设置值:如果自身条件允许,则将其纳入系统设置;否则则保持原有配置不变。
4)BLE 广播间隔
当设备进入广播模式时,在特定时间段内会发送统一的信息包。两次连续广播事件之间的时间间隔即为设定的传输周期。在单次广播事件中,该信息包会依次在信道37、38以及39中被发送一次以确保覆盖所有相关节点。

GAP Role Task(参数配置api、广播内容、连接间隔、断开蓝牙、更新参数)
该角色被定义为一个独立的任务,并负责配置GAP相关参数。例如,在广播内容方面和连接参数更新方面均有所涉及。
GAP 层的角色 包含如下:
- 1)Broadcaster:广播源,在不可与外部设备通信的情况下发送广播信号。
- 2)Observer:接收器,在不发起任何通信的情况下对发送出去的广播信号进行探测。
- 3)Peripheral:支持与外部设备建立直接通信的外围设备。
- 4)Central:负责探测到所有潜在的广播信号并主动发起与这些信号源建立通信的节点。
常用的API 函数 包含如下:
1)获取参数


2)设置参数

3)主动断开蓝牙

4)发送更新连接参数请求

GAP Bond Manager(GAP Bond Mgr 连接安全初始化api)
视频里讲的GAP security profiles
GAPBondMgr 被用于完成蓝牙连接中安全特性的初始化过程。该种特性被定义为...
| 类型 | 描述 |
|---|---|
| Pairing | 配对,进行密钥交换的过程 |
| Encryption | 加密,配对完成后或重新建立连接后,数据将加密 |
| Authentication | 认证,在配对过程中采用了MITM 保护(如Passcode、NFC 等) |
| Bonding | 绑定,存储加密密钥至flash 存储器以便下次连接使用 |
| Authorization | 授权,除了认证外的一种应用层密钥交换方式 |
| OOB | 一种MITM 认证方式,密钥交换不通过空中传输,如通过NFC、串口等传输 |
| MITM | Man in the Middle Protection. 在配对过程中,通过该方式实现了认证过程,防止通信被监听 |
| Just Works | 在配对过程中,不需要MITM,实现了密钥传输 |
在通常的带有安全特性的连接中,首次连接 步骤如下:
-
- Pairing: 建立连接的方式包括通过Just Works或MITM机制(例如基于密钥交换的认证方式)。
-
- Encryption: 基于步骤一所得取的密钥对通信链路进行加密处理。
-
- Bonding: 将加密后的密钥存储到存储器中。
当需要重新建立连接时,在初次建立连接的过程中可采用Bonding技术中的密钥用于对通信链路进行加密。如果跳过Bonding步骤,则每次连接都需要重新配对以恢复配置。
配对绑定配置例程如程序清单5.5 所示。
uint32 passkey = DEFAULT_PASSCODE;
uint8 pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
uint8 mitm = TRUE; //开启MITM 认证加密
uint8 ioCap = GAPBOND_IO_CAP_NO_INPUT_NO_OUTPUT;
uint8 bonding = TRUE; //开启bonding
GAPBondMgr_SetParameter( GAPBOND_DEFAULT_PASSCODE, sizeof ( uint32 ), &passkey );
GAPBondMgr_SetParameter( GAPBOND_PAIRING_MODE, sizeof ( uint8 ), &pairMode );
GAPBondMgr_SetParameter( GAPBOND_MITM_PROTECTION, sizeof ( uint8 ), &mitm );
GAPBondMgr_SetParameter( GAPBOND_IO_CAPABILITIES, sizeof ( uint8 ), &ioCap );
GAPBondMgr_SetParameter( GAPBOND_BONDING_ENABLED, sizeof ( uint8 ), &bonding );

ATT(Attribute protocol 属性协议层)
简单而言, ATT层用于定义用户指令及其操作的数据, 例如读取或写入某个数据. 在BLE协议栈中, 开发者最常接触到的是ATT层.
在ATT层面中,支持设备向另一台设备呈现一段特定的数据内容,并将其定义为'属性'.负责呈现'属性'的那一台设备被称为服务器,与其相互配对的一方则被定义为客户端.
在链路层LL状态(包括主机和从机),它们与设备之间的ATT角色之间存在互不干扰的关系。其中,在这种关系下(即作为服务器或客户端),主机设备以及从机都可以灵活地承担不同的角色职责。
BLE提出了attribute概念,并用于表示单条数据;由于除了定义数据之外还能够发送ATT命令的信息包属性层被命名为ATT层
GATT(Service/Characteristic的UUID主从机通信) ———— 用于通信
Generic attribute profile
通过名称即可推断出 GATT 层的性质。
GATT 位于 ATT 之上的一层结构,并明确了如何在 ATT 的基础上构建服务框架。
基于属性(attribute)中的数据内容制定规则,并采用分组(group)的方法对属性进行分类管理以实现规范化控制。如果没有支持该机制的基础层协议如蓝牙(BLE),单个节点之间的互联就无法实现;正是由于引入了蓝牙制式接口体系中的通用接口技术(GATT)以及多种多样应用于该领域的特定配置参数配置文件(application profile),使得BLE能够超越ZigBee等无线协议在兼容性方面的不足;从而使得BLE成为市场上销量最高的2.4 GHz无线通信解决方案。
BLE 协议栈中的GATT层负责两个设备间的应用层数据通信。基于Client/Server架构设计的GATT层中,客户端(Client)通过调用服务和特征来完成数据交互。


关于服务与特征的描述如下:
用户的应用由一个或多个服务(Service)组成的
每个服务Service 包含一个或多个特征(Characteristic)
各个特性由一个或多个属性组成。具体说明时提到这些属性是设备间传输信息的基本单元。根据GATT标准将这些基本单元整合成为块状数据的形式被称为Characteristic。
-
- a. Characteristic Value:特征值
-
- b. Characteristic Declaration:特征申明,描述特征的权限、类型等
-
-
c. 客户特性配置:支持 GATT 服务器发送通知或指示数据
-
- d. Characteristic User Description:描述特征的ASCII 字符串
-
- 特征值数据:经过层层解析后所追求的目标。
- 特征值数据属性:具体位置及其数据类型。
- 配置GATT服务器发送出去的相关属性(notified),并期待一个回应(indicated)。
- 用于描述特征值的ASCII字符串。
- 属性表中的唯一索引项(handle),每个属性都有其独特的索引编号。
- 标识属性数据所代表的具体内容(UUID)。其中一些UUID由SIG指定定义,还有一些则是根据需求进行自定义开发。
- 限定客户端对属性值访问的权利范围(permission),特别强调此权限与特征值数据本身的访问权限有所区别。

在GATT中实现了对属性的发现、读取及完整流程。其中特征值与内容配置文件均存储在属性表中。下图展示了GATT某服务的属性表(以TI为例):





上表中每一行均对应一个服务属性
第一行标识一个服务(如温湿度监测服务)的基本起始信息
第二至第四行列举该服务的第一个特征值(包括UUID、声明信息、特征值、描述项)
第五至第七行则为该服务的第二个特征值及其相关内容
特别的是第十三行的作用在于设置客户端特征值参数项,
用于服务器主动向客户端发送数据
蓝色蓝牙设备开发入门流程主要包括设定属性表并调用相关API接收发送属性数据
顶层profile配置文件
该系统由两个主要组成部分构成,在协议栈层次结构中位于顶端位置,并与应用层实现紧密地连接在一起。系统设计允许developer无需深入理解protocol stack的底层原理即可顺利进行开发工作。
下面这两个是芯海蓝牙模块开发指南的说明
Lib 库文件
Lib被定义为蓝牙库文件,并以MASK ROM代码为基础实现主要的蓝牙协议栈功能。少数蓝牙协议栈的具体实现则包含于Lib库文件之中。
外设驱动
旨在提高移植性的情况下,协议栈通过将硬件层抽象成一个 HAL 硬件抽象层来实现模块化设计。当新硬件平台准备好时,仅需更改该 HAL 本身即可完成升级,并无需调整其上层的协议栈和其他组件及应用程序。
蓝牙主机和从机之间传输数据实现
<>
一款强大的芯片nRF52840及利用蓝牙5.0实现数据远程采集
<>
分析OSAL及蓝牙协议栈相关代码:https://...
研究OSAL及蓝牙协议栈的相关代码:
基于蓝牙4.0与BLE的通信协议体系
BLE开发环境搭建
教程使用IAR,添加BLE蓝牙协议栈文件,类似操作系统的OSAL
OSAL工作原理
相关博文:OSAL
基于STM3...移植方案的研究与实现该研究主要针对STM...平台进行了深入分析,并提出了相应的优化策略
通过构建层次化架构设计...实现了资源的有效管理
该方案在性能提升方面表现突出,在实际应用中展现出良好的稳定性和扩展性

OSAL 被定义为 Operating System Abstraction Layer(OSAL),即操作系统抽象层,在支持多任务并行运行的基础上实现了BLE协议栈、配置文件以及所有应用程序(app)在其之上运行的功能。值得注意的是这一架构并非按照传统操作系统的架构设计而是采用一种简单的任务轮询机制以实现高效的事件处理能力
OSAL层在任何一个时间段内只执行一个特定的任务;CPU能够通过优先级调度机制来管理这些作业;每个作业都被分配了一个固定的时间片 slice;这种机制类似于现代操作系统的多线程调度模式;当某个作业的时间片用完时会立即切换到下一个作业;由于每个作业的执行时间极短(如OSAL中的单个作业可能持续仅15毫秒);这种频繁的任务切换使得系统看上去像是有多条作业同时运行;
OSAL 相较于RTOS,在任务调度能力上有所欠缺,并未包含任务堆栈机制、系统的延迟控制、中断处理、进程间的通信机制以及在任务调度中保留上下文的能力。
软件功能是由任务事件来实现的,创建一个任务事件需要以下工作:
-
- 生成任务标识符(task ID);
-
- 开发并实施任务初始化流程,并需整合至OSAL的初始化阶段;
-
- 实现任务处理逻辑;
-
- 根据需求提供消息服务。
BLE协议栈的各个层级均采用OSAL任务方式进行实现;其中LL控制的任务因其最紧迫的时间约束而具有最高优先级;为此构建了专门的任务管理系统;该系统通过消息处理、存储管理以及时间驱动型定时机制等多种附加服务来完成功能。
如程序清单5.1所示展示了OSAL的任务事件处理回调函数数组。其中xx_ProcessEvent函数的顺序必须与后续salInitTasks函数中完成的任务初始化操作的顺序保持一致。
首先创建一个事件表以存储各任务对应的事件信息
接着创建一个事件处理函数表用于保存各任务对应的事件处理函数
最后将这两个表格进行对齐以实现功能匹配


事件处理函数表:
里面保存的都是函数指针
///< The order in this table must be identical to the task initialization calls below in osalInitTask.
const pTaskEventHandlerFn tasksArr[] //都是函数指针,指向了事件处理函数
{
LL_ProcessEvent, // task 0
HCI_ProcessEvent, // task 1
#if defined ( OSAL_CBTIMER_NUM_TASKS )
OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ), // task 2
#endif
L2CAP_ProcessEvent, // task 3
SM_ProcessEvent, // task 4
GAP_ProcessEvent, // task 5
GATT_ProcessEvent, // task 6
GAPRole_ProcessEvent, // task 7
#if (DEFAULT_GAPBOND_MGR_ENABLE==1)
GAPBondMgr_ProcessEvent, // task 8
#endif
GATTServApp_ProcessEvent, // task 9
SimpleBLEPeripheral_ProcessEvent, // task 10
};
TASK初始化(osal_start_system调用初始化函数osalInitTasks)
为了实现OSAL的功能,在main函数的最后一段代码中必须执行一个名为osal_start_system的过程。该过程负责调用根据特定的应用需求来设计的初始化函数salInitTasks。salInitTasks函数如程序清单5.2所示的例子所示。
事件表:
接上文程序
const uint8 tasksCnt = sizeof(tasksArr)/sizeof(tasksArr[0]);
uint16 *tasksEvents;
/** * @fn void osalInitTasks(void)
* @brief This function invokes the initialization function for each task.
* @param none
* @return none
*/
void osalInitTasks( void )
{
uint8 taskID = 0;
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
//tasksEvents事件表首地址 通过这个指针可以访问到事件表的每一项
//如果有事件发生,就查找函数表,找到对应事件的处理函数进行处理
//处理完成继续访问事件表,看是否还有其他事件要发生 基于事件驱动的轮询操作系统
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
/* LL Task */
LL_Init( taskID++ );//ID越小,优先级越高 LL控制任务的时间要求最为迫切,任务优先级最高
/* HCI Task */
HCI_Init( taskID++ );
#if defined ( OSAL_CBTIMER_NUM_TASKS )
/* Callback Timer Tasks */
osal_CbTimerInit( taskID );
taskID += OSAL_CBTIMER_NUM_TASKS;
#endif
/* L2CAP Task */
L2CAP_Init( taskID++ );
/* SM Task */
SM_Init( taskID++ );
/* GAP Task */
GAP_Init( taskID++ );
/* GATT Task */
GATT_Init( taskID++ );
/* Profiles */
GAPRole_Init( taskID++ );
#if(DEFAULT_GAPBOND_MGR_ENABLE==1)
GAPBondMgr_Init( taskID++ );
#endif
GATTServApp_Init( taskID++ );
/* Application */
SimpleBLEPeripheral_Init( taskID );//GAP GATT的配置 视频中串口、LED、ADC实验也在此函数内完成
}
osalInitTasks 函数依次调用BLE 协议栈各层的启动进程以完成协议栈初始化,并为每个任务分配一个8位的任务标识符(task ID)。当osalInitTasks函数完成运行后,系统将进入循环等待处理各项任务的状态。
使用OSAL 时,注意事项如下:
- 1)由任务ID确定任务优先级**(注:此处已修改为将"决定于"改为"由...确定")
- 2)当任务ID越小时,则其优先级越低(注:此处已修改为"当...时,则..."结构,并将动词从过去时态改为现在时态)
TASK与event 的处理机制:基于位运算的操作逻辑中,在每个事件存储空间(每个events占用2个字节)的空间中最多能定义16种不同的事件类型(通过osal_start_system函数启动运行过程,并通过调用osal_run_system函数来执行相应的操作)
完成TASK 初始化后
void osal_start_system( void )
{
for(;;) // Forever Loop
{
osal_run_system();
}
}


OSAL任务采用二进制变量形式进行配置,在每位二进制位上对应一个独特的事件标识符。因此每个任务最多可设置为包含不同类型的事件共16种。OSAL的事件处理流程图如图5.1所示
osal_run_system 函数(在osal_start_system 函数的死循环内调用)流程如下:
-
- 重新配置OSAL Timer以触发指定时间段后的中断(timeout)事件,并将该中断源的任务ID设定为默认值(通常设为0)。
- 视频中此处 Hal.ProcessPoll() 即HAL层的信息处理核心环节。
-
- 检查当前与该 task 相关的任务队列是否存在中断请求:
- 如果存在,则跳转至步骤3);
- 如果不存在,则对该 task 的 ID 进行自增操作;
- 继续返回步骤2) 进行判断;
- 当 task 的 ID 超出系统定义的任务总数时,则跳转至步骤4)。
-
- 执行 tasksArr 回调函数数组中的对应 ProcessEvent 函数;
- 执行完成后退出 osal_run_system 函数;
- 并在下次循环启动时立即返回到整个流程的开始。
-
- 进入 Sleep 调度机制,在唤醒后立即返回到整个流程的开始。

当事件处理程序完成后必须清除相应标识。如果未清除相应标识,则OSAL 将认为事件尚未完成并继续调用相应的处理函数进行后续操作。
在simpleBLEPeripheral(From-Device)例程中运行时,在START_DEVICE_EVT触发后立即执行SimpleBLEPeripheral_ProcessEvent处理函数。该处理函数将被激活并执行完毕后,系统会返回一个16位的事件变量,并清除对应的SBP_START_DEVICE_EVT标志位。相关的程序代码可参考附录中的程序清单5.4部分。
if ( events & SBP_START_DEVICE_EVT )
{
……
return ( events ^ SBP_START_DEVICE_EVT );
}
原理探讨:每个event占用2个字节,并采用16位编码方式存储信息。其中最多可定义总共events变量代表不同类型的事件数量?在进行相关计算时会利用到按bit与操作以判断多个条件是否同时满足?当所有bit均为真值时(即全置高位),系统会触发该特定event并立即调用其处理函数?完成之后立即执行按bit异或操作以清除对应event的标志字段?
OSAL函数api介绍
OSAL 添加事件函数
视频课程里介绍的这些api在OSAL.c文件里面
- 1)立即事件
uint8 osal_set_event( uint8 task_id, uint16 event_flag )
调用该函数后将产生立即事件。
- 2)定时器事件
uint8 osal_start_timerEx( uint8 task_id, uint16 event_id, uint32 timeout_value )
调用该函数将设置一个软件定时器,超时后将产生对应事件。超时时间timeout_value 单位为ms。
- 3)自动加载定时器事件
uint8 osal_start_reload_timer( uint8 taskID, uint16 event_id, uint32 timeout_value );
调用该函数将设置一个自动加载的软件定时器,超时后将产生对应事件,并自动重新加载定时器。
超时时间timeout_value 单位为ms。
OSAL内存管理函数
视频课程里介绍的这些api在OSAL.c文件里面
OSAL 提供了基本的内存管理函数。内存管理函数定义如下:
1)内存分配
void *osal_mem_alloc( uint16 size )
调用该函数将分配指定size 的内存空间,并返回内存首地址。当分配失败时,将返回NULL。
2)内存释放
void osal_mem_free( void *ptr )
调用该函数将释放之前分配的内存空间。
OSAL消息通信函数
OSAL 消息机制负责不同子系统之间的通信。消息相当于数据,在这里没有对数据的种类和长度进行限制。消息管理函数定义如下:
- 1)OSAL 消息创建
uint8 * osal_msg_allocate(uint16 len );
发送消息前,需要调用该函数创建消息占用的内存空间(内部已经包含了osal_mem_alloc 函数功
能)。需要为该函数指定空间大小,该函数返回内存空间地址指针。
- 2)OSAL 消息发送
uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
将待发送的消息内容拷贝至msg_ptr 内存空间后,调用该函数向指定任务发送消息。调用该函数将产
生SYS_EVENT_MSG 事件并告知对应Task。
- 3)OSAL 消息接收
uint8 *osal_msg_receive( uint8 task_id )
目标Task 判断有SYS_EVENT_MSG 事件后,将调用该函数从指定任务接收消息数据。
- 4)OSAL 消息释放
uint8 osal_msg_deallocate( uint8 *msg_ptr );
消息处理完成后,需要调用该函数来释放内存空间(内部已经包含了osal_mem_free 函数功能)。
SYS_EVENT_MSG(0x8000)属于OSAL 保留的消息处理事件,并且每个任务都会包含这一特定的事件。该事件的作用是实现消息在不同任务间的传递机制,请参阅"OSAL 消息"一节以获取详细信息。
其他的OSAL层API函数接口
[OSAL API]( 9, 9, 9, 9, 9, 9)

OSAL UART实验
uart结构体:

uart函数:

uart调用,290行串口输出:Hello BLE World

串口回调函数内容:


OSAL 主从机通信实验(添加特征值)
主从机通信通过特征值实现的,类似标签,有四个属性:长度、读写、UUID、功能。
本实验实现了R6的功能。

UUID


长度

读写

把char6所有的属性加入到属性表里


set函数添加char6


get函数添加char6


特征量被主站获取或副站告知的时候,触发该回调机制
特征量被主站获取或副站告知的时候,触发该回调机制


write写入回调函数



change回调函数



init函数设置char6属性参数,这里才是真正把char6属性值设置好了


这个可有可无

连接状态改变的时候,,可有可无

实验现象
从机上电开始广播,主机(比如手机蓝牙助手)进行通信。
视频中又写了一个主机的程序,双方通过串口转发(透传),方便演示:



客户端发现事件处理







从机上电如何实现广播
在SimpleBLEPeripheral_Init函数里面,将从机广播功能设置为TRUE

主机自动扫描



主机连接设备



从机串口的数据处理


从机蓝牙读属性的数据处理

主机串口数据处理


OSAL BLE获得多个特征值句柄实验
主机当客户端代码分析
添加特征值


修改事件函数simpleBLEGATTDiscoveryEvent




OSAL BLE自动重连设备实验
默认断开设备主机是不能重新连接的,

OSAL BLE的绑定和配对实验
一般要输入密码,安全,限制非法连接
在SimpleBLEPeripheral_Init函数里面

改回调函数


不要每次都输入密码绑定



OSAL操作系统分析
蓝牙协议栈遇到的问题总结(主从模式、MAC地址、RSSI)
参考:蓝牙4.0/BLE协议栈学习笔记(二)
地址:
在学习开发蓝牙协议栈遇到的问题总结:
蓝牙设备号BD_ADDR相当于MAC地址;与uuid不同的是,在UUID标准中,uuid被定义为服务号,并可作为唯一的标识符。
- scanrspdata 数组用于响应式扫描设备,并允许用户自定义设备名称。
- advertdata 数组主要用于存储和管理广播类数据及其相关信息。
主从机之间的通信流程主要包括以下几个步骤:首先执行扫描过程(Search process),随后识别本地设备(Identify local devices),接着建立连接(Establish connection),随后检测可用服务(Detect available services),最后完成读取和写入characteristic属性的操作。
协议栈中采用的是SimpleBLEPeripheral这一角色,在此角色下主要负责发布消息以通知周边设备。而SimpleBLECentral则负责作为主机角色,在此阶段的主要任务是实现主机与配对设备之间的通信。读取或写入characteristic可被理解为服务向客户节点传输数据以及客户节点向服务传输数据两种操作。
主机设备包括客户端(client)和服务器(service),它们之间能够实现双向的数据交互。客户端通过主动的数据传输实现对服务器的请求与响应;而服务器则能够主动接收并处理来自客户端的数据流。
主机向从机发送读写数据,并调用GATT_WriteCharValue函数和ATT_ReadCharValue函数。
if ( simpleBLEDoWrite )
{
// Do a write
attWriteReq_t req;
req.handle = simpleBLECharHdl+2;
req.len = 2;
req.value[0] = simpleBLECharVal;
req.sig = 0;
req.cmd = 0;
status = GATT_WriteCharValue( simpleBLEConnHandle, &req, simpleBLETaskId );
if ( status == SUCCESS )
{
NPI_WriteTransport(“write ok\r\n”, 10);
simpleBLEProcedureInProgress = TRUE;
simpleBLEDoWrite = !simpleBLEDoWrite;
}
else
{
// Do a read
attReadReq_t req;
req.handle = simpleBLECharHdl+2;
status = GATT_ReadCharValue( simpleBLEConnHandle, &req, simpleBLETaskId );
}
从设备与主设备的数据传输方式不同。主设备通过write命令实现数据发送功能;而子设备则采用Notification通知命令来完成信息传递任务。当子设备需要向主设备传输数据时会调用GATT_Notification函数如:
static attHandleValueNoti_t Report ;
uint16 GetHandle;
noti.len = 1;
noti.value[0] = GetLen;
GATT_Notification(GetHandle, &Report, FALSE );
4.测量电池电量:
battMeasure函数采用ADC采样内部电压以获取相应的电压值。其参考电压设置为1.25伏,并能承受的最大测量电压为3.75伏。若希望提高测量精度,则需从外部引脚引入更加稳定的参考电源,并通过ADC重新采样;然后再次利用ADC进行采样转换以完成最终的数据输出。
if ( events &SBP_ADV_RGB_EVT )
{
//P0_3=~P0_3;
advertData[6]= battMeasure();//获取电池电量
GAPRole_SetParameter( GAPROLE_ADVERT_DATA, sizeof( advertData ), advertData );
osal_start_timerEx(simpleBLEBroadcaster_TaskID,SBP_ADV_RGB_EVT,1000);
return ( events ^ SBP_ADV_RGB_EVT );
}
5.获取RSSI值:
通过采集信号强度数据RSSI值,进而采用相关定位算法进行计算。需要注意的是,在实际应用中可能会出现采样值的波动,在这种情况下必须结合滤波算法对采样数据进行处理以提高定位精度。
case GAP_DEVICE_INFO_EVENT:
{
if( (pEvent->deviceInfo.pEvtData+7)==0xA7)
simpleBLEAddDeviceInfo( pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType, pEvent->deviceInfo.rssi ,(pEvent->deviceInfo.pEvtData+6));
}
break;
IOS蓝牙iBeacon协议(UUID、Major、Minor、RSSI)
芯海蓝牙第一个例程里用到
该文介绍了IBEAcon与蓝牙之间的主要区别,并详细说明了它们各自的特点及其适用场景。具体来说,在工作频段上两者存在显著差异;从发射功率来看,IBEAcon具备更强的优势;在波形特性和发射性能方面,则体现了不同的技术路线;此外,在抗干扰能力以及多设备协同工作能力上也有明显差别;最后从稳定性与可靠性两个关键指标来看, IBEAcon的表现更加突出
该文介绍了iBeacon技术的相关知识。其中提到第七个iBeacon参数为:x,该值表示接收设备发送的数据中包含的位置信息的最小粒度,单位为米,其取值范围为1米到10米之间,默认值为5米,当该值较小时,iBeacon服务能够更精确地定位设备位置;第八个iBeacon参数为:y,该值代表接收设备发送的位置数据所处的大致区域范围,单位为平方米;第九个iBeacon参数为:z,该值决定了接收设备发送的位置数据所具有的精确度,其取值范围为1米到50米之间;第十个iBeacon参数为:t,该值表示接收设备发送的位置数据的时间更新频率,单位为秒,其取值范围为1秒到60秒之间;第十一项主要涉及通过接收设备发送的数据来实现定位的具体方法及过程;第十二项则详细说明了如何通过接收设备发送的数据来实现实时追踪功能;第十三项重点讨论了如何通过接收设备发送的数据来实现室内精准追踪的技术原理及其实现过程。
蓝牙IBEACON协议案例详细解析
手机蓝牙APP工具的使用(nrf Connect)
请安装nRF Master Control Panel(BLE)应用程序。目前支持安卓及iOS平台。该应用能够承担SCANNER和ADVERTER两大功能。接下来我们将深入探讨SCANNER功能的使用方法。

扫描者
通过点击SCAN按钮或下拉菜单选项进行操作后,在线展示当前扫描结果;滑动屏幕右方区域即可观察到各设备信号强度变化曲线图;不同颜色分别对应不同的设备

连接设备
在测试用例中设置设备标识名GCBT40-my,并单击连接按钮
可写与可监听
该设备GCBT40-my发布的服务标识符为UUID=0xff10;其中包含两个重要特征值:一个标识符为UTF-8编码格式的字符串类型(对应向上指针图标),另一个标识符则用于存储音频数据(对应三个向下指针图标)。

写操作(APP到从设备)
写操作,数据从APP端发送到从设备端
执行撰写操作后,可以选择设置数据输入格式,其中TEXT字段设为字符串类型,可以在Save as窗口中录入所需数据,然后点击SAVE按钮进行保存,以便下次可以直接从LOAD界面传输该数据块

监听操作(从设备到APP0)
数据从从设备端发送到APP端
轻点图标"↓↓↓"后,在APP后台运行时会自动接收来自设备notify的数据流。进入右滑区域即可查看实时接收的数据流。

JDY-10M蓝牙模块使用教程(AT指令)
参考:千锋教育科技_物联网_嵌入式系统开发及应用实践课程内容(涵盖蓝牙、4G、WIFI、NB-iot、ZigBee等模块)
地址:https://www.bilibili.com/video/BV1Ui4y1s71B?p=52
蓝牙开发介绍


JDY-10M模块介绍





JDY-10M引脚说明



普通数据收发AT指令
使用AT指令,隐藏了底层的蓝牙通信协议栈






控制功能数据AT指令



手机APP通信






蓝牙广播数据格式和简单易懂的蓝牙视频教程
蓝牙广播包的最大容量为37个字节,在此中用于设备MAC地址占用6个字节后剩余的仅能提供31个字节的空间。当广播数据量超出限制时,在31个字节不足以存储的情况下,则可以通过将一部分较为次要的数据转移至扫描响应数据字段中来辅助分担广播信息的发送任务
博文及对应的B站视频。
<>
BLE 报文结构
<>
蓝牙连接过程及对应的断连原因
BLE通信过程建立在连接基础之上,在根据角色可分为蓝牙主设备(亦称中央设备)与外围设备(Bluetooth accessory device)的基础上展开讨论。本研究将统称为主机与从机进行描述以简化表述,在每次蓝牙通信中均采用由主机发起响应的方式操作。经测试发现频繁出现"秒断"现象(specifically, the phenomenon occurs when the host device successfully connects to the peripheral device and then immediately disconnects),其具体原因码值为0x3e(determined through testing and packet analysis)。通过查阅相关资料并深入探究问题本质后最终得以解决
为了更好地了解,《Core_v4.2》中关于0x3e断开原因的具体说明是什么?在第385页有详细的说明内容。

从表面含义来看,则是一个非常笼统的表述,在具体操作上发起了一个连接请求。但这一连接请求未能成功建立。为了弄清问题的根本原因,则必须深入掌握蓝牙设备的连接流程。现参考该文档的相关部分来详细分析:在第144页的具体信息位于第144页的位置处。

上图中,设备A是蓝牙主机,B是蓝牙从机。BLE的连接过程,大致分为6个步骤:
主机切换到连接模式,并开始捕获待连接设备的广播包。如果在预设的时间间隔内未能捕获任何数据包,则会发生超时现象
2)主机成功接收到从机的广播包。
3)主机发送一个连接请求包。
当主机发送出一个连接请求包时(如图所示的步骤4A),无论是否接收到该包(即步骤4A),主机都会立即切换到连通状态,并将这一状态信息告知应用程序层。若接收端设备接收到该连接请求包(如图所示的步骤4B),则接收端设备也会立即切换到连通状态,并将这一信息告知应用程序层;若未接收到该连接请求包,则不会向接收端发送相应的确认信息。
5)主机随即发送一个同步包。
接收方在接收到同步包之后向主机发送一个确认报文,并将不断重复执行第5步和第6步的操作流程。只有当这两个步骤均顺利完成时才会确认连接建立成功并完成整个连接过程。如果在第5步或第6步中的任一报文丢失则会导致整个连接操作失败并返回断开原因码0x3e
知道了BLE连接的详细过程,并进一步利用抓包工具获取了该过程中的数据包信息后发现,在绝大多数情况下容易察觉到第5步或第6步的数据包丢失现象。这种缺失情况多发生在周围布满蓝牙设备导致信道拥挤的情况下。通过在空旷且没有多余蓝牙设备干扰的地方进行实验观察到,在这种环境下出现这一现象的概率显著下降。这充分验证了我们的推论:当周围配置过多的蓝牙设备时(如多个设备同时工作),应用层可以通过多次尝试连接来规避这一问题。
- 当步骤二中的广播包无法接收时,则会触发错误码0x08。
- 在连接流程中若成功完成至第四阶段后却未能继续至第五阶段,则会暂时建立连接并立即中断,并报告断开原因代码为0x3E。
- 在完成第六阶段后仍出现通信中断的情况(例如任一端口发生断电),也会触发错误码读数为0x08。
蓝牙官方资料下载地址(官方)
<>
