在对协议栈在 Bluenrg2 芯片上采用 SPI 作为 HCI 的数据传输进行测试的时候,发现存在丢包问题。当进行大吞吐连续传输时,可以发现协议栈收到的字节数少于测试APP发送的字节数。
首先需要找到丢包的位置,有多个可能:①在HCI层传输上报给协议栈上层的过程丢包;②在HCI层与芯片进行SPI通信时丢包;③在芯片接收和上报的过程丢包(按说可能性不大)。
使用在每一层计数接收到的数据的字节数,进行比较的方式确定产生丢包的位置。
1 在HCI层传输上报给协议栈上层的过程丢包
 在 HCI 层的 SPI 初始化hci_driver_init()处也设置一个打印当前接收的字节数的定时任务:
HCI_SPI_ACL_recv_count = 0;
k_timer_init(&HCI_SPI_count_work, HCI_SPI_count_timeout, NULL);
k_timer_start(&HCI_SPI_count_work, K_SECONDS(30), K_SECONDS(30));
void HCI_SPI_count_timeout(struct k_timer *timer)
 {
 printf("HCI SPI ACL recv count timeout: %dn", HCI_SPI_ACL_recv_count);
 }
 在HCI层的接收函数hci_driver_init_loop()处,对ACL数据包进行判断和计数。
switch(data[0])
 {
 case HCI_EVENT_PKT:
 buf = bt_buf_get_controller_tx_evt();
 break;
 case HCI_ACLDATA_PKT:
 buf = bt_buf_get_controller_tx_acl();
 HCI_SPI_ACL_recv_count += ret;
 // printk("ACL: ");
 // for (int i = 0; i < ret; ++i)
 // {
 // printk("%02x:",data[i]);
 // }
 // printk("n");
 break;
 default:
 return;
 }
测试
手机连接BLE模块,发送间隔设定为1ms,数据包大小20字节,测试得到打印结果。比较发现,协议栈上层的计数比HCI层接收处的数据计数更少,在HCI层传输上报给协议栈上层的过程有丢包。
检查HCI层接收数据和上报的代码,发现当数据传输量很大,MCU来不及处理时,协议栈上层的接收队列会堆积最后爆满,HCI 层申请 buffer 的时候可能失败,此时本次从芯片处接收到的数据就会被丢弃。
解决方案是在开始一次 SPI 接收之前,判断当前的缓冲区是否还有空间,有空间才接收。一般来说(事实上最后发现这个芯片好像并不是这样的),芯片收到的数据如果一直没有被HCI层接收,芯片端的缓冲区满了之后,芯片会暂停数据传输服务。这样可以使发送端的传输暂停,等待MCU完成处理后再继续传输,避免丢包。
if (bt_buf_reserve_size(BT_BUF_ACL_IN) == 0)
 {
 printk("HCI ACL BUFFER EMPTY rn");
 return;
 }
int ret = HCI_TL_SPI_Receive(data, len); //ret: bytes num Recv
再次测试发现,HCI层接收到的字节数和协议栈上层接收的字节数一致,但手机端发送的字节数和协议栈上层接收的字节数还是不一致,丢包还是存在。
2 在芯片接收和上报的过程丢包
在 SPI 的接收函数HCI_TL_SPI_Receive()处也加入一个计数HCI_SPI_ACL_recv_count,计数从芯片处接收到的全部数据,包括包头等;在HCI层将数据包塞入缓冲区之后,加入一个计数HCI_ACL_buf_recv_count,计数buffer缓冲区内的数据字节数(不包括包头)。
四个计数分别是:
HCI_SPI_recv_count_timeout: 从芯片处通过SPI接收到的全部数据包(包括包头)
HCI SPI ACL recv count timeout: HCI层接收到的ACL数据包的数据(仅数据,不包括包头等)
HCI_ACL_buf_recv_count_timeout: HCI层写入缓冲区后,缓冲区内的data字段里的数据(仅数据,不包括包头等)
app_count: 协议栈上层接收到的数据(仅数据,不包括包头等)
因为蓝牙启动的过程中也有一系列数据交互,为了确保计数的准确性,加入一个开始计数的HCISPIFlag,HCISPIFlag为true时才开始计数。当HCI层接收到手机端发送的99:99:99:99数据包(该数据包不会上报给协议栈上层)时,HCISPIFlag转为true。
ACL数据包的包头为12字节,例如测试数据包的内容:
02:01:281b00:17:00:04:00:520b00:00:01:02:03:04:05:06:07:08:09:00:01:02:03:04:05:06:07:08:09
开始计数的命令判断:
bool HCIcountCmdCheck(uint8_t *buf) {
 uint8_t cmd[4] = {0x99, 0x99, 0x99, 0x99};
 for (int i = 0; i < 4; ++i)
 {
 if (buf[i + 12] != cmd[i]) {
 return false;
 }
 }
 return true;
 }
 HCI层SPI接收处的计数:
if(byte_count > 0)
 {
 /* avoid to read more data than the size of the buffer /
 if (byte_count > size)
 {
 byte_count = size;
 }
 for(len = 0; len < byte_count; len++)
 {
 rt_spi_transfer(ble_spi, &char_00, (uint8_t )&read_char, 1);
 buffer[len] = read_char;
 }
 HCI_SPI_recv_count += len;
 // ACL pack received count
 if (HCISPIFlag)
 {
 if (buffer[0] == HCI_ACLDATA_PKT) {
 HCI_SPI_ACL_recv_count += (len - 12);
 }
 }
 }
测试
手机连接BLE模块,发送间隔设定为1ms,数据包大小20字节,测试得到打印结果:
[00:26:45.218]收←◆Connected
 [00:27:05.152]收←◆HCI_SPI_recv_count_timeout: 660
 HCI SPI ACL recv count timeout: 0
 HCI_ACL_buf_recv_count_timeout: 0
 [00:27:05.302]收←◆app count timeout: 0
 [00:27:08.899]收←◆HCI count start
 app count start
 [00:27:35.134]收←◆HCI_SPI_recv_count_timeout: 302404
 HCI SPI ACL recv count timeout: 188580
 HCI_ACL_buf_recv_count_timeout: 188580
 [00:27:35.284]收←◆app count timeout: 189840
 [00:28:05.116]收←◆HCI_SPI_recv_count_timeout: 720932
 HCI SPI ACL recv count timeout: 450160
 HCI_ACL_buf_recv_count_timeout: 450160
 [00:28:05.267]收←◆app count timeout: 451560
 [00:28:35.096]收←◆HCI_SPI_recv_count_timeout: 1143556
 HCI SPI ACL recv count timeout: 714300
 HCI_ACL_buf_recv_count_timeout: 714300
 [00:28:35.246]收←◆app count timeout: 715480
 [00:29:05.081]收←◆HCI_SPI_recv_count_timeout: 1579780
 HCI SPI ACL recv count timeout: 986940
 HCI_ACL_buf_recv_count_timeout: 986940
 [00:29:05.231]收←◆app count timeout: 988100
 [00:29:35.066]收←◆HCI_SPI_recv_count_timeout: 1726692
 HCI SPI ACL recv count timeout: 1078760
 HCI_ACL_buf_recv_count_timeout: 1078760
 [00:29:35.215]收←◆app count timeout: 1078760
手机APP端:

手机端发送 55276个包,共1105504字节,其中20字节的测试数据包 55275个,共1105500字节。
协议栈上层收到1078760字节数据,即53938个数据包; HCI层接收到的 ACL 数据包的数据字节数和 HCI 层写入缓冲区的data字段里的数据字节数,与协议栈上层的一致(最终一致,中间定时器打印的count数不一致是因为缓冲区的数据还未被取出)。从芯片处通过SPI接收到的全部数据包为1726692字节,其中660字节为启动阶段传输。
ACL 数据包的包头为12字节,发送的命令 ACL 包为16字节,测试ACL数据包为32字节。则实际接收到的测试ACL数据包为(1726692 - 660 - 16) / 32 = 53938个,与协议栈上层的一致。
对比发现,在芯片接收手机数据和上报的过程中发生了丢包。一般来说,芯片收到的数据如果一直没有被HCI层接收,芯片端的缓冲区满了之后,芯片会阻止发送端(手机)继续发送数据。
- 
                                接收机
                                +关注
关注
8文章
1181浏览量
53469 - 
                                SPI接口
                                +关注
关注
0文章
258浏览量
34381 - 
                                BLE威廉希尔官方网站
                                +关注
关注
0文章
28浏览量
5852 - 
                                MCU芯片
                                +关注
关注
3文章
252浏览量
11446 - 
                                RTThread
                                +关注
关注
8文章
132浏览量
40873 
发布评论请先 登录
相关推荐
RTT_Zephyr_Polling BlueNRG2 SPI使用说明
    
RTT平台zephyr_polling软件包SPI Bluenrg2芯片宕机问题与修复
    
RTT zephyr_polling软件包 Bluenrg2蓝牙芯片启动流程
    
如何用Ubuntu qemu跑zephyr_polling的蓝牙?
    
SPI驱动屏幕移植LVGL软件包具体流程
RT-Thread 软件包介绍
STM32F103C8 使用RT-Thread软件包系统读取MPU6050
    
什么是Linux软件包,如何管理它们
RT-Thread在线软件包改为本地软件包的方法
    
RTT zephyr_polling SPI Bluenrg2数据传输测试
    
RT-Thread平台 zephyr_polling软件包 Bluenrg2 蓝牙芯片启动流程
    
          
        
        
RTT平台zephyr_polling软件包SPI Bluenrg2丢包问题排查
 
    
           
            
            
                
            
评论