Chinaunix首页 | 论坛 | 博客

fx

  • 博客访问: 1372373
  • 博文数量: 115
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 3964
  • 用 户 组: 普通用户
  • 注册时间: 2013-05-02 14:36
文章分类
文章存档

2022年(2)

2019年(2)

2018年(10)

2017年(1)

2016年(50)

2015年(12)

2014年(9)

2013年(29)

分类: 嵌入式

2018-07-18 14:30:24

软件环境:nRF5_SDK_13.1.0_7ca7556\examples\ble_peripheral\ble_app_uart

 

使用nordic旧的SDK开发时,需要进行ble传输数据时通常都是将待发送的数据分包成20字节一包,之后再一包一包发送出去。

为什么要分成20字节一包? 因为我们发送的数据是负载在 ATT层的,BLE规范定义的ATT层默认大小为23,其中3字节为协议数据。所以能负载的用户数据只有20字节。即 ATT MTU默认值为23

如下图,使用notification发送数据时的格式,对于ATT MTU23时来说,attribute value只能负载20字节。


Ble规范中定义了针对这个 ATT MTU实际定义了一个 MTU协商过程,只不过nordic旧版本SDK中没有对这个协商过程提供支持。所以以前的开发中都是将数据分成20字节一包再调用API发送。

新的sdk中现在可以支持MTU 协商了。这有什么好处? MTU更大,如果数据量比较小的话,甚至不用做分包。另外MTU更大,需要分包的数量更少,则较少了分包工作时间的消耗。

 

Nordic协议栈默认配置下一般一个连接事件中最大可以发送的应用数据包的个数 不同的版本会有一些差别 不过一般都是在 6-8应用数据包左右。

Ps: 这里的默认配置下是指 没有修改底层radio参数以及连接间隔不是非常小,比如小于10ms的情况下。 同样 ATT MTU也不能太大,因为调用数据发送API时,数据实际上是存储在底层协议栈的buff中的,如果ATT MTU设置比较大,你每次调用发送函数都发送MTU大小的一包数据,那么底层的buff是不够存放6包数据的。

 

所以在一个连接事件可以发送6MTU大小的长度包的情况下,如果连接事件为20ms1s可以交互50次, 那么数据传输速率就有 50*6 *(MTU-3)/1000  KB/s

 

所以对于旧的sdk来说,只支持MTU23字节则理论应用数据传输速率为  6KB/s

而新SDK支持了ATT MTU协商后,协商一个更大的MTU就可以提高数据传输速率。例如将MTU 修改成64,则理论上传输速率可以 >18KB/s

综上,对于nordic协议栈来说,通过增加 MTU 的适当大小充分利用协议栈提供的缓存资源和发送能力,可以明显提高从机端的发送速率。

 

下面实验并观察修改ATT MTU大小后的数据传输效果

首先在main函数中调用了gatt_init 初始化函数

因为使用的是 ble_app_uart例子,改例子是作为外围设备的,所以这里调用了外围设备设置ATT MTU的函数来设置想要修改的ATT MTU,这里设置成64

上面只是在初始化时设置了期望协商MTU,并没有实际发起ATT MTU的协商。

真正的协商启动是在连接后进行的。

 

如下图,收到连接事件后进入 gatt的连接事件处理函数中,首先获取了上面初始化时设置的外围设备期望的MTU,之后判断期望MTU比当前有效MTU大,则会启动MTU协商请求。

当前有效MTU初始值为 默认MTU23字节。

PS:MTU并不是直接设置的就有效的,连接的两端有一个协商过程,最终的MTU是两边设置的MTU中的小的那个值。因为手机一般支持的值比较大,所以通常你设置的MTU不是很大时最终协商的结果就是你设置的值。

这里测试一个简单的例子,设置MTU64字节,然后连续调用6次发送API每次都发送MTU-3字节的数据,通过抓包工具查看是否数据发送都在一个连接事件中。如果都在一个连接事件中,那么就如上面的计算,通过适当增加ATT MTU的大小可以提高数据传输速率。


点击(此处)折叠或打开

  1. void TestSend(void)
  2. {
  3.     uint32_t err;
  4.     uint8_t packLen;
  5.     uint16_t offset = 0;
  6.     uint8_t buff[500];
  7.     uint16_t index = 0;
  8.     
  9.     for(uint8_t i = 0; i<(MY_ATT_MTU-3); i++ )
  10.     {
  11.         buff[index++] = 0;
  12.     }
  13.     for(uint8_t i = 0; i<(MY_ATT_MTU-3); i++ )
  14.     {
  15.         buff[index++] = 1;
  16.     }
  17.     for(uint8_t i = 0; i<(MY_ATT_MTU-3); i++ )
  18.     {
  19.         buff[index++] = 0;
  20.     }
  21.     for(uint8_t i = 0; i<(MY_ATT_MTU-3); i++ )
  22.     {
  23.         buff[index++] = 1;
  24.     }
  25.     for(uint8_t i = 0; i<(MY_ATT_MTU-3); i++ )
  26.     {
  27.         buff[index++] = 0;
  28.     }
  29.     for(uint8_t i = 0; i<(MY_ATT_MTU-3); i++ )
  30.     {
  31.         buff[index++] = 0;
  32.     }
  33.     printf("send data len:%d\r\n",index);

  34.     while(offset < index)
  35.     {
  36.         packLen = (index-offset)>(MY_ATT_MTU-3)?(MY_ATT_MTU-3):(index-offset);
  37.         
  38.         err = ble_nus_string_send(&m_nus, buff+offset, packLen);
  39.         offset += packLen;
  40.         printf("err:%x\r\n",err);
  41.     }
  42. }

点击(此处)折叠或打开


  1. Main函数中做如下修改,按键是调用发送测试函数

  2. Int Main(void )
  3. {
  4.     …………….
  5.     …………….
  6.     nrf_gpio_cfg_input(16, NRF_GPIO_PIN_PULLUP);
  7.     for (;;)
  8.     {
  9.         if ( nrf_gpio_pin_read(16) == 0 )
  10.         {
  11.             nrf_delay_ms(10);
  12.             
  13.             if ( nrf_gpio_pin_read(16) == 0 )
  14.             TestSend();
  15.             while(nrf_gpio_pin_read(16) == 0);
  16.         }
  17.     }
  18. }

烧录后,手机连接设备。并使能Tx characteristicnotify功能。

之后按键即可测试发送。抓包截图如下:

 

首先连接后就会立刻进行MTU协商,协商结果就是我们设置的64字节。



下图是按键使用notify发送数据给手机的截图。我们实际调用了6次发送函数,每次发送61字节。不过截图这里只能截下2次的发送结果。

从最左侧可以看到 通道号都为 0x05,即数据传输都在一个连接事件中。调用一次API发送了61字节数据,因为底层链路层有长度限制,所以被分成3包发送,不过这是自动进行的,不需要我们自己去分包。

PS:这里我们使用了MTU64,连续调用了6次发送API都可以成功,如果MTU设置的比较大,那么可能并不能6次都调用API都能成功。如前文中所述,因为协议栈底层的缓存buff也是有限的, MTU64时,你连续调用6API,底层协议栈的缓存可以存下这么多数据。 如果MTU比较大,那么底层协议栈缓存可能就存不下6包数据了。你们可以自己试验下。改大点MTU后连续调用的结果,应该不到6次就会返回资源不足的错误。



阅读(3244) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~