(二十四)深入理解蓝牙BLE之“H5协议”

news2024/10/6 4:13:57

前言:

蓝牙产品在实际落地中,很多时候采用host+controller的通信模型,其中host负责实现协议栈profile是运行在主控cpu上的。controller为另外一颗单独的蓝牙芯片,负责蓝牙link layer的处理,两个芯片通过hci消息来交互数据。

hci定义了消息格式,但只有消息格式是不完整的,还需要定义在什么硬件总线上传输数据。协议规定hci消息可以在usb总线上传输,也可以在uart协议上传输,还可以在sdio协议上传输,这部分是hci的传输层定义的内容。

传输层HCI:

传输层,主机控制层接口,通过硬件接口uart/usb/sdio把host协议层的数据发送给controller层,并且接收controller层的数据。

  • usb总线传输:

由于USB总线除了VCC和GND之外,数据总线只有两根,所以在蓝牙里面,也将传输HCI消息的USB总线叫做H2协议。

  • uart总线传输:

H4:基于UART的传输,在tx和rx数据线上直接传输hci格式的数据,相对简单,但为了增加传输层流控,往往需要再增加cts和rts两根线,另外一个问题是由于它直接传输hci原始数据,没有任何的再次封装,因此当某一个数据出现错误时,即使是出现了一个错误,也可能导致后面的所有数据都无法被正确的解析出来(比如表示data length的字节出现了错误,因为它无法从错误的数据中解析出来正确的数据头),遇到这种情况,只能重启controller和host来解决问题了。

H5:基于UART的传输,也叫做3线uart协议,在tx和rx的数据线上传输经过slip封装的hci格式的消息,相对复杂,但是由于slip自身有重传的机制,也有错误检测的机制,因此它不需要cts/rts来实现流控,同时即使传输过程中某个数据位发生了错误,它也可以检测出来然后丢掉,不会影响后续数据的正确解析。

  • sdio总线传输:

sdio总线也可以传输hci指令,这种方式遇到的比较少,因为sdio的速率比较高,用在wifi的场景下比较合适,用在蓝牙这种低速率的场景下有点浪费了。

H5协议:

h5协议也叫做3线uart,使用slip协议对数据包进行封装,以保证数据的可靠性和完整性,我们分几个部分介绍slip协议的实现。在tx/rx数据线上的传输的数据,是hci消息经过slip格式封装的,如图1所示。“Slip Packet 1”可以表示一个hci命令,一个vent,一个acl数据,或和其他消息,“Slip Packet2”代表另外一个h5消息。

图1

Slip codec:

从图2中可以发现,单个slip包是以0xC0开始,并以0xC0结束的,host和controller在解析slip包时,是按照0xC0来分割数据,解析出一条一条单独的hci消息的。使用特殊字符来表示一段消息的起始和结束时,会涉及一个转义的过程,以slip协议为例,就是需要将原始数据中的0xC0转义为其他字符,以防被通信的对方错误的以为是起始符号和结束符号。

图2

slip的转义如下,原始数据中的0xC0数据,转义为0xDB 0xDC。同时0xDB转义为0xDB 0xDD。在数据发送的slip封包时,将hci数据中的0xC0字符进行转义然后在发送到tx总线上,同理在解析消息时,遇到0xDB后,需要将后面跟着的数据一起转义为相应的字符。

图3

Slip header:

如图1所示,slip的header共4个字节。通过H5传输层发送的每个数据包都会有header字段,需要说明的是传输层不支持分包与重组。

  • Seq Number:共3bit,取值范围为0~7,表示数据包的序号从0开始,每次+1,当到达7之后,下次数据包的seq number重新归0。需要说明的是如果是重传包,seq number值不变。另外如果是unreliable packet,则seq number字段为0,并且在解析时会被忽略该字段。

  • Ack Number:共3bit,取值范围为0~7,表示期望收到的对方的数据包的seq number,比如host已经收到了seq number为1的controller上报的hci消息,则host会回复ack为2的消息给controller。ack number有两层作用,第一层表示你发的seq number为1的消息我收到了,第二层意思表示host期待接收controller下一个seq number为2的消息。seq和ack的机制是实现流控和重传的核心思想,后面会详细介绍。

  • Data Integrity Check Present:1bit,表示本slip数据包是否有数据完整性校验字段(Data Integrity Check)。

  • Reliable Packet:1bit,表示本slip数据包是否为Reliable包,详细内容后面介绍。

  • Packet Type:4字节,表示hci消息的包类型,Packet Type为1,2,3,4和5是ble比较常见的类型,其他是H5特有的消息类型,Acknowledgment packets表示是一个单纯的ack,也叫做pure ack,该消息只包含ack number字段,不包含有效的seq number和数据。Link Control Packet指的是sync,sync rsp,config和config rsp等消息,他们是H5协议在Link Establishment时用到的。

图4

  • Payload Length:表示图二中Payload的数据总长度(总字节数),不包括Header字段,不包括可选的Data Intergrity Check字段。

  • Header Checksum:1个字节,用来校验header数据的正确性。

Data Intergrity Check:

数据完整性检查字段是可选的。它可用于确保数据包有效。数据完整性检查字段附加在数据包的末尾。Packet Header和Payload的每个字节均用于计算数据完整性检查。

Reliable Packets:

带着有效数据的h5包,叫做可靠包。

Unreliable Packets:

图4中的Acknowledge packets type消息包,是unreliable packets,也叫做pure ack,目的是用于告诉对端,我收到了(但是我没有什么数据要发给你)。

Example Of Message Communication:

我们这里使用Sliding Window Size为1举例(Sliding Window Size的概念,在Link Establishment章节介绍)。

正常通讯:

如图5所示,#2,#4,#6是Host发给Controller的hci command,#3,#5,#7是Controller的hci event回复,例如Host收到Controller回复的event #7之后,暂时没有需要继续发送的hci command,因此回复了一包pure ack #8,表示已经收到了Controller的消息了(否则Controller会一直重传#7包)。

图5

超时回复:

如图6所示,与正常通信类似,host在收到了Controller的hci event之后,启动了一个超时回复timer,在这个timer到达之后,如果仍然没有要发送的数据,则发送一个pure ack(ACK2),如果在timer未到达之前就有要发送的hci command,则发送command,取消掉该timer。

pure ack只有在没有要发送的数据时才会使用,它本身不带任何数据,host在发送#2,#4,#6时,也启动了超时回复timer,只是在timer到达时,发现存在要发送的hci command,则取消掉了timer,直接发送了command。

#2,#4,#6本身就充当了ack的作用,并且又携带了有效的hci command,在它们存在的时候,应该去避免发送pure ack,从而提高通讯效率。当然host端完全可以在#2,#4,#6之前也发送pure ack,比如在#2之前,发送一包#1.5的pure ack(ACK 7)。

在图6中,除了Host端启动了超时回复timer,另外一端Controller在发送了#7包后,也启动了重传timer,在重传timer到达之后,仍然未收到Host发送的消息时(类似hci command的#2,或者pure ack #8),Controller会发送“#7包”的重复包,也就是图6中的#9,但该图中由于Host发送了ACK2,所以#9被取消掉了。

Host端在处理H5协议实现时,会配置两个timer,一个用于回复ack,一个用于重传。Controller也是同样的实现逻辑,也会存在两个timer。这里面有两个点需要注意:一个是Host和Controller在配合实现H5协议时,需要双方拥有相同的超时时间(Host的超时回复timeout和Controller超时回复timeout相同,Host的重传timeout和Controller的重传timeout相同);另一个是重传的timeout应该比回复的timeout的数值大,否则的话#9就回出现在#8之前,多余了一包消息交互。

图6

超时重传:

重传机制是H5协议的核心之一,Host和Contoller是两个独立的芯片,他们之前是通过硬件总线相连接,因此就存在误码率的问题,当出现了错误数据位,根据H5的格式规范,这一包数据就会被另外一端丢失,因此特别依赖重传机制来恢复正常的工作,这个也是H5协议比H4协议有优势的地方。

重传情况1,如图7所示,Controller发给Host的#7包,由于其他硬件设备的偶尔干扰(或者其他原因),导致了出现了错误bit,被Host丢弃了,此时Host和Controller分别存在一个重传timer,它们的timeout相同,并且Host启动timer的时间要早于Controller(#6包比#7包要早,Host和Controller在发送完一包数据时,启动了重传timer),因此Host的重传timeout首先到期,然后它发了一包#8(#6的重传包),#8包被Controller正确解析,但是由于它SEQ为5,发现是Controller之前处理过的seq number消息,因此#8被丢弃的同时(#6包被Controller正确接收,只是Host并不知道)启动一个超时回复timer,准备回复ack,其他Controller的逻辑依然正常运行,在它的重传的timeout到达后,它发送了#9,同时取消掉了回复ack的timer,Host收到#9之后正确解析,发现是它期待收到的,并且消息里面的ACK Number也是符合预期的,结束掉继续重传的timer,回复pure ack,至此重传逻辑执行结束,双方开始恢复正常通信。

图7

重传情况2,如图8所示,host发送的#8包(pure ack)被意外损坏了,Controller没有成功解析出来这条消息,随后Controller在重传timer到达后,发送了#9包,host端在收到了#9包后,根据seq number判断出来是一个处理过的重复包,启动了超时回复timer,在timeout到达时回复了#10包,双方恢复正常通信,这里面有一个点,重传timer的timeout一定要比超时回复ack的timeout数值大,因此才能保证在#10发送之前不会出现Controller发送的#11,#13等等等。

图片8占位

图8

特殊处理:

刚刚提到过,应该尽量减少pure ack的发送,使用带数据的reliable packets来发送,但是也有特殊的情况,比如我们打开了扫描,并且Sliding Window Size为1,那么host在接收controller上报的adv report包时,就需要及时的回复ack,以实现可以快速的接收到后面的adv report,实现在单位时间内接收到更多的广播包,这时就可以使用pure ack来实现,每收到一包adv report,都快速回复一包pure ack,而不是等待超时回复的timeout到达后才回复pure ack。当然这种方式不是最好的方案,建议的最好方案还是配置Sliding Window Size。

撞包处理:

H5协议在实际工程落地时,会有遇到很多问题,比如最常见的就是Host和Controller撞包的问题。比如当host配置controller为扫描状态,controller会不断的上报adv report,这个时候,host又发送了一包控制命令给controller,这时就存在撞包的可能,如图9。从图中可以发现#7和#8几乎是同时发送的,在Controller的角度来看,是先发送了#7,然后收到了#8,但是在host一端来看是相反的,它认为先发送了#8,然后收到了#7,这就是撞包的情况。处理撞包的逻辑时,核心思想是把同一个包中的seq和ack分开来处理。我们从Controller的角度来解释这次撞包的处理逻辑,Controller在发送了#7之后,启动了重传timer,然后收到了#8,解析#8包发现SEQ是正确的,但ACK不正确(因为ACK不正确,无法确定对方是否收到了#7包,因此没有取消掉重传timer),因此无法继续发送其他数据包(无法确定本包是否已被处理,因此重传任务还在),但由于seq是正确的,因此启动了超时回复ack的timer,由于重传timer大于超时回复ack的timer,所以超时回复ack的timer会优先就绪,然后Controller回复了pure ack,在host侧也是同样的逻辑,也会回复一包pure ack,后面双方发送的这两包里面的pure ack中的ACK是正确的,因此双方取消掉了各自的重传任务,撞包处理结束,恢复正常通信。

图片9占位

图9

Link Establishment:

图10

之前介绍的消息通信的事例,都是在Active状态下的正常通信,但是在双方进入Active状态之前,需要先经过其他几个状态,这几个状态是短暂的,但是也是必须的。

Uninitialized:

Host和Controller在上电之后所处的第一个状态,在此状态下,双方会100ms周期发送h5的sync包,同时在收到了对方的sync之后,回复sync response,双方在收到对方的sync response包之后,进入到initialized状态,这个过程是用来双方确定波特率等uart的配置信息的,如果双方不在同一个波特率状态,也无法收到彼此的sync包。sync和sync response同样符合h5格式,他们属于Link Control Packet,packet type = 0x0f。

sync消息:0x01,0x7E。

sync response消息:0x02,0x7D。

Initialized:

进入initialized之后,双方会周期发送h5的config包,同时在收到了对方的config之后,回复config response,双方在收到对方的config response包之后,进入到Active状态,该过程的主要目的是为了协商双方的Sliding Window Size。但是如果收到了上一个状态的sync包消息,也需要回复sync response消息。

config消息:0x03,0xfc,Configuration Field。

config response消息:0x04,0x7B,Configuration Field。

Configration Field包括:Sliding Window Size,OOF Flow Control,Data Integrity Check Type,Version Number。

Sliding Window Size表示本端(Host或者Controller)在收到对端的ack之前,可以发送多少个数据包,这个参数代表该端的处理能力,在config阶段双方交互彼此的Sliding Window Size,然后双方会以最小的Sliding Window Size一方为协商的值,以此进行通信(比如host的Sliding Window Size为2,Controller的Sliding Window Size为3,则使用Sliding Window Size为2作为最终的双方通信配置参数)。

Active:

进入到Active状态之后,双方设备就开始正常通信了,在该状态下,如果收到了对端的config消息,需要回复config response消息,如果收到了对端的sync消息,则证明对端已经重启了,则本端也进行重置操作,重新进入Uninitialized状态。

Sliding Window Size:

本端设备在未收到对端设备的ack之前,可以发送的最大消息包数量(不包括重传消息)。如图11为Slidling Window Size为2的消息交互模型,可以看看到左侧的通信方连续发送了SEQ1和SEQ2的消息。

图11

Recommand Parameters:

slip协议中,时序参数的理论建议值:

Acknowledgment of packets:

ack_timeout = 2 * Tmax。

Resending reliable packets:

resend_timeout = 3 * Tmax。

Tmax的计算方式:

Tmax的计算方式如下图,实际开发中,BLE的单包的最大长度不会是4095,我们以256作为最大值进行计算,Tmax的值为2.7ms。

工程实现的建议值:

resend_timeout的值为3*2.7ms,ack_timeout为2*2.7ms,但是实际工程师,这样的配置会有些太苛刻了,因为代码运行和不同的代码架构也会引入时间开销,某些极端场景,可能host还没有处理完上一包的消息呢,controller的resend_timeout已经到了,已经开始重传了,我的经验值是resend_timeout为50ms,ack_timeout为10ms,但并不适用所有平台,希望大家遇到这个问题时可以根据实际情况调整这两个参数。

Disadvantages and deficiencies:

H5协议有很多优点,但是也需要付出很多代价,带来的开销如下:

  1. uart会变得更繁忙,相比h4协议,h5需要回复ack,因此会使得双方的通信数据变多。

  1. 开发周期更长,h5协议在实现时涉及很多线程同步的逻辑,处理好需要比h4更多的时间。

  1. 实现h5协议会占用更多主控的cpu资源和内存资源。

Suggestion:

H5协议本身并不复杂,但是实现起来需要处理好很多细节,才能开发出稳定运行的代码,本人建议关注的细节有以下几点:

  1. resend timeout一定要比ack timeout大,并且需要根据实际项目的运行情况来配置。

  1. 撞包,重传的部分一定要把逻辑捋清楚,controller和host一定要统一逻辑。

  1. 多线程同步一定要做好,以host的h5代码逻辑实现为例,重传任务,超时回复任务,数据正常发送任务,都会操作uart的tx,如果互斥处理的不好,就会出现异常情况,比如发送了不该发送的pure ack,会给对端的Controller数据包解析造成影响,本人建议只有一个线程可以操作uart tx,其他任务想要操作tx时,向该任务发送消息,这样子互斥问题就没有了,该线程统一管理uart tx资源,uart rx资源同样的逻辑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/185698.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SD卡提示格式化后怎么办?可尝试这种数据恢复方法快速找回!

生活中,虽然我们更习惯用手机、U盘来存储数据,但是对于摄影爱好者,SD卡还是非常刚需。 在使用SD卡存储文件时,经常遇到SD卡无法读取,要求我们格式化后才可以使用。此时,该如何备份里面的数据,或…

IB课程为何号称全球最难国际课程?

在读国际学校的同学们,一定对大名鼎鼎的IB课程不陌生,可是他为什么被称作是它号称最难的国际课程呢?今天就来给大家全面解析一下IB课程~ IB课程最开始是IBO为外交官子女开设全球统一标准的课程。IB课程为全球学生开设从幼儿园到大…

【论文速递】TMM2023 - FECANet:用特征增强的上下文感知网络增强小样本语义分割

【论文速递】TMM2023 - FECANet:用特征增强的上下文感知网络增强小样本语义分割 【论文原文】:FECANet: Boosting Few-Shot Semantic Segmentation with Feature-Enhanced Context-Aware Network 获取地址:https://ieeexplore.ieee.org/stamp/stamp.js…

这款小巧精致的 Keychron K7 满足了我对键盘的所有想象

🔽 前言 博主是一个“练习”时长两年半的前端码农,在练习期间打交道最多的就是键盘,敲得多了懂得也就多了对键盘的要求也变多了。 之前认为,不就一块键盘嘛,能打字就行。 现在认为,键盘必须要有好的手感&a…

Vue前端基于模板实现word导出功能

目录一、依赖二、模板三、代码一、依赖 // 核心依赖 cnpm i docxtemplater3.32.5 cnpm i file-saver2.0.5 cnpm i jszip-utils0.1.0 cnpm i pizzip3.1.3// ui cnpm i element-ui2.15.8二、模板 public 下新建 test.docx 三、代码 <template><div><el-form …

Maven依赖冲突

An attempt was made to call a method that does not exist 依赖冲突完整报错如下 Description:An attempt was made to call a method that does not exist. The attempt was made from the following location:com.baomidou.mybatisplus.extension.plugins.inner.Paginati…

Springboot抑郁症测试系统的设计与实现

在各大医院的教学过程中&#xff0c;用户的抑郁症测试是一项非常重要的事情。随着计算机多媒体技术的发展和网络的普及&#xff0c;“基于网络的学习模式”正悄无声息的改变着传统的抑郁症测试系统&#xff0c;“在线视频、案例展示”的研究和设计也成为教育技术领域的热点课题…

Spring-IOC相关内容

Spring-IOC相关内容 4&#xff0c;IOC相关内容 4.1 bean基础配置 对于bean的配置中&#xff0c;主要会讲解bean基础配置,bean的别名配置,bean的作用范围配置(重点),这三部分内容&#xff1a; 4.1.1 bean基础配置(id与class) 对于bean的基础配置&#xff0c;在前面的案例中…

Chapter2:ROS基础

ROS1{\rm ROS1}ROS1的基础及应用&#xff0c;基于古月的课&#xff0c;各位可以去看&#xff0c;基于hawkbot{\rm hawkbot}hawkbot机器人进行实际操作。 ROS{\rm ROS}ROS版本&#xff1a;ROS1{\rm ROS1}ROS1的Melodic{\rm Melodic}Melodic&#xff1b;实际机器人&#xff1a;Ha…

Magisk内部实现原理

Android10以后&#xff0c;Android系统限制了System分区的修改&#xff0c;结果就是&#xff0c;即使你i是自己编译的Android系统&#xff0c;即使是有做高的root权限&#xff0c;你依然无法挂载System分区并对其内容进行修改,尽管网上有各种帖子说可以使用mount -o rw,remount…

SpringBoot+Vue项目企业客户管理系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏…

Linux(centos7)基本操作---文件管理和用户管理

文件管理 文件的目录结构 文件的目录结构从根&#xff08;/&#xff09;目录开始&#xff0c;主要由一下几个目录组成&#xff0c;之间的作用也是不同的&#xff0c;具体作用如下&#xff1a; bin目录&#xff1a;主要存放系统中的一些基本的有执行权限&#xff08;x&#…

动态AOP 自动以标签 源码解析

Spring AOP 是一个简化版的AOP 实现&#xff0c;并没有提供完整的AOP功能&#xff0c;通常情况下&#xff0c;Spring AOP 是能够满足我们日常开发过程中的大多数场景的&#xff0c;但在某些情况下&#xff0c; 我们可能需要使用Spring AOP 范围外的某些AOP 功能。 AspectJ是一…

【JAVA程序设计】(C00101)基于Servlet的在线鞋店销售管理系统

基于Servlet的在线鞋店销售管理系统项目简介项目获取开发环境项目技术运行截图项目简介 本项目是基于J2EE的servlet的在线鞋店销售管理系统&#xff0c;网上鞋店&#xff0c;球鞋&#xff0c;篮球鞋&#xff0c;跑步鞋&#xff0c;本项目有三种权限&#xff1a;游客、用户、管…

LeetCode 1480. 一维数组的动态和

有人相爱 有人深夜看海 有人LeetCode第一题都做不出来 小趴菜就是我 女神镇楼压压惊 文章目录LeetCode 1480. 一维数组的动态和题目描述&#xff1a;示例1&#xff1a;示例2&#xff1a;示例3&#xff1a;提示&#xff1a;解题思路题解结果展示大神题解执行消耗内存为 0 kb&am…

轻量级 K8S 环境、本地 K8S 环境Minikube,一键使用 (史上最全)

文章很长&#xff0c;而且持续更新&#xff0c;建议收藏起来&#xff0c;慢慢读&#xff01;疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 &#xff1a; 免费赠送 :《尼恩Java面试宝典》 持续更新 史上最全 面试必备 2000页 面试必备 大厂必备 涨薪必备 免费赠送 经典…

基于android的教育机构家校通系统app

需求信息&#xff1a; 客户端老师 1&#xff1a;用户注册与登录 2&#xff1a;添加作业信息&#xff1b;作业包含选择、填空以及简单题 3&#xff1a;查看自己添加的试题信息&#xff1b; 4&#xff1a;对学生提交的作业信息进行查看和批改&#xff1b; 5&#xff1a;和学生进行…

自学Vue开发Dapp去中心化钱包(三)

前言本篇主要记录学习Vue并实际参与完结web3门户项目的经验和走过的弯路。拖了这么久才来还债&#xff0c;说项目忙那是借口&#xff0c;还是因为个人懒&#xff01;从自学到实战Vue实际中间就1周的学习熟悉时间&#xff0c;学习不够深就会造成基础不稳&#xff0c;多次推翻重来…

新的一年里技术管理者(工作者)们如何做好技术规划?

技术管理者的主要工作 技术管理者的主要工作是带人、做事、看方向: 带人是指团队人员能力的培养、团队梯队的建设等等;做事是指完成各项业务需求;看方向是指明确团队未来的发展方向和目标。我们经常会辩论“做管理了还要不要写代码”这个话题,而“写代码”只是“做事”里面…

Java——Maven项目管理

目录Maven1&#xff0c;Maven1.1 Maven简介1.1.1 Maven模型1.1.2 仓库1.3 Maven基本使用1.3.1 Maven 常用命令1.3.2 Maven 生命周期1.4.2 Maven 坐标详解1.4.3 IDEA 创建 Maven项目1.4.4 IDEA 导入 Maven项目1.5 依赖管理1.5.1 使用坐标引入jar包1.5.2 依赖范围Maven 目标 能够…