Menggunakan kelas Delphi XE2 dan TJvHidDevice dari perpustakaan Jedi, saya berhasil berkomunikasi dengan perangkat USB (papan pic32mx7, dengan kode saya berjalan di atasnya). Cara biasa "kirim permintaan, tunggu satu respons" berfungsi.

Masalahnya adalah dengan perintah yang menghasilkan lebih banyak tanggapan berurutan. Jika tanggapan tersebut dikirim oleh perangkat secepat mungkin - atau bahkan jika saya menambahkan penundaan kecil di antara mereka seperti 5 ms - saya kehilangan paket (laporan? bingkai?). Acara OnDeviceData sepertinya tidak cocok untuk mereka semua. Jika saya menambahkan penundaan yang lebih besar dalam kode perangkat, masalahnya akan hilang.

Saya menggunakan program USBPcap untuk menangkap data USB dan membuangnya ke file yang, setelah saya buka di WireShark, berisi semua data yang dikirim oleh perangkat (saya mengirim 255 paket sebagai tes, dengan semua nol dan satu "1" menggeser tempatnya sebanyak 1 posisi di setiap paket). Jadi, saya pikir perangkat dan Windows melakukan tugasnya.

Untuk memastikan kode Delphi saya tidak salah, saya mencoba proyek contoh Jedi "DevReader" (ini adalah kode main.pas) yang membuang data ke layar dan juga paket yang hilang.

Saya merasa harus ada lebih banyak informasi di internet tentang kelas USB Jedi tetapi saya kesulitan menemukannya.

Saya mungkin dapat menghindari masalah ini dengan menggabungkan/menggabungkan respons perangkat, tetapi masih ingin tahu apa yang terjadi.

Sunting:

  1. Mencoba dari aplikasi konsol: paket tidak hilang lagi.
  2. Memodifikasi aplikasi demo Jedi untuk hanya menghitung paket yang diterima dan memperbarui label penghitung di layar (tidak ada pengecatan ulang jendela paksa) - tidak ada paket yang hilang.
  3. Menambahkan sleep(1) dalam acara OnData - tidak ada paket yang hilang.
  4. Menambahkan sleep(2) di acara OnData - kehilangan paket lagi.

Ini terlihat seperti utas Jedi yang membaca data tidak boleh ditunda oleh pemrosesan apa pun - bukankah seharusnya ada buffering data yang terjadi (oleh Windows?) yang memungkinkan jenis penundaan pemrosesan ini? Dilihat dari "pola" packet loss sepertinya ada buffering, tetapi itu tidak cukup karena saya dapat menerima mis. 30 paket kemudian kehilangan 5 kemudian menerima lagi 20 dst.

Saya akan memodifikasi kode saya untuk menyalin data dan keluar dari acara OnData secepat mungkin sehingga utas memiliki "waktu henti" minimum dan saya akan melaporkan hasilnya.

2
Milos 21 Mei 2015, 15:31
Pertama-tama ambil GUI dari proses membaca. Buang saja data tersebut ke dalam sesuatu yang nantinya akan dianalisis. Jika itu berhasil, Anda tahu apa yang harus dilakukan.
 – 
LU RD
21 Mei 2015, 20:14
Berapa nilai bInterval dari deskriptor endpoint HIDs IN Anda?
 – 
Turbo J
22 Mei 2015, 03:28
BInterval = 1, sama untuk masuk & keluar
 – 
Milos
22 Mei 2015, 11:41
@"LU RD" meskipun pemrosesan GUI tampak sangat minim dalam proyek demo jedi, tampaknya cukup untuk menyebabkan hilangnya data. Saya akan mengedit pertanyaan dengan info lebih lanjut.
 – 
Milos
22 Mei 2015, 12:47

1 menjawab

Jawaban Terbaik

Karena penyebab masalah tampaknya terkait dengan jumlah waktu utas membaca USB diblokir oleh Synchronise, yaitu pemrosesan data yang dilakukan oleh utas utama, saya membuat perubahan pada kode utas, (kelas TJvHidDeviceReadThread , JvHidControllerClass.pas unit). Kode apa pun yang menggunakan unit ini dan kelas yang terkandung harus tetap berfungsi tanpa modifikasi apa pun, tidak ada publik yang diubah.

Perilaku baru: setiap kali data dibaca, data tersebut ditempatkan dalam daftar aman utas. Alih-alih Sinkronisasi, sekarang menggunakan Queue, tetapi hanya jika belum diantrekan. Metode Antrian membaca dari daftar aman utas hingga kosong. Ini memicu peristiwa (peristiwa yang sama seperti dalam kode lama) untuk setiap laporan buffer dalam daftar. Setelah daftar kosong, bendera "Antri" direset dan pembacaan berikutnya akan menyebabkan Antrian lagi.

Dalam pengujian sejauh ini saya tidak menemukan paket yang hilang.

Kelas utas diperpanjang:


  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;

Di bagian implementasi, berikut ini diubah:

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);
...

Dan berikut ini ditambahkan:

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;
0
Milos 26 Mei 2015, 12:08