【问题标题】:Using Delphi + Jedi, losing USB data when device sends it "too fast"使用 Delphi + Jedi,当设备发送“太快”时丢失 USB 数据
【发布时间】:2015-05-21 12:31:16
【问题描述】:

使用Jedi 库中的Delphi XE2TJvHidDevice 类,我成功地与USB 设备通信(pic32mx7 板,我的代码在其上运行)。 “发送请求,等待单个响应”的通常方式有效。

问题在于导致大量连续响应的命令。如果设备尽可能快地发送这些响应 - 或者即使我在它们之间添加一个小的延迟,如 5ms - 我会丢失数据包(报告?帧?)。 OnDeviceData 事件似乎并没有为所有人触发。如果我在设备代码中添加更大的延迟,问题就会消失。

我使用USBPcap 程序捕获 USB 数据并将其转储到一个文件中,一旦我在 WireShark 中打开它,该文件包含设备发送的所有数据(我发送 255 个数据包作为测试,全零和一个“1”在每个数据包中将其位置移动 1 个位置)。所以,我认为设备和 Windows 都在做他们的工作。

为了确保我的 Delphi 代码没有错误,我尝试了 Jedi 示例项目“DevReader”(这里是 main.pas code),它在屏幕上转储数据并且它也丢失了数据包。

我觉得网上应该有更多关于 Jedi 的 USB 课程的信息,但我找不到。

我也许可以通过聚合/压缩设备的响应来避免这个问题,但仍然想知道发生了什么。

编辑:

  1. 从控制台应用尝试:数据包不再丢失。
  2. 将 Jedi 演示应用修改为仅计算接收到的数据包并更新屏幕上的计数器标签(无强制窗口重绘) - 不会丢失数据包。
  3. 在 OnData 事件中添加了 sleep(1) - 不会丢失数据包。
  4. 在 OnData 事件中添加了 sleep(2) - 再次丢失数据包。

这看起来像读取数据的绝地线程不能被任何处理延迟 - 不应该有一些数据缓冲(通过 Windows 吗?)允许这种类型的处理延迟?从丢包“模式”来看,似乎有缓冲,但这还不够,因为我可以接收到例如30 个数据包,然后丢失 5 个,然后再收到 20 个,以此类推。

我将修改我的代码以尽快复制数据并退出 OnData 事件,以使线程具有最短的“停机时间”并报告结果。

【问题讨论】:

  • 首先从阅读过程中去掉GUI。只需将数据转储到稍后分析的内容中。如果可行,您就知道该怎么做了。
  • HIDs IN 端点描述符的bInterval 值是多少?
  • @Turbo bInterval = 1, in & out 相同
  • @"LU RD" 尽管 GUI 处理在绝地演示项目中看起来非常少,但似乎足以导致数据丢失。我将使用更多信息编辑问题。

标签: delphi usb hid jedi


【解决方案1】:

由于问题的原因似乎与USB读取线程被Synchronise阻塞的时间长短有关,即主线程进行的数据处理,所以我在线程代码中进行了更改,(TJvHidDeviceReadThread类,JvHidControllerClass.pas 单元)。任何使用这个单元的代码和所包含的类都应该在没有任何修改的情况下仍然可以工作,没有任何公共被改变。

新行为:每次读取数据时,都会将其放入线程安全列表中。它现在使用Queue 而不是同步,但前提是它尚未排队。 Queued 方法从线程安全列表中读取,直到它为空。它为列表中的每个缓冲报告触发一个事件(与旧代码中的事件相同)。一旦列表为空,“Queued”标志被重置,下一次读取将再次导致排队。

在目前的测试中,我没有遇到丢包的情况。

线程类被扩展:


  TJvHidDeviceReadThread = class(TJvCustomThread)
  private
    FErr: DWORD;

    // start of additions
    ReceivedReports : TThreadList;
    Queued: boolean;
    procedure PushReceivedReport(const bytes: array of byte; const NumBytesRead: cardinal);
    function PopReceivedReport(var ReportID: byte; var ReportBytes: TBytes): boolean;
    procedure FlushBuffer;
    // end of additions

    procedure DoData;
    procedure DoDataError;
    constructor CtlCreate(const Dev: TJvHidDevice);
  protected
    procedure Execute; override;
  public
    Device: TJvHidDevice;
    NumBytesRead: Cardinal;
    Report: array of Byte;
    constructor Create(CreateSuspended: Boolean);
    //added destructor:
    destructor Destroy; override;
  end;

在实施部分,修改了以下内容:

constructor TJvHidDeviceReadThread.CtlCreate(const Dev: TJvHidDevice);
begin
  inherited Create(False);
  // start of changes
  ReceivedReports := TThreadList.Create; 
  // end of changes
  Device := Dev;
  NumBytesRead := 0;
  SetLength(Report, Dev.Caps.InputReportByteLength);
end;

procedure TJvHidDeviceReadThread.Execute;
...
...
...
    //replaced: Synchronize(DoData); with:
    PushReceivedReport (Report, NumBytesRead);
...

并添加了以下内容:

type

TReport = class
  ID: byte;
  Bytes: TBytes;
end;

destructor TJvHidDeviceReadThread.Destroy;
var
  l: TList;
begin
  RemoveQueuedEvents (self);
  try
    l := ReceivedReports.LockList;
    while l.Count>0 do
      begin
        TReport(l[0]).Free;
        l.Delete(0);
      end;
  finally
    ReceivedReports.UnlockList;
    FreeAndNil (ReceivedReports);
  end;

  inherited;
end;

procedure TJvHidDeviceReadThread.FlushBuffer;
var
  ReportID: byte;
  ReportBytes: TBytes;
begin
  while PopReceivedReport (ReportID, ReportBytes) do
        Device.OnData(Device, ReportID, ReportBytes, length(ReportBytes));
end;

function TJvHidDeviceReadThread.PopReceivedReport(var ReportID: byte; var ReportBytes: TBytes): boolean;
var
  l: TList;
  rep: TReport;
begin
  l := ReceivedReports.LockList;
  rep := nil;
  try
    result := l.Count>0;
    if result
      then
        begin
          rep := l[0];
          l.Delete(0);
        end
      else Queued := false;
  finally
    ReceivedReports.UnlockList;
  end;

  if result then
    begin
      ReportID := rep.ID;
      SetLength(ReportBytes, length(rep.Bytes));
      System.move (rep.Bytes[0], ReportBytes[0], length(rep.Bytes));
      rep.Free;
    end;
end;

procedure TJvHidDeviceReadThread.PushReceivedReport(const bytes: array of byte; const NumBytesRead: cardinal);
var
  rep: TReport;
begin
  rep := TReport.Create;
  setlength (rep.Bytes, NumBytesRead-1);
  rep.ID := Bytes[0];
  System.move (Bytes[1], rep.Bytes[0], NumBytesRead-1);

  // explicitely lock the list just to provide a locking mechanism for the Queue flag as well
  ReceivedReports.LockList;
  try
    if not Queued then
      begin
        Queued := true;
        Queue (FlushBuffer);
      end;
    ReceivedReports.Add(rep);
  finally
    ReceivedReports.UnlockList;
  end;
end;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-18
    • 2019-12-08
    • 2017-09-14
    • 1970-01-01
    • 2019-06-15
    • 1970-01-01
    相关资源
    最近更新 更多