目录

参考:芯海厂家《CST92F25 SDK开发指南V1.2.pdf》 蓝牙BLE 5.0
参考:「物联网」- 蓝牙4.0 BLE开发入门到精通(视频使用CC2540蓝牙BLE芯片、介绍IAR编程环境安装及使用)
地址: 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,低功耗蓝牙,是一种无线传输小数据的超低功耗蓝牙技术。蓝牙设备总共分为三种:Bluetooth、Bluetooth smart、Bluetooth smart ready。

  • 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)

摘自:BLE(2)——基本特性(状态、角色、地址、信道)

蓝牙 BLE 工作在 2.4GHz 的频段上,分为 40 个 RF 信道,每个信道 2M。同一时刻,只能用一个信道进行数据的传输/接收。

这 40 个 RF Channel 上,并不是平等的, SIG 把物理信道转换成为一个叫做 Channel Index 的玩意(其实就是简单的转换关系):
在这里插入图片描述

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

在这里插入图片描述
在 BLE 4.2 时代,Advertising、Scanning、Initiating 状态只允许在 Ch Index 的 37/38/39 上执行数据的收发。Advertising 是为了让其他 BLE 设备发现本设备,Scanning 是为了扫描到其他设备。为了让发现设备和广播更容易不受诸如 WIFI 的干扰,专门将 Advertising、Scanning、Initiating 数据收发放到了 与 WIFI 频段隔离的部分,起到一定抗干扰作用:
在这里插入图片描述

除了 37、38、39 这些频段,在 Connection 状态下使用了 其他的 37 个 Channel通信信道,通过跳频技术(Hopping),来减少数据干扰,增强系统的可靠性。

BLE协议栈分层

  • 协议:类似语言,如汉语、英语等。所谓协议,即将指定的字节按照一定的顺序排列起来,以便他人使用自己的设备时,能通过该协议同其他设备进行通信。协议的特点,就是有固定的帧格式,通过该格式发送,接收者通过解读帧格式,进而得到新息内容;
  • 协议栈:协议的具体实现形式,通俗可理解为代码的实现、函数库,供开发人员调用;
  • BLE协议栈:将蓝牙各个层的协议集合在一起,以函数库的形式体现,并提供应用层的API供用户调用。

下图是协议栈的结构分层:

蓝牙协议分host和controller两个部分,Host是正真意义的蓝牙协议,Controller为蓝牙底层,或者说是基带芯片。
Profiles(配置文件)和应用总是基于GAP和GATT 之上。
在单芯片方案中,Controller 和HOST、Profiles、应用层都在同一个芯片上。

在这里插入图片描述

在这里插入图片描述

视频中介绍的CC2540蓝牙BLE SOC芯片可单芯片实现协议栈结构的所有组件,开发者可以直接搭建自己的应用程序。
在这里插入图片描述

PHY层(Physical layer 物理层)

PHY层用来指定BLE所用的无线频段(2.4G),调制解调方式和方法、跳频等。PHY层做得好不好,直接决定整个BLE芯片的功耗,灵敏度以及selectivity等射频指标。

LL层(Link Layer 链路层)

主要是RF射频控制,链路层定义了协议栈中最为基础的状态机、数据包格式、广播和连接流程等问题

LL层是整个BLE协议栈的核心,也是BLE协议栈的难点和重点。像Nordic的BLE协议栈能同时支持20个link(连接),就是LL层的功劳。
LL层要做的事情非常多,比如具体选择哪个RF射频通道进行通信,怎么识别空中数据包,具体在哪个时间点把数据包发送出去,怎么保证数据的完整性,ACK如何接收,如何进行重传,以及如何对链路进行管理和控制等等。LL层只负责把数据发送出去或者接收回来,对数据进行怎样的解析则交给上面的GAP或者GATT。

LL层的五种RF射频状态:

  • Standby 待机状态
  • Advertising 广播状态
  • Scaning 扫描状态
  • Initiating 发起连接状态
  • Connected 连接状态

(1)Advertising、Scaning
广播状态下还没有任何连接,广播者只是单纯地往外广播数据。
扫描状态下,扫描者仅仅接收广播者广播的数据。

(2)Initiating 、Connected
发起连接状态是发起者发起的,发起者是通过发出request连接请求来响应广播者的一个设备,如果广播者接受了这个request则进行连接,进入Connected连接状态。
进入连接状态之后,发起连接的设备成为主设备,接受连接的设备成为从设备

HCI层(Host controller interface 主机控制接口层)

HCI 层通信层,host 和 controller 提供一个标准化的传输接口。该层可以由软件api 实现或者使用硬件接口 uart、spi、usb 来控制。

HCI是可选的(具体请参考文章: 三种蓝牙架构实现方案(蓝牙协议栈方 案)),HCI主要用于2颗芯片实现BLE协议栈的场合,用来规范两者之间的通信协议和通信命令等。

L2CAP层(Logic link control and adaptation protocol 逻辑链路控制和自适应协议)

L2CAP 为上层提供数据封装服务。层相当于快递,将数据打包,可以让客户点对点的通信(允许点对多点)。

L2CAP对LL进行了一次简单封装,LL只关心传输的数据本身,L2CAP就要区分是加密通道还是普通通道,同时还要对连接间隔进行管理。

L2CAP 是BLE 蓝牙的复用层,其支持数据的分割和重组,使得较大的报文可以在底层无线电中进行传输。L2CAP 决定了MTU size(Maximum Transmission Unit:最大传输单元),目前芯片支持的MTU_SIZE 为23~247 Bytes。

SMP层(Secure manager protocol 安全管理协议)

SM 层安全服务层,提供配对和密钥的分发,实现安全连接和数据交换。

SMP用来管理BLE连接的加密和安全的,如何保证连接的安全性,同时不影响用户的体验,这些都是SMP要考虑的工作。

GAP层(Generic access profile 通用访问配置文件) ———— 用于连接

直接与应用程序、profile配置文件进行通信的接口,处理设备发现和连接等相关服务就是通过这一层。另外还处理安全特性的初始化,对上级提供应用接口,对下层进行配置。
GAP是对LL层payload(有效数据包)如何进行解析的两种方式中的一种,而且是最简单的那一种。
GAP简单的对LL payload进行一些规范和定义,因此GAP能实现的功能极其有限。GAP目前主要用来进行广播,扫描和发起连接等,控制LL层的五种RF状态切换

BLE协议栈GAP层实现了蓝牙连接功能,其定义了设备的访问模式与流程,包括:设备发现,建立连接,断开连接,初始化安全特性,设备配置等。

GAP 状态(待机、广播、扫描、连接、主从)

GAP 层的蓝牙状态切换图如图5.3所示,每个状态描述如下:

  • 1)待机状态(Standby)
    设备没有传输和发送数据,并且没有连接到任何设备
  • 2)广播状态(Advertiser)
    周期性广播状态
  • 3)扫描状态(Scanner)
    主动地寻找正在广播的设备
  • 4)发起连接状态(Initiator)
    主动向某个设备发起连接
  • 5)已连接,主机状态(Master)
  • 6)已连接,从机状态(Slave)

在这里插入图片描述

BLE 连接请求参数(连接间隔、广播间隔、从机潜伏、监督超时)

BLE 连接参数包含如下:

1)连接时间间隔:
在蓝牙通信过程中,所有的数据传输均发生在连接事件(connection interval)期间。连接事件周期性的发生,两个连接事件间的间隔时间即为连接间隔。每个连接事件内可传输多个包,该芯片支持的最大传输包数量为8 个。
连接间隔示意图如图5.4所示。连接间隔单位时间为1.25ms,时间范围为7.5ms~4.0s 之间。

在这里插入图片描述

  • 1.Connection Interval缩短,Master和Slave通信更加频繁,提高了数据吞吐速度,缩短了数据发送时间,但也提高了功耗。
  • 2.Connection Interval加长,通信频率降低,数据吞吐速度降低,数据发送等待的时间更长,但这种设置降低了功耗。
  • 3.Slave Latency减少或者设置为0,那么每次连接事件中都需要回复Master的包,当然功耗会上升,但数据发送速度也会提高。
  • 4.Slave Latency加长,功耗下降,数据发送速度降低。

相关博文:
蓝牙 BLE连接参数 连接间隔讲解
BLE 连接间隔最大值和最小值的问题
BLE连接参数设置要点
BLE连接参数 (Connect Parameters)规范

2)从机潜伏:
当Slave 没有数据发送时,允许Slave 跳过连接事件。从机潜伏值(Slave Latency),是允许从设备跳过的最大连接次数。在连接事件中,如果Slave 没有对Master 的包做出回应,Master 将会在后来的连接事件中重复发送,直到Slave 回应。图5.5 为Latency=0 与Latency=3 的对比示意图。

在这里插入图片描述

3)监督超时:
在每个连接事件中,主机发送信号,从机进行回应。当长时间未收到对方信号包时,蓝牙将断开,超时断开的时间即为监督超时时间。监督超时时间单位为10ms,范围为100ms~32s 之间。
连接参数由主机进行维护与更新,从机无法更改连接参数,但是从机可以请求主机进行连接参数更新。从机将连接参数期望值发送给主机,主机根据自身情况,选择一个合适的连接参数更新或者拒绝更新。

4)BLE 广播间隔

当设备处于广播状态时,将发送广播信号。广播信号发生于广播事件中,相邻两个广播事件的间隔即为广播间隔。在每次广播事件中,广播包会分别在3 个广播通道(37、38、39 信道)中被发送一次。
广播事件如图5.6 所示。广播间隔单位时间为0.625ms,范围为20ms~10.24s。
在这里插入图片描述

GAP Role Task(参数配置api、广播内容、连接间隔、断开蓝牙、更新参数)

GAP Role 为一个单独的任务,其主要实现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 用于蓝牙连接时安全特性的初始化。蓝牙安全特性定义如表5.5 所示。

类型描述
Pairing配对,进行密钥交换的过程
Encryption加密,配对完成后或重新建立连接后,数据将加密
Authentication认证,在配对过程中采用了MITM 保护(如Passcode、NFC 等)
Bonding绑定存储加密密钥至flash 存储器以便下次连接使用
Authorization授权,除了认证外的一种应用层密钥交换方式
OOB一种MITM 认证方式,密钥交换不通过空中传输,如通过NFC、串口等传输
MITMMan in the Middle Protection. 在配对过程中,通过该方式实现了认证过程,防止通信被监听
Just Works在配对过程中,不需要MITM,实现了密钥传输

在通常的带有安全特性的连接中,首次连接步骤如下:

  • 1)Pairing:配对,采用Just Works 或者MITM(如passcode)交换秘钥
  • 2)Encryption:使用步骤1)的秘钥加密链路
  • 3)Bonding:保存加密秘钥至Flash

当需要重新建立连接时,可使用首次连接过程中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 层 ATT 环境中,允许设备向另外一个设备展示一块特定的数据,称之为“属性” ,展示“属性”的设备称为服务器,与之配对的设备称为客户端

链路层LL状态(主机和从机)与设备的 ATT 角色(服务器和客户端)是相互独立的,链路层的主机设备可以是 ATT 服务器,也可以是 ATT客户端,从机也一样。

BLE引入了attribute概念,用来描述一条一条的数据。Attribute除了定义数据,同时定义该数据可以使用的ATT命令,因此这一层被称为ATT层。

GATT(Service/Characteristic的UUID主从机通信) ———— 用于通信

Generic attribute profile通用属性配置文件
GATT 层从名字就能看出,GATT 是在 ATT 上面的一层结构,定义了使用 ATT的服务框架,

GATT用来规范attribute中的数据内容,并运用group(分组)的概念对attribute进行分类管理。没有GATT,BLE协议栈也能跑,但互联互通就会出问题,也正是因为有了GATT和各种各样的应用profile,BLE摆脱了ZigBee等无线协议的兼容性困境,成了出货量最大的2.4G无线通信产品。

BLE 协议栈GATT 层用于实现两个设备间的应用层数据通信。GATT 层采用Client/Server 架构,客户端(Client)通过访问服务器端(Server)的服务(Service)与特征(Characteristic),实现数据交互。
GATT 层架构如图5.7 所示。

在这里插入图片描述

在这里插入图片描述

关于服务与特征的描述如下:

  • 用户的应用由一个或多个服务(Service)组成的

  • 每个服务Service 包含一个或多个特征(Characteristic)

  • 每个特征(Characteristic)包含一个或多个属性(Attribute)。属性分别如下(属性是设备之间传输的信息的基本单元,GATT将这些基本单元组织成一块一块的数据,称为Characteristic):

    • a. Characteristic Value:特征值
    • b. Characteristic Declaration:特征申明,描述特征的权限、类型等
    • c. Client Characteristic Configuration:允许GATT 服务器发送notification 或indication 数据。
    • d. Characteristic User Description:描述特征的ASCII 字符串

  • Characteristic Value:特征值的数据,层层解包之后最终想要的值。
  • Characteristic Declaration:特征值数据值的属性,位置和类型。
  • Client Characteristic Configuration:配置GATT服务器主动发送出去的属性(notified),并且期望一个回应(indicated)。
  • Characteristic User Description:描述特征值的ASCII字符串。
  • Handle:属性表中的索引,每个属性有一个唯一的handle。
  • Type:指示属性数据表示的什么样的内容,被称为UUID。有一些UUID被SIG定义,有一些自定义。
  • Permission:限制GATT客户端对属性值的访问权限,注意区分和特征值数据的访问权限。

在这里插入图片描述
GATT里定了发现、读取、写入整个属性过程,特征值、内容配置文件都存在属性表中,下图是GATT某个服务的属性表(TI为例):
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述 上表中,每一行就是一个属性
第1行表示一个服务(比如温湿度服务)的开始
第2, 3,4行是该服务的第一个Characteristic,包含了UUID、Declaration、Char Value、Description。
第5, 6,7行是该服务的第二个Characteristic。。。
特别的是第13行,作用是配置客户端特征值,用于server主动发送数据给client。
蓝牙的应用开发入门就是设定特征值的属性表,然后调用API去接收发送属性。

顶层profile配置文件

由两个部分组成,处于协议栈的最顶层,将协议栈和应用层紧密的连接到一起,开发者对协议栈本身底层原理不用深入理解,就可以进行使用开发。


下面这两个是芯海蓝牙模块开发指南的说明

Lib 库文件

Lib 为蓝牙库文件,大部分蓝牙协议栈在MASK ROM 代码中实现,少部分蓝牙协议栈代码在Lib 库文件中。

外设驱动

为了实现更好的移植性,协议栈将硬件层抽象出了一个HAL 硬件抽象层,当新的硬件平台做好后,只需修改HAL,而不需修改HAL 之上的协议栈的其他组件和应用程序。

蓝牙主机和从机之间传输数据实现

https://blog.csdn.net/happygrilclh/article/details/76290561

一款强大的芯片nRF52840及利用蓝牙5.0实现数据远程采集

https://blog.csdn.net/daocaokafei/article/details/116949986

分析OSAL和蓝牙协议栈代码:https://blog.csdn.net/daocaokafei/article/details/114735021

蓝牙4.0BLE协议栈

BLE开发环境搭建

教程使用IAR,添加BLE蓝牙协议栈文件,类似操作系统的OSAL

OSAL工作原理

相关博文:OSAL
STM32 OSAL操作系统抽象层的移植

在这里插入图片描述

OSAL 为Operating System Abstraction Layer,即操作系统抽象层,支持多任务运行,其中BLE 协议栈、配置文件以及所有的应用程序(app)都在其上运行。它并不是一个传统意义上的操作系统,而是一个允许软件建立和执行事件的循环。OSAL 基于消息驱动,是一个简易的任务轮询系统。

OSAL层一个时间内只有一个任务在运行,CPU可以使用任务调度机制策略,每个任务分配一个特定的时间片,就有点像操作系统,时间片用完了就进行任务切换,由于每个任务执行的时间很短(OSAL每个任务可能就15ms),任务切换的很频繁,就看到一个假象,OSAL同时多个任务在运行。

OSAL 与RTOS 相比,缺少了任务堆栈,系统延时,中断管理,进程间通信,保存上下文的任务调度。

软件功能是由任务事件来实现的,创建一个任务事件需要以下工作:

  • 1)创建任务ID(task identifier);
  • 2)编写任务初始化(task initialization routine)进程,并需要添加到OSAL 初始化进程中,这就是说系统启动后不能动态添加任务;
  • 3)编写任务处理程序;
  • 4)如有需要提供消息服务。

BLE 协议栈的各层都是以OSAL 任务方式实现,由于LL 控制任务的时间要求最为迫切,所以其任务优先级最高。为了实现任务管理,OSAL 通过消息处理(message process),存储管理,计时器定时等附加服务实现。

OSAL 任务事件处理回调函数数组示例如程序清单5.1所示。任务事件处理函数xx_ProcessEvent 顺序需与后续的osalInitTasks 函数内的Task 初始化顺序一致。

先建立一个事件表,保存各任务对应的事件
再建立一个事件处理函数表,保存各任务对应的事件处理函数
将两个表对应起来在这里插入图片描述
在这里插入图片描述

事件处理函数表:
里面保存的都是函数指针

///< 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 的进程,该进程会调用由特定应用决定的初始化函数osalInitTasks。osalInitTasks 函数示例如程序清单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 协议栈各层的启动进程来初始化协议栈,并设置一个任务的8bit 任务ID(task ID)。osalInitTasks 函数执行完成后,将跳入循环等待执行任务,系统启动完成。

使用OSAL 时,注意事项如下:

  • 1)任务优先级决定于任务ID,任务ID 越小,优先级越高
  • 2)BLE 协议栈各层的任务优先级比应用程序的高。

TASK与event 处理原理:位运算,events占2个字节最多定义16个事件(osal_start_system调用运行函数osal_run_system)

OSAL 完成TASK 初始化后,将调用osal_start_system 函数开始运行OSAL系统。系统是以一个死循环的形式工作的,其函数实现如程序清单5.3 所示,该代码在MaskROM 中运行。

void osal_start_system( void )
{
	for(;;) // Forever Loop
	{
		osal_run_system();
	}
}

在这里插入图片描述
在这里插入图片描述

OSAL 任务事件采用16 位变量定义,每1 位表示1 个唯一的事件标识,因此每个任务最多可定义16个不同类型事件。OSAL 事件处理流程图如图5.1所示。

osal_run_system 函数(在osal_start_system 函数的死循环内调用)流程如下:

  • 1)更新OSAL Timer,以便设置timeout 事件,同时将事件查询task id 设置为0
  • 视频中此处Hal_ProcessPoll(),HAL层信息处理
  • 2)判断当前task id 的任务是否有事件发生,如有跳转至步骤3),如无则进行task id 自增,并继续执行步骤2)的判断流程。当task id 超出任务数量时,跳转至步骤4)
  • 3)执行tasksArr 回调函数数组内的对应xx_ProcessEvent 函数,执行完成后,退出osal_run_system 函数,并在下次循环里继续开始整个流程。
  • 4)执行Sleep 调度,当唤醒后,继续开始整个流程。
    在这里插入图片描述

当事件处理程序处理完相应事件后,需要清除相应标识。如未清除相应标识,OSAL 会认为事件还没有处理完成,后续会继续调用相应处理函数进行处理。

如在simpleBLEPeripheral(从机) 例程中,当事件START_DEVICE_EVT 发生,其处理函数SimpleBLEPeripheral_ProcessEvent 就运行,结束后返回16bit 事件变量,并清除事件标识SBP_START_DEVICE_EVT。程序代码如程序清单5.4 所示。

if ( events & SBP_START_DEVICE_EVT )
{
	……
	return ( events ^ SBP_START_DEVICE_EVT );
}

原理分析:events占2个字节16位,即最多定义16个事件,通过位运算中的按位与运算,全部为1则为1,进入该事件处理函数,处理完执行按位异或运算,相同为0,清除该事件标志位。

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
在这里插入图片描述

OSAL UART实验

uart结构体:
在这里插入图片描述
uart函数:
在这里插入图片描述

uart调用,290行串口输出:Hello BLE World
在这里插入图片描述
串口回调函数内容:
在这里插入图片描述

在这里插入图片描述

OSAL 主从机通信实验(添加特征值)

主从机通信通过特征值实现的,类似标签,有四个属性:长度、读写、UUID、功能。

本实验实现了R6的功能。

在这里插入图片描述
UUID
在这里插入图片描述

在这里插入图片描述

长度
在这里插入图片描述
读写

在这里插入图片描述

把char6所有的属性加入到属性表里
在这里插入图片描述

在这里插入图片描述

set函数添加char6

在这里插入图片描述

在这里插入图片描述
get函数添加char6

在这里插入图片描述
在这里插入图片描述

Read读取回调函数
特征值被主机读取或被从机通知的时候,调用这个回调函数

在这里插入图片描述
在这里插入图片描述

write写入回调函数
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

change回调函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

init函数设置char6属性参数,这里才是真正把char6属性值设置好了
在这里插入图片描述

在这里插入图片描述

这个可有可无
在这里插入图片描述
连接状态改变的时候,,可有可无
在这里插入图片描述

实验现象

从机上电开始广播,主机(比如手机蓝牙助手)进行通信。

视频中又写了一个主机的程序,双方通过串口转发(透传),方便演示:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

客户端发现事件处理
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

从机上电如何实现广播

在SimpleBLEPeripheral_Init函数里面,将从机广播功能设置为TRUE

在这里插入图片描述

主机自动扫描

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

主机连接设备

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

从机串口的数据处理

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

主机串口数据处理

在这里插入图片描述

在这里插入图片描述

OSAL BLE获得多个特征值句柄实验

主机当客户端代码分析

添加特征值
在这里插入图片描述

在这里插入图片描述
修改事件函数simpleBLEGATTDiscoveryEvent

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

OSAL BLE自动重连设备实验

默认断开设备主机是不能重新连接的,

在这里插入图片描述

OSAL BLE的绑定和配对实验

一般要输入密码,安全,限制非法连接

在SimpleBLEPeripheral_Init函数里面
在这里插入图片描述
改回调函数

在这里插入图片描述
在这里插入图片描述
不要每次都输入密码绑定

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

OSAL操作系统分析

OSAL操作系统分析

蓝牙协议栈遇到的问题总结(主从模式、MAC地址、RSSI)

参考:蓝牙4.0BLE协议栈学习笔记(二)
地址:https://blog.csdn.net/u012246376/article/details/47700477?spm=1001.2014.3001.5502

在学习开发蓝牙协议栈遇到的问题总结:

1.蓝牙设备号BD_ADDR就是MAC地址,不同于uuid,uuid是服务号,作为唯一标识符。

2.scanRspData数组是扫描回应数据数组,用户可以自定义设备名。advertData数组是广播数据数组,主要是包含在广播里的数据信息。

3.主从机通信:
主从机通信具体流程就是 Scanning (搜索) -->Devices Found(发现从机) --> Connected (连接) --> discover Service(发现设备服务)–>读写characteristic(属性)。

协议栈中的SimpleBLEPeripheral是从机模式,主要是广播信息,让其他设备知道。
SimpleBLECentral是主机模式,主要是与从机建立连接。
读写characteristic可以理解为GATT层的client向service发送数据,或者是service向client端发送数据。

主机设备可以是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.25v,最大测量电压是3.75V。如果要获取精度较高的值需要从外部输入引脚接入稳定性较高的参考电压,然后通过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和蓝牙有什么区别_它们的区别在哪里

7-iBeacon参数

蓝牙IBEACON协议案例详细解析

手机蓝牙APP工具的使用(nrf Connect)

摘自:nrf Connect低功耗蓝牙APP工具的使用

APP下载: nRF Master Control Panel (BLE)
安卓、IOS平台都有
该APP可以实现SCANER和ADVERTER两种角色
下面主要来讲讲SCANER角色的使用

在这里插入图片描述

扫描者

点击SCAN或者下拉界面,可以刷新设备列表,右滑界面可以看到每个设备的信号强度的变化曲线图,不同颜色代表不同的设备。

在这里插入图片描述

连接设备

测试设备的设备名称GCBT40-my,点击CONNECT
连接成功后会自动获取所有的服务UUID

可写与可监听

设备GCBT40-my发布了服务UUID=0xff10,
特征值UUID=0xff11为可写操作(点击图标”↑“),特征值UUID=0xff12为可监听操作(点击图标”↓↓↓“),

在这里插入图片描述

写操作(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站视频。

https://blog.csdn.net/weixin_48033504/article/details/122638836

蓝牙连接过程及对应的断连原因

我们知道BLE通信过程是建立在连接基础之上的,按角色不同可以分为蓝牙主设备、蓝牙从设备,也叫中央设备和外围设备。以下简称为主机和从机。一次蓝牙通信,通常由主机发起,从机响应。我在调试蓝牙主机程序的过程中,发现经常会出现“秒断”的现象,即主机连接上从机,然后立马又断开了,断开原因是0x3e。经过查找资料和抓包分析,终于搞清楚了发生这个现象的原因。

先看下BLE核心规范《 Core_v4.2》中对0x3e的断开原因是怎么说明的,在文档的385页可以找到相关描述:

在这里插入图片描述

从字面意思看,是非常笼统的,意思发起了一个连接,但这个连接建立失败了。要想弄清楚这个问题的原因,必须要对蓝牙的连接过程有所了解。下面就结合BLE核心规范《 Core_v4.2》来看一下蓝牙的连接过程。在这个文档的第144页,可以找到和连接过程相关的描述:

在这里插入图片描述

上图中,设备A是蓝牙主机,B是蓝牙从机。BLE的连接过程,大致分为6个步骤:

1)主机进入连接态,侦听待连接设备的广播包。如果在指定时间内没侦听到,会导致连接超时,这个时间由开发者指定。

2)主机成功接收到从机的广播包。

3)主机发送一个连接请求包。

4)主机发出连接请求包后,不管从机有没有收到,都会立即转入连接状态,同时报给用户层一个“已连接上”的消息,也就是图中的4A。如果从机能收到这个连接请求包,则从机也立即转入连接状态,并报给用户层一个“已连接上”的消息。如果从机未能收到连接请求包,则不会发4B的消息。

5)主机随即发送一个同步包。

6)从机收到同步包后,回给主机一个同步包,然后不停循环5和6两个步骤,连接才是真正建立了。如果5和6任何一个包丢失,都会导致连接建立失败,报0x3e的断开原因。

知道了BLE连接的详细过程,再结合抓包工具抓取连接过程的数据包分析,可以很容易发现,通常是丢失第5步或者第6步的数据包,导致连接建立失败。而这两个包丢失,通常发生在周围存在很多蓝牙设备,导致信道十分拥挤的情况下。通过在空旷无多余蓝牙设备的地方实验发现,出现这个现象的概率大大降低,由此验证了这个推论。当周围蓝牙设备不可避免地过多时,应用层可以通过多次重连来规避这个问题。

  • 如果第2步的广播包收不到,报错0x08;
  • 连接过程如果能走到第4步,但缺失第5步,会先连接上,但立马断开,断开原因报0x3E;
  • 如果能走到第6步之后, 但是中途PDU没了,例如某一方断电,也会报0x08

蓝牙官方资料下载地址(官方)

https://blog.csdn.net/spy_007_/article/details/124398604

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐