目录
- 蓝牙配对和绑定
- 蓝牙4.0 BLE
- 信道(RF Channel)
- BLE协议栈分层
- PHY层(Physical layer 物理层)
- LL层(Link Layer 链路层)
- HCI层(Host controller interface 主机控制接口层)
- L2CAP层(Logic link control and adaptation protocol 逻辑链路控制和自适应协议)
- SMP层(Secure manager protocol 安全管理协议)
- GAP层(Generic access profile 通用访问配置文件) —— 用于连接
- GAP 状态(待机、广播、扫描、连接、主从)
- BLE 连接请求参数(连接间隔、广播间隔、从机潜伏、监督超时)
- GAP Role Task(参数配置api、广播内容、连接间隔、断开蓝牙、更新参数)
- GAP Bond Manager(GAP Bond Mgr 连接安全初始化api)
- ATT(Attribute protocol 属性协议层)
- GATT(Service/Characteristic的UUID主从机通信) —— 用于通信
- 顶层profile配置文件
- Lib 库文件
- 外设驱动
- 蓝牙主机和从机之间传输数据实现
- 一款强大的芯片nRF52840及利用蓝牙5.0实现数据远程采集
- BLE开发环境搭建
- OSAL工作原理
- TASK初始化(osal_start_system调用初始化函数osalInitTasks)
- TASK与event 处理(osal_start_system调用运行函数osal_run_system)
- OSAL函数api介绍
- OSAL 添加事件函数
- OSAL内存管理函数
- OSAL消息通信函数
- 其他的OSAL层API函数接口
- OSAL UART实验
- OSAL 主从机通信实验(添加特征值)
- 实验现象
- 从机上电如何实现广播
- 主机自动扫描
- 主机连接设备
- 从机串口的数据处理
- 从机蓝牙读属性的数据处理
- 主机串口数据处理
- OSAL BLE获得多个特征值句柄实验
- OSAL BLE自动重连设备实验
- OSAL BLE的绑定和配对实验
- OSAL操作系统分析
- 蓝牙协议栈遇到的问题总结(主从模式、MAC地址、RSSI)
- IOS蓝牙iBeacon协议(UUID、Major、Minor、RSSI)
- 手机蓝牙APP工具的使用(nrf Connect)
- JDY-10M蓝牙模块使用教程(AT指令)
参考:芯海厂家《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 之间。
2)从机潜伏:
当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、串口等传输 |
MITM | Man 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
#endifL2CAP_ProcessEvent, // task 3SM_ProcessEvent, // task 4GAP_ProcessEvent, // task 5GATT_ProcessEvent, // task 6GAPRole_ProcessEvent, // task 7
#if (DEFAULT_GAPBOND_MGR_ENABLE==1)GAPBondMgr_ProcessEvent, // task 8
#endifGATTServApp_ProcessEvent, // task 9SimpleBLEPeripheral_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++ );
#endifGATTServApp_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 处理(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 );
}
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 writeattWriteReq_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 readattReadReq_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通信