使用Delphi + Jedi,在设备发送时丢失USB数据"太快"

时间:2015-05-21 12:31:17

标签: delphi usb hid jedi

使用Delphi XE2库中的TJvHidDeviceJedi类,我成功地与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) - 再次丢失数据包。
  5. 这看起来像读取数据的Jedi线程不能被任何处理延迟 - 不应该有一些缓冲的数据(通过Windows?),这将允许这种类型的处理延迟?从丢包"模式判断"似乎有缓冲,但它是不够的,因为我可以接受例如30包然后丢失5然后再接收20等。

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

1 个答案:

答案 0 :(得分:0)

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

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

到目前为止,我没有遇到丢失的数据包。

线程类已扩展:

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;

并添加了以下内容:

{{1}}