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以下答案由GPT-3.5大模型与博主波罗歌共同编写:
根据您提供的信息,超时问题可能与以下几个方面有关:
解决方法:考虑采用支持更高速率的USB接口或采用具有更高带宽的USB控制器。
解决方法:可以增大传输缓冲区的大小,提高传输的速度。同时可以调整USB设备的传输速率,使其更符合应用程序的需求。
解决方法:更新或者重新安装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);
}
}
希望以上信息能对您解决超时问题有所帮助。
如果我的回答解决了您的问题,请采纳!