2015-05-21 36 views
2

使用Delphi XE2TJvHidDevice类从Jedi库,我设法与USB设备(pic32mx7板,我的代码运行在其上)成功通信。 “发送请求,等待单一响应”的通常方式起作用。使用Delphi + Jedi,当设备发送“太快”时丢失USB数据

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

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

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

我觉得应该有更多关于Jedi的USB类的网络信息,但我很难找到它。

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

编辑:

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

这看起来像Jedi线程读取数据不能被任何处理延迟 - 不应该有一些数据缓冲(由Windows?),这将允许这种类型的处理延迟?从数据包丢失的“模式”来看,似乎有缓冲,但它是不够的,因为我可以接收30个数据包然后丢失5然后接收另外20个等。

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

+0

首先带走来自阅读过程的GUI。只需将数据转储到稍后分析的内容即可。如果有效,你知道该怎么做。 –

+0

什么是您的HID IN端点描述符的“bInterval”值? –

+0

@Turbo bInterval = 1,输入和输出相同 – Milos

回答

0

由于问题的原因似乎与USB读取线程被Synchronise(即主线程执行的数据处理)阻塞的时间量有关,因此我对线程代码进行了更改(TJvHidDeviceReadThread类,JvHidControllerClass.pas单元)。任何使用这个单元的代码和包含的类都应该没有任何修改,没有任何公开的改变。

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

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

Thread类的延长:


    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; 
相关问题