测试一下 CH585 的蓝牙传输速率 ...... 矜辰所致
前言
我们知道蓝牙 5.x 以后, 使用 LE 2M PHY 模式的空口理论传输速度为 2 Mbps ,也就是 250 kB/s。在实际应用中,由于协议头、握手机制、连接间隔、重传等开销,实际有效值会有折扣 。
对于博主现在学习的 CH585 芯片来说,官方示例里面也提供了 Ble 测试的示例,本文我们就来测试一下 CH585 的 Ble 传输速率。
相关博文:
BLE 蓝牙连接参数详解.
我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!
目录
- 前言
- [一、 基础测试](#一、 基础测试)
- [二、 影响速度的因素](#二、 影响速度的因素)
-
- [2.1 PHY](#2.1 PHY)
- [2.2 MTU](#2.2 MTU)
- [2.3 BLE_BUFF_MAX_LEN](#2.3 BLE_BUFF_MAX_LEN)
- [2.4 单包数据长度](#2.4 单包数据长度)
-
- [2.4.1 MTU 可以比单包数据长度大的意义?](#2.4.1 MTU 可以比单包数据长度大的意义?)
- [2.5 连接间隔](#2.5 连接间隔)
- [2.6 单连接事件发包数量](#2.6 单连接事件发包数量)
- [三、 修改测试](#三、 修改测试)
-
- [3.1 连接间隔修改](#3.1 连接间隔修改)
- [3.2 单连接事件发包数量修改](#3.2 单连接事件发包数量修改)
-
- [3.3.1 主机和从机参数的说明](#3.3.1 主机和从机参数的说明)
- [3.3.2 发送端的 BUFF 与 TX_NUM](#3.3.2 发送端的 BUFF 与 TX_NUM)
- [3.3 单包数据长度修改](#3.3 单包数据长度修改)
- [3.4 测试结果表格](#3.4 测试结果表格)
- 结语
一、 基础测试
先测试一下官方原本的示例,在官方 EVT 种,Ble 速度测试的程序如下:

上面测试是需要两块板子: 一主,一从机 。(手机或者 PC 也可以连接从机测试,但是因为配置问题,速度比官方测试主机例程慢很多,这个在下文会有探讨)。
测试效果如下:

速度大概在 100 kB/s 的样子。
实际上在博主刚开始学习的时候,当时没有注意到需要主机从机配合使用,直接使用了 PC 和 手机连接 蓝牙的测速从机,速度都大打折扣。
当时 PC 使用 dongle ,手机直接连接,未设置直接连接测试的,当时还有点疑问记录了一下 = =!:

上图的速度慢,当时博主就虽然有疑惑,可以肯定的是一定是与主机端的配置有关的,只是当时忙其他事情就放了一段时间。
后来又遇到类似问题,也正确的使用官方主机测试例程得到了正常的测试结果,想着就花点时间再研究一下,多测试一下。
二、 影响速度的因素
本小节是结论,博主修改的测试过程,放在下一小节。
注意,这里说的要素当然是我们应用层可以配置和修改的部分。
2.1 PHY
接收端和发送端的 PHY,肯定都需要配置为 LE_2M_PHY 才能速度最快。
2.2 MTU
MTU 协商,理论最大支持 512 ,一般由主机进行与从机协商,取主机和从机能够支持的最小值。MTU越大,大数据分包越少,效率越高。
512 只是"一次 ATT 写命令"能带的数据上限。
在示例中,通过主机协商 MTU 的大小为:
c
attExchangeMTUReq_t req = {
.clientRxMTU = BLE_BUFF_MAX_LEN - 4,
};
这个BLE_BUFF_MAX_LEN 是 CONFIG.h 中定义的,我们可以通过 IDE 配置定义:

示例中是定义为BLE_BUFF_MAX_LEN=496,所以我们协商 MTU 最大值只能为 492 (下面会一起分析 MTU 与 BLE_BUFF_MAX_LEN)。
2.3 BLE_BUFF_MAX_LEN
这个BLE_BUFF_MAX_LEN 单个连接最大包长度 就要和 MTU 一起说说了。当然,我们速率测试也是遵循BLE_BUFF_MAX_LEN越大,大数据分包越少,效率越高。
他们的关系如下表格:
| 项目 | 所在层 | 谁决定 | 作用 | 与 251 B 关系 |
|---|---|---|---|---|
| MTU | ATT 层(协议) | 主机协商 | 一条 ATT 报文最大长度 | 可 >251(需拆帧) |
| BLE_BUFF_MAX_LEN | 驱动/芯片 | 固件常量 | 整包 L2CAP PDU 缓冲长度 | 必须 ≥ MTU+4,且 ≤ 516(CH5xx 芯片上限) |
为什么 MTU 设置为 BLE_BUFF_MAX_LEN - 4,是因为 L2CAP 头需要占 4 B。 当然我们可以把 MTU 设置得更小也是可以得,MTU 最大只能是 BLE_BUFF_MAX_LEN - 4 。
BLE_BUFF_MAX_LEN 告诉协议栈:
"我这片内存最多能装多长的 一整条 L2CAP 包(含 4 B 头)",
芯片就按这个数去分配 FIFO、DMA 描述符、Rx/Tx 描述块,
如果设置太大而 RAM 不够,初始化直接失败;
如果设小了,> 该长度的包直接被拒收
所以在示例中,已经把这两者放大到尽可能大的地步,但是为什么不是最大的 BLE_BUFF_MAX_LEN 516 和 MTU 512 呢?
实际是因为大这么一些意义不大,还是需要分包发送,最后一包数据量还小,效率反而不高,继续往下看。
2.4 单包数据长度
单包数据长度是用户应用层自己定义的,测试肯定是以单包数据最大值去测试的。
这个地方单包数据长度可以任意定义,过大的数据蓝牙底层会自动分包,这里为了合理的测速,肯定要设置一个合适的长度值。
比如我们的示例中,定义了一个 speed_test_buf[244] 244 长度的数组。
表示,每一包发送数据为 244 字节。
为什么是 244 字节?
这是因为,251 字节是蓝牙底层单包数据的最大值,任何 BLE 5 芯片都不能单帧突破 251 B 。
然后 251 字节,L2CAP 层包头需要占用 4 个字节,就剩下 247 个字节,蓝牙物理层 247 字节一包数据, ATT 层包头需要占用 3 个字节(1 B Opcode + 2 B Handle ),最终用户数据最大只能 244 B。
所以最终示例中,设定的就是协议规定的单包允许的最大值去测试的,不需要分包,可以完美的体现速度。
所以只要 MTU 协商后大于 247 ,就可以满足完成的 244 字节一包的数组测试,所以上面的 BLE_BUFF_MAX_LEN 和 MTU ,大一点小一点对于本测试来说都一样,这里为了让大家更容易明白,我们举个例子:
BLE_BUFF_MAX_LEN 516
MTU 512 (最大为 516 - 4)
speed_test_buf [512] 单包设置为 512 (设置512不符合规范,可能会自动截断或者其他,规范要注意 MTU - 3)
speed_test_buf [509] 单包设置为 509
那么他会怎么拆包发送呢:
关键点记住,用户数据最大一帧为 244 B 就行了
它会分成 3 包 : 244 B + 244 B + 21 B 发送
2.4.1 MTU 可以比单包数据长度大的意义?
这里额外提一个问题,既然蓝牙底层都规定了最大一包为 251 字节,那么为什么还有 MTU 可以协商为 512 呢? 意义何在?
虽然说 用户数据 >244 字节会被 L2CAP 自动拆成多帧发出去 ,但是 MTU 设大可以提高的 "单次系统调用效率" ,具体如下:
- 减少 CPU 中断次数:原来要 3 次 write,现在 1 次搞定;这样同时也可以降低功耗,因为唤醒次数变小了;
- 提高吞吐量:帧头/间隔开销固定,大包拆帧比多次小包写更快;
- 协议栈统一:上层不用关心怎么拆,只管 ≤509 B 即可。
应用场景:
在文件透传,固件升级的时候,一般都会把 MTU 设置最大,一次写 509 B,芯片自动拆成多帧,省 CPU、省电、提速。
说到这里我也产生了一个问题,示例设置为 244 一包数据,如果我设置为 488 ,让芯片自动分包变成 2 包,会不会速度更快? 这个下文要测试一下!
2.5 连接间隔
连接间隔也是影响速率测试的关键因素,连接间隔越小,响应越快,理论来说速率更大,但是这还要结合单次连接事件发包数量。
如果连接间隔太短,会导致每次通信发送数据包未达到设置最大发送包数。
如果连接间隔太长,会导致每个连接周期发完所有包之后,空闲,影响速率测试(这个会在下面一起举例分析)。
关于连接参数,可以参考我以前的博文 BLE 蓝牙连接参数详解
Ble 最小周期是7.5 ms,从机只请求,通信周期由主机决定。
- 开发板,可以设置到最小的 6(7.5ms)
- 安卓最小到 9( 11.25ms )
- 苹果最小到 12(15ms)
在示例中,主机设置的连接间隔为 7(8.75ms ):
c
// Connection min interval in 1.25ms
#define DEFAULT_MIN_CONNECTION_INTERVAL 7
// Connection max interval in 1.25ms
#define DEFAULT_MAX_CONNECTION_INTERVAL 7
这个下面我们也需要修改测试。
2.6 单连接事件发包数量
顾名思义,这个很好理解,就是每个连接周期里面,发多少数据包。
在示例中是通过 BLE_TX_NUM_EVENT 这个宏定义的,从机默认为:

因为测速是从机发,这里主机默认为 10。
所以例程的测试思路就是,单包数据长度为 244 字节,连接间隔为 8.75ms, 在每8.75 ms 的事件内发送 6 包244 字节的用户数据。
设置是这么设置的,能不能发送完是另外一回事,这就是为什么连接间隔和发包数量影响测试结果:
假如 连接间隔为 7.5ms ,它在这么短的事件发送不完 6 包 244 字节,反而速度下降,
假如 连接间隔为 12.5ms ,它发送完 6 包以后时间有多,完全浪费了,也会导致整体测速下降。
好了,介绍到这里,我们接下来进行一些测试。
三、 修改测试
按照示例测试我们得出的 CH585 的 Ble 速率 为 100kB/s 。
本小节我们做一些修改,看看参数的改变对它速率的影响。
首先我们还要说明一下,主机和从机交互,都会在连接的时候协商好:

这个是流程代码框架已经实现好的,我们不去修改这个流程,只需要配置我们对应的参数。
3.1 连接间隔修改
首先是连接间隔,目前为 7 ,但是最小支持6,看看把它设置为最最小的时候速率是否更快,连接间隔由主机决定,我们直接在主机修改宏定义:
c
// Connection min interval in 1.25ms
#define DEFAULT_MIN_CONNECTION_INTERVAL 6
// Connection max interval in 1.25ms
#define DEFAULT_MAX_CONNECTION_INTERVAL 6
看一下测试结果:

这里连接间隔变小了,速率反而下降了,这就是我们上文提到的问题,很有可能是因为一个连接周期,数据包没发送完全,数据是从机发送的,我们看一下从机的单连接事件发包数量:

我们还可以通过功耗仪,看一看从机发送的情况:

可以看到,7.5ms 的一个连接周期内,从机只发送了3包数据。
变小不行,我们把连接间隔放大到 8 ,发现速率反而上升了:

功耗仪器再看一下发包数量:

通过这样,我感觉要发送到 6 包,还可以往上加间隔,感觉速率还能提升,于是继续加大间隔,变成 9(11.25ms):

果然速率又提升了,这里我们可以想象得到,9 得连接间隔已经可以达到我们设置的单连接事件最大发包数量,如果我们不修改最大发包数量,继续加大连接间隔,速率肯定会下降的,博主还是测试了下:

3.2 单连接事件发包数量修改
实际上通过上面的测试,结合上文的分析,我们可以推测,只要在一个连接间隔内,完美的把能够发送的数据的时间全部利用上(因为还有一些时间协议栈需要拿来处理一些自己的流程,比如睡眠,唤醒,校对时间等),就能够达到一个最大的速率。
在上面我们测试的过程中,如果发包数量为 6 ,只有当连接间隔为 9 的时候,正好发送完成 6包,所以达到了最大的速率。
这里我们还可以考虑一个问题,连接时间太短,频繁的交互,每次协议栈都需要多浪费一些时间,每次连接间隔协议栈都需要自己占用一些时间,如果我们把连接间隔加大,单周期发送数据量放大,是否会更快一点呢?
尝试一下,我们先把对应的地方修改一下(主机和从机都改了,是因为刚开始测试的时候博主还不是很清楚,所以两边改成都支持,省得遇到不必要的问题,下文有参数说明):

然后再主机工程中,把连接间隔修改为 18 ,按理解,应该是能够发完 12 包,估计还有时间多。
但是测试下来,发现反而有问题了:

反而只有 5 包,速度也大打折扣 !!!
经过一段时间的测试,是因为设置这么大,蓝牙部分内存不够了,我把主机工程中的BLE_MEMHEAP_SIZE 从 1024*6 改成了 1024*8 ,就正常了(下图的参数设置可以优化,因为测试时候省得遇到不确定得问题,一并设置得,具体的可以查看下一小节的参数说明。):
c
#define BLE_MEMHEAP_SIZE (1024*8)//

这里要说明一下,具体是 BLE_TX_NUM_EVENT 还是BLE_BUFF_NUM 占用空间自己不是很确定,个人更加偏向于BLE_BUFF_NUM 加大,需要更多得占用内存,这里只是发现了这个问题,提出来的一种解决办法。
3.3.1 主机和从机参数的说明
还是因为上面的问题,在上面一些参数设置的时候,我都是主机从机一起设置,不管收发端,都设置到满足条件,所以在上面接收端的主机内存不够的时候,不确定是BLE_TX_NUM_EVENT 还是BLE_BUFF_NUM 占用空间,直到想说明一个问题:
这里的 BLE_BUFF_NUM 要比 BLE_TX_NUM_EVENT 大,BLE_BUFF_NUM 比BLE_TX_NUM_EVENT 大才能正常接收完,要不然设置为几个,一个周期最大只能接收几包 。
在想说明这个问题的时候,博主也测试了一下,测试完这个,可以更加确定的几点问题:
- 接收端的
BLE_BUFF_NUM要比发射端的BLE_TX_NUM_EVENT大,才能在一个周期接收完成设置的包数。BLE_BUFF_NUM设置为 a,单连接周期最大接收的包数就为 a。 BLE_BUFF_NUM越大,所需要的内存越多BLE_TX_NUM_EVENT只与发送端有关,可以把接收端主机的BLE_TX_NUM_EVENT设置为 1,也不影响测试。
上面说了接收端的 BLE_BUFF_NUM,发送端的 BLE_BUFF_NUM 有没有关系呢?
3.3.2 发送端的 BUFF 与 TX_NUM
发送端的 BLE_BUFF_NUM 也需要比自己发送端的 BLE_TX_NUM_EVENT 大!
一定是要大于哦,即便相等都会影响数据交互。看下面的测试把:
先设置小一点(数据发不完):

额外说明一下,上面可以看到,从机已经把蓝牙占用的内存设置为了1024*10 的大小。
如果 BLE_BUFF_NUM 和 BLE_TX_NUM_EVENT 相等:

数据达不到最大,查看一下,不一定每次都能发送完成:

把 BLE_BUFF_NUM 设置比 BLE_TX_NUM_EVENT 大:

这种速率,猜想也应该知道,每包肯定能完整得发送到12 包数据,还是抓了一下看:

这里的测试明确了几个参数的设置关系,测试下来,并不会比我们最开始那种设置速率有提升。但是!!!我们是按照最初的连接间隔和包数判断,连接间隔18 肯定可以发完 12 包,我们才设置的 12 包,通过上面的图像我感觉还可以多1包,于是我把发送端的包数在上面的基础上改成了 13 :

果然可以,速度又快了!!

继续增加,发送 14 包:


测试到这里,基本上也达到了测试效果,在连接间隔能够发完的情况下,单连接事件发包数肯定是越大越好。
而且连接间隔增加,会在一定程度上,省去每次连接间隔之间的固定交互时间,可以多发那么一两包,提高速度。但是代价就是更大的内存占用。
3.3 单包数据长度修改
最后我们再来测试一下协议栈的自动分包处理,看看会不会对速度有影响,我们把每次发送的字节改成 488 ,我们按照原来的间隔 18 ,每次 12 包来算,协议栈自动拆包,我们是不是需要把每次改成 6 包,一包 488 来呢,测试一下:

看来就算协议栈拆包,也需要这个硬性支持,那我们该回去把 BLE_TX_NUM_EVENT 设置为 12 ,测试下来和前面差不多,也是 120kB/s , 这里就不上图了。
我们尝试设置为 14,速率又上升了:


至于这种方式是不是能够又明显提升,看起来好像是这样,毕竟已经算是比较极限的测试了,所能提高的空间也是有限的,这里个人测试大家参考一下就行了。
3.4 测试结果表格
最后,还是把本次测试的结果列一个表格(个人测试,仅供参考):

连接间隔:单位 1.25ms
6:7.5ms 7:8.75ms ... 18: 22.5ms
单包数据长度 : speed_test_buf[] (从机)
单连接事件发包数量: BLE_TX_NUM_EVENT(从机)
连接间隔 : CONNECTION_INTERVAL (主机)
上面最后两个单包数据长度虽然我们设定为 488 ,但是我们知道蓝牙底层会自动分包,所以对于测试来看,还是发完 12 包/ 14 包数据。
测试条件:2M_PHY
BLE_BUFF_MAX_LEN 496
MTU设置为 492
单包最大长度理论值为 MTU-3 = 489 ,这里保证了2包都足够
当然还要确保BLE_BUFF_NUM 大于 BLE_TX_NUM_EVENT,尽可能的大
BLE_BUFF_NUM 越大内存占用越多
结语
本文我们测试了一下沁恒微 CH585 Ble 的传输速率,还给大家说明了影响 Ble 传输速率的各种配置。让大家以后在应用的时候,能够合理的利用配置进行数据交互。
好了,本文就到这里。谢谢大家!