libusb的libusb_bulk_transfer函数超时

win10 64系统,libusb 1.0,用zadig安装的驱动。目标设备采用bulk传输,一问一答的方式,既PC向目标板(EndPoint 0x01)发一条数据,再从目标板(EndPoint 0x81)读回来一组数据。写的数据长度为8字节,读不到300字节。因为通讯很频繁,且对时间比较敏感,所以需要控制完成一次读写(一读一写)所需的时间。这个时间发现经常会在30ms左右,且很少有28ms~5ms之间的值(希望是在5ms以内,其中2ms是目标板卡执行指令的时间,这个时间是目标板定时器决定的)。已经设置数据交互进程的优先级为RealTime。用QueryPerformanceCounter和QueryPerformanceFrequency作定时器判断执行的时间.
主要部分代码如下:

int init()
{
  ....
    if(libusb_init(&ctx))
   {
       return -1;
   }
    cnt = libusb_get_device_list(ctx, &dev_list);
    if(cnt < 0)
    {
         libusb_exit(ctx);
         return -2;
    }
   for(int i = 0; i < cnt; i ++)
   {
          if(libusb_get_device_descriptor(dev_list[i], &dev_desc[i]))
          {
                  return -3;
          }
          .......
   }
   return cnt;
}

int open(int dev)
{
     .......
     libusb_open(...,...);
     ......
     libusb_set_auto_detach_kernel_driver(......,  1);
     libusb_claim_interface(......,0);
}
int write(int dev, uchar* buf, int len, int ot_ms)
{
       ......
        rtn = libusb_bulk_transfer(h_dev[id],1, buf,len, &rel_len,ot_ms );
       ......
}
int read(int dev, uchar* buf, int len, int ot_ms)
{
      {
       ......
        rtn = libusb_bulk_transfer(h_dev[id],0X81, buf,len, &rel_len,ot_ms );
       ......
    }
}
void test(int retry)
{
  double ms;
  for(int i = 0; i < retry; i ++)
  {
    timer_start();
    EnterCriticalSection(&cs);
    rtn = write(......);
    if(rtn)
    {
        .......
    }
    //也曾经在这里timer_start,用来卡read的时间,发现实际上超时的都是read
    rtn =read(......);
    LeaveCriticalSection(&cs);
    if(rtn)
   {
      ..........
   }
    ms = timer_get_ms();
    if(ms > 5)
    {
        ..............
    }
    timer_dly_ms(1);
  }
}

用程序卡时间,发现每次都是read超时。但用BUS Hound抓包,与程序的执行略有不同,每次超时之后,下一次write的时间点,与超时当次read的时间点差距,正好接近超时多出来的时间。怀疑只是Bus Hound计的时间点是write或者read的开始时间点所导致。即便超时,Bus Hound抓到的数据本身也是对的。尝试过缩短read每一包数据的长度到64字节以内,发现依然会有类似的情况。
请问这个可能是什么原因造成的?是否有改善的思路?如何改善?谢谢

补充一点,libusb_bulk_transfer函数 的超时时间其实为这里只设置了5ms,但是一次数据读写实际执行的时间却有可能超过28ms,这期间并没有发现libusb返回超时错误,这个超时是我自己用QueryPerformanceFrequency和QueryPerformanceCounter测量的时间超时。

可以使用了一个读空的操作,即在建立usb设备与上位机的连接之前,有一个读空的步骤,清除之前连接遗留的数据。这个地方调用了libusb_bulk_transfer,这里本身读的就是空值,一直没有能读到其他的值,导致读取超时,出现了TIMEOUT_ERROR,因此在调用libusb_bulk_transfer之前要调用libusb_clear_halt()这个指令,就可以顺利读取

该回答通过自己思路及引用到GPTᴼᴾᴱᴺᴬᴵ搜索,得到内容具体如下:

从代码和描述来看,您的程序使用的是libusb库进行USB通讯,在写入和读取数据时,使用的是libusb_bulk_transfer函数,并设置了超时时间ot_ms。您的目标是控制一次读写(一读一写)所需的时间,并且希望这个时间在5ms以内。但是实际测试中,发现经常会在30ms左右,很少有在5ms以内的情况,且每次超时都是在读取(read)数据时发生的。

可能的原因:

1. USB总线带宽限制:USB总线的带宽是有限的,如果数据量较大,可能会导致读写数据的时间较长。您可以通过缩短每一包数据的长度来测试是否有改善。

2. 系统调度和中断处理:在Windows系统中,有很多后台任务和中断处理程序,这些可能会干扰您的程序执行,导致读写时间不稳定。您可以尝试使用实时线程或者提高线程的优先级来减少系统干扰。

3. USB设备响应时间:USB设备的响应时间也会影响读写时间,如果设备响应时间较长,可能会导致读写时间较长。您可以查看设备的响应时间,并根据需要优化设备响应时间。

改善思路:

1. 使用异步传输:libusb支持异步传输,使用异步传输可以减少读写时间,提高程序的响应速度。您可以尝试使用libusb_submit_transfer函数进行异步传输,这样就可以在等待数据传输完成的同时,执行其他操作,从而提高程序的并发性。

2. 使用多线程:使用多线程可以使程序更加并发,提高程序的响应速度。您可以将读写操作放在不同的线程中执行,这样可以避免读写操作之间的互相干扰,从而提高程序的稳定性。

3. 优化USB设备:如果USB设备的响应时间较长,可以尝试优化设备的响应时间,例如使用更快的处理器或者优化设备驱动程序等。

总之,USB通讯的时间受到多种因素的影响,需要全面考虑。您可以综合考虑以上因素,选择合适的优化方式来改善程序的执行效率。


如果以上回答对您有所帮助,点击一下采纳该答案~谢谢

引用ChatGPT部分参考回答:
根据你提供的代码和描述,可能存在以下几个原因:
1、数据传输量过大:你描述了读取的数据量超过了300字节,虽然Bulk传输方式可以进行大数据传输,但也需要考虑传输的带宽和速度。如果数据传输量过大,可能会导致传输时间过长,而且读写间隔时间也不确定,从而影响到你的时间控制。
解决方法:减少一次读写的数据量,分为多次读写。
2、传输过程中出现响应丢失或延迟:USB传输是一种异步传输方式,数据在USB总线上传输需要经过多个操作才能完成,例如芯片的中断处理等。另外,在使用Bulk传输方式时,会有一些特殊间隔和重传机制,如果出现响应丢失或延迟,可能会导致一次读写时间过长,进而影响你的时间控制。
解决方法:可以考虑使用同步传输方式或者其他更可靠的传输方式,例如Isochronous或Interrupt传输。
3、操作系统或硬件原因:操作系统和硬件也可能会对USB通信时间产生影响,例如CPU占用率过高、中断处理时间过长等,可能会导致USB通信的响应时间变慢。
解决方法:可以尝试优化操作系统或硬件环境,例如关闭不需要的进程或服务、优化中断设置等。
综上所述,建议你可以:
1、调整一次读写的数据量,分为多次读写,避免一次传输过长。
2、尝试使用其他可靠的传输方式,例如Isochronous或Interrupt传输,观察结果。
3、优化操作系统或硬件环境,例如关闭不必要的进程或服务、优化中断设置等。
4、尝试在程序中加入错误处理机制,例如重传机制,以保证数据的完整性。

给出如下的完善建议:
1、在进行USB通信之前,最好打开实时权限,这样可以使USB通信有更高的传输优先级。
例如,可以添加以下代码:

int r = libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);//调试等级
if(r!=0){......}
r = libusb_set_option(NULL, LIBUSB_OPTION_USE_USBDK, 1);
if(r!=0){......}

2、在数据读取和写入的时候,应该通过设置超时时间,以减少数据传输的等待时间。例如,可以在 write() 和 read() 函数中添加以下代码:

libusb_set_option(h_dev[id], LIBUSB_OPTION_TIMEOUT, ot_ms);

这将设置超时时间为 ot_ms ,防止循环等待时间过长。
3、在进行USB通信时,可以将读写数据的操作限制在一个线程或者进程中,避免多个线程进行USB读写操作,从而引发操作系统的调度和时间问题。
例如,可以将 test() 函数中的循环操作限制在一个线程中:

void worker_thread(void* arg)
{
    // 在这里调用 test() 函数
}
int main()
{
    // ...
    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)worker_thread, NULL, 0, NULL);
    // ...
}

4、如果需要更精确的时间控制,可以使用更高精度的计时器。例如,可以使用 Windows API 中的 QueryProcessCycleTime() 函数获取进程周期计数器。
这是一个可能更精确的计时器,可参考以下代码:

LARGE_INTEGER start_cycles, end_cycles, freq;
QueryProcessCycleTime(GetCurrentProcess(), &start_cycles);
//执行 usb 操作
QueryProcessCycleTime(GetCurrentProcess(), &end_cycles);
QueryPerformanceFrequency(&freq);
double elapsed_time = (end_cycles.QuadPart - start_cycles.QuadPart) / (double)freq.QuadPart;

5、另外,如果需要在多个操作间进行同步、互斥操作,建议使用更高级的线程同步机制,例如互斥锁或信号量。
例如在 test() 函数之前,在初始化 cs 变量时,可以修改为以下代码:

CRITICAL_SECTION cs;
InitializeCriticalSectionAndSpinCount(&cs, 4000);

这是对临界区进行初始化的方式,其中第二个参数是为了设置锁的互斥性和轮询性能的。

代码放完整一点,关键的超时判断、超时参数都没放出来。还有你要实现的目标是否有问题,需要30ms的操作,你代码里用5ms作为超时判断?

该回答引用ChatGPT
可能的原因是系统调度导致读写操作被阻塞,从而导致超时。也有可能是数据量太大,导致 USB 总线带宽不足而发生超时。

关于改善,可以尝试通过增加超时时间来解决,或者优化程序逻辑,让读写操作尽可能快地完成。

此外,程序的并发性也可能会影响读写操作的时间,可以考虑使用多线程或并行计算来解决这个问题。

另外,根据代码看,貌似未使用到阻塞模式,建议考虑使用阻塞模式。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
根据您提供的信息,超时问题可能与以下几个方面有关:

  1. USB总线带宽问题 - 使用BUS Hound抓包可以看出您的应用程序和USB设备之间的通讯看上去是正常的,但是仍然存在超时问题。这可能是由于USB总线带宽饱和造成的。

解决方法:考虑采用支持更高速率的USB接口或采用具有更高带宽的USB控制器。

  1. USB传输缓冲区问题 - USB传输缓冲区可能被填满或者传输缓慢,导致传输时间超过预期。

解决方法:可以增大传输缓冲区的大小,提高传输的速度。同时可以调整USB设备的传输速率,使其更符合应用程序的需求。

  1. USB驱动程序问题 - USB驱动程序可能存在缺陷,导致传输超时。

解决方法:更新或者重新安装USB驱动程序。

  1. 应用程序实现问题 - 应用程序可能存在未考虑到的问题,例如进行了重复的USB传输请求、存在未处理的USB传输请求等。

解决方法:检查应用程序的USB传输实现,确保没有未处理的请求或重复的请求。

  1. USB设备的响应速度 - USB设备可能响应的速度较慢,导致传输超时。

解决方法:优化USB设备的实现,以提高其响应速度。同时通过调整计时器的精度来减小对USB设备的依赖性。

以下是一些可能有用的代码片段,用于使用QueryPerformanceCounter和QueryPerformanceFrequency对函数定时:

#include <Windows.h>

void timer_start(void)
{
  LARGE_INTEGER li;
  QueryPerformanceCounter(&li);
  timer_start_time = li.QuadPart;
}

double timer_get_ms(void)
{
  double retval;
  LARGE_INTEGER li;
  QueryPerformanceCounter(&li);
  retval = (li.QuadPart - timer_start_time) / (double)timer_perf_freq.QuadPart;
  retval *= 1000.0;
  return retval;
}

void timer_dly_ms(double ms)
{
  LARGE_INTEGER li;
  double target_time = timer_get_ms() + ms;

  while (timer_get_ms() < target_time)
  {
    Sleep(1);
  }
}

希望以上信息能对您解决超时问题有所帮助。
如果我的回答解决了您的问题,请采纳!