You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Cypress CyAPI(FX3)开发USB嗅探器双端点XferData报错问题

基于Cypress CyAPI(FX3)实现多端点数据嗅探的故障排查与解决

场景背景

开发数据嗅探类工具用于监测、存储设备传输数据,支撑自有程序测试工作。对接Cypress FX3系列设备时,需要基于CyAPI实现抓数逻辑:跳过设备打开、初始化阶段的配置交互报文,待设备启动正式业务数据流后,抓取对应端点的有效业务数据。
初始实现采用双线程同步架构:

  • 线程1:调用设备原生API实例化Device对象,依次执行open()、init()完成设备初始化(该阶段交互数据不需要抓取),初始化完成后通过std::promise发出同步信号,再调用getData()启动正式业务数据传输,传输结束后执行close()关闭设备。
  • 线程2:实例化CyAPI封装类WrapperCyAPI对象,依次执行open()、init()获取设备句柄,等待同步信号触发后调用usb.getData()抓取传输数据,完成后关闭设备。

单端点正常实现代码

主逻辑代码:

#include <iostream>
#include <thread>
#include <vector>
#include <tuple>
#include <future>

#include <DeviceAPI.h>
#include <WrapperCyAPI.h>

int main()
{
  std::promise<void> prm;
  std::future<void> fut{ prm.get_future() };
  std::thread th1{
    [&prm]()
    {
      Device device;
      device.open();
      device.init(); // init阶段的配置交互数据不需要抓取
      prm.set_value();
      device.getData();
      
      device.close();
    }
  };


  std::thread th2{
    [&fut]()
    {
      WrapperCyAPI usb;
      usb.open();
      usb.init();
      fut.wait();
      std::vector<uint8_t> data{ usb.getData() };
      usb.close();
    }
  };

  th1.join();
  th2.join();
}

单端点读逻辑:

std::vector<uint8_t> USB::getData()
{
  long receivedSize{ static_cast<long>(buffer.size()) };
  device->EndPointOf(ENDPOINT_IN)->XferData(&buffer[0], receivedSize);
  return { buffer };
}

该场景下功能运行正常,单个IN端点传输100KB数据可完整接收。

双端点场景故障表现

设备同时向2个IN端点传输数据(每个端点传输100KB,总数据量200KB)时,修改代码适配双端点读取后XferData调用报错。
修改后主逻辑相关片段:

int main()
{
  // 其余逻辑不变
  std::thread th2{
    [&fut]()
    {
      WrapperCyAPI usb;
      usb.open();
      usb.init();
      fut.wait();
      std::tuple<std::vector<uint8_t>, std::vector<uint8_t>> data{ usb.getData() };
      usb.close();
    }
  };
}

修改后的双端点读逻辑:

std::tuple<std::vector<uint8_t>, std::vector<uint8_t>> USB::getData()
{
  long receivedSize{ static_cast<long>(buffer1.size()) };
  device->EndPointOf(ENDPOINT_IN1)->XferData(&buffer1[0], receivedSize);
  receivedSize = static_cast<long>(buffer2.size());
  device->EndPointOf(ENDPOINT_IN2)->XferData(&buffer2[0], receivedSize);
  return { buffer1, buffer2 };
}

故障现象:ENDPOINT_IN1调用XferData可正常获取100KB数据,ENDPOINT_IN2调用XferData返回false,无法获取数据。

故障根因
  • 同步串行调用逻辑存在硬伤:XferData是阻塞式同步接口,当前实现会等第一个端点100KB数据全部接收完成后,才向第二个端点提交读请求。FX3设备端端点缓冲区、主机侧USB驱动栈的端点缓冲区容量非常有限(高速USB模式下单端点缓冲通常仅数KB到十几KB),第二个端点的100KB数据在等待第一个端点传输的过程中,会因为没有待处理的读请求、缓冲区满被丢弃,直接导致第二个端点请求超时返回false。
  • 双句柄访问存在驱动兼容性问题:Windows下USB设备默认是独占访问模式,当前在两个线程分别打开原生API句柄、CyAPI句柄,单端点场景能跑通仅代表当前驱动配置允许共享打开,但部分版本的CyUSB3驱动在多句柄访问场景下,不会自动为未提交读请求的端点维持传输调度,第二个端点的传输根本没有被驱动层接管,自然收不到数据。
  • USB批量传输(Bulk Transfer)本身没有端到端流控保证,必须提前提交读请求在驱动层排队,才能保证数据不丢;等数据已经从设备发出后再临时提交读请求,必然出现丢包。
可行解决方案
  • 改用异步并行提交传输请求,替换串行同步调用逻辑
    不要等第一个端点读完再提交第二个端点的请求,初始化完成后同时给两个端点提交Overlapped异步读请求,等待两个传输全部完成后再回收数据。参考实现如下:
    #include <windows.h>
    
    std::tuple<std::vector<uint8_t>, std::vector<uint8_t>> USB::getData()
    {
      OVERLAPPED ov1 = {0}, ov2 = {0};
      ov1.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
      ov2.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
      long len1 = static_cast<long>(buffer1.size());
      long len2 = static_cast<long>(buffer2.size());
      
      // 同时提交两个端点的异步读请求,不等待第一个完成
      device->EndPointOf(ENDPOINT_IN1)->XferData(&buffer1[0], len1, &ov1);
      device->EndPointOf(ENDPOINT_IN2)->XferData(&buffer2[0], len2, &ov2);
      
      // 等待两个传输全部完成
      HANDLE events[] = {ov1.hEvent, ov2.hEvent};
      WaitForMultipleObjects(2, events, TRUE, 5000); // 按需设置超时时间
      
      // 获取实际传输长度
      DWORD transferred1 = 0, transferred2 = 0;
      GetOverlappedResult(device->EndPointOf(ENDPOINT_IN1)->hDevice, &ov1, &transferred1, FALSE);
      GetOverlappedResult(device->EndPointOf(ENDPOINT_IN2)->hDevice, &ov2, &transferred2, FALSE);
      
      CloseHandle(ov1.hEvent);
      CloseHandle(ov2.hEvent);
      
      // 按实际接收长度截断缓冲区
      buffer1.resize(transferred1);
      buffer2.resize(transferred2);
      return {buffer1, buffer2};
    }
    
  • 为每个端点创建独立接收线程,提前排队读请求
    给两个IN端点分别创建独立的接收线程,每个线程内循环调用XferData持续接收数据,同步信号触发后再从环形接收队列中截取有效业务段数据,避免端点因为没有待处理读请求出现缓冲区溢出。注意要给每个端点配置足够容量的环形队列,适配高速传输场景。
  • 规避双句柄访问的兼容性问题
    最稳妥的实现方式是不要在两个线程分别打开设备句柄,直接基于CyAPI单独实现初始化阶段的交互逻辑,过滤掉初始化阶段的配置报文,等初始化完成后再开始记录业务数据,从根源上避免多句柄访问带来的驱动端点调度异常。
  • 细节校验:调用XferData前确认传入的长度参数为缓冲区实际分配大小,传输完成后必须按接口返回的实际接收长度截断缓冲区,不要直接返回整个预分配的buffer,避免混入无效数据。

内容的提问来源于stack exchange,提问作者Alireza Nikpay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 04:09:15