Einzelnen Beitrag anzeigen

Benutzerbild von TERWI
TERWI

Registriert seit: 29. Mär 2008
Ort: D-49626
381 Beiträge
 
Delphi 11 Alexandria
 
#12

AW: QueryPerformanceCounter - Probleme, Zeitversatz

  Alt 18. Jul 2011, 19:07
Da ich wirklich nicht der Held in Sachen Threads bin, brauche ich eure (kleine) Unterstützung.
Zum Verständnis, wie es in meinen FB-Tool 'abgeht', hier mal die relevanten Codeschnipsel, wie im Normalfall bei erkannter und angelernter FB:
Delphi-Quellcode:
// -----------------------------------------------------------------------------
procedure TIR2KB_frm_RCINTERFACE.FormCreate(Sender: TObject);
begin
  ...
  RCTimer.Enabled := false; // disable Timer
  RCTimer.Interval := 80; // set to 80 mS / fit for almost IR-RCCodes
  ...
  SetProcessCPUKernel(GetProcessID, 1); // use always 1st CPU (safe ?)
  if QueryPerformanceFrequency(qpcfreq) Then
  begin
    ... // do not start COM if error occurs here !
  end;
  ...
end;

// -----------------------------------------------------------------------------
procedure TIR2KB_frm_RCINTERFACE.StartCOM;
begin
  RCTimer.Enabled := false; // stop timer for safety / on restart
  Data.RCInterface := 2;
  ComPort.Open;
  ComPort.SetRTS(True);
  ComPort.SetDTR(True);
  // QueryPerformanceCounter(TSqpc1); // remain TqpcStamp divider
  TSqpc1 := 0; // defined settingfor 1st compare
end;

// -----------------------------------------------------------------------------
procedure TIR2KB_frm_RCINTERFACE.ComPortDSRChange(Sender: TObject; OnOff: Boolean);
begin
  QueryPerformanceCounter(TSqpc2); // get new TqpcStamp
  Tqpc := ((TSqpc2 - TSqpc1) * 1000000) div qpcfreq; // calc Tqpc in µS !
  TSqpc1 := TSqpc2; // set last TqpcStamp
  if (Tqpc > 12500) then // > 12.5 mS ? > must be a new Code-Sequence!
  begin
    fillchar(TCP, sizeof(TTCP),0); // clear TP - new frame started
    RCTimer.Enabled := false; // stop timer for safety
    RCTimer.Enabled := true; // start timer again
    exit; // !!! wait for next valid pulse
  end; // TCP.Len = 0 !
  if (TCP.Len < TCPmaxlen) then // space in TP to write ?
  begin
    TCP.HL[TCP.Len] := OnOff; // Pulse-type
    TCP.Pulse[TCP.Len] := Tqpc; // Pulse-duration
    TCP.Time := TCP.Time + Tqpc; // Command-duration (incremented)
    inc(TCP.Len); // number of pulses
  end;
end;

// -----------------------------------------------------------------------------
procedure TIR2KB_frm_RCINTERFACE.RCTimerTimer(Sender: TObject);
begin
  case Data.RCInterface of
   1: begin // USB
        ....
        CheckRCData;
      end;
   2: begin // COM
        RCTimer.Enabled := false; // stop timer for safety / on restart
        CheckRCData;
      end;
   3: begin // HID
        ....
        CheckRCData;
      end;
  end;
end;
'FormCreate' arbeitet neben den o.a. Dingen logo noch ne Latte anderer Initialisierungen ab. Normaler INI-Kram...
So bald der gewünschte Kern definiert gesetzt ist (Abfrage auf Erfolg fehlt noch), wird hier nur einmal der erforderliche Divisor gelesen.
Sollte lt. MS nur einmal passieren - hier sicherlich der richtige Platz !?

'StartCOM' wird im weiteren Verlauf des Programms automatisch ausgeführt, wenn so weit COM mit dem korrekten Port und QPC verfügbar ist.
Der erstmaligen Aufruf von QPC ist hier eigentlich unwichtig, daher ausmarkiert und der Wert der 'Startzeit' auf 0 gesetzt.

'ComPortDSRChange' ist eine Event-Routine, die (nur) bei Signalwechsel (H > L oder L > H) am DSR-Pin vom COM-Port aufgerufen wird.
Erst hier lese ich den QPC als Stopp-Wert und ermittle die vergangene Zeit seit letztem Event - oder Start.
[INTERMEZZO]
Eben hier fiel auf, das sich 'gelegentlich' und nicht konkret wiederholbar halt eben die berechnete Zeit (weit) in's negative verschiebt und auch kurz danach ebenso (und weiter) in's positive.
Irgendwann nach ca. 10-20 bis 30-40 mS passt das dann interessanterweise wieder.
Für ne Mittelwertbildung von Ping-Zeiten kann man das vielleicht ignorieren - aber in diesem Fall fehlen bei den Adressen und Daten einer FB-Taste halt ein paar elementare wichtige Bits zur Tastenerkennung ....

[/INTERMEZZO]
Startwert wird = Stoppwert gesetzt für den nächsten Event.
Wenn > als 12.5mS, dann handelt es sich garantiert (99,99999%) um den 1. Event seit Start oder eine neue Folge von Impulsen der FB (auch wenn man eine Taste gedrückt hält). Ist das der Fall, lösche ich mein 'Daten-Array', stoppe den Timer pro forma und starte in wieder.
Nix Repeat, While, Wait, Sleep oder in der Art. Hier wird keiner Prozessorzeit verballert.
Wichtig:
Dieser Timer läuft 80 mS (das passt für die Laufzeit eines Daten-/Adress-Frames für alle möglichen gängigen IR-FBs) und gibt mir ein definiertes Ende auf das sonst endlose Warten eines beendeten Frames.
Ich mache das deswegen hier so, weil es unterschiedliche FB-Protokolle mit unterschiedlichen Pulse-/Pause-Zeiten und unterschiedlichen Datenlängen gibt. Muss universell sein - man kann nicht (schon) für für jeden Typ ne extra Abfrage machen, bläht nur auf und wird unübersichtlich.
Danach 'raus aus dem Haus'.

Handelt es sich um einen (vermeintlich) gültigen Impulsabstand, merke ich mir entsprechende Daten zur späteren Auswertung.
Danach warten auf den nächsten Event - oder halt bis der Timer nach 80 mS abgelaufen ist.

'RCTimerTimer' benötige ich eigentlich nur für COM - USB und HID 'funktionieren anders, deshalb lasse ich hier erst mal weg ....
Der Timer schaltet sich bei Aufruf selber ab, bis er wieder durch den ersten 'Neu-Impuls' in 'ComPortDSRChange' gestartet wird.
Dazwischen vergehen mindestens 20 mS - mit dem 'Erkennen' eines neuen, kompletten Daten-/Adress-Frames also min. 100 mS.
Das reicht erfahrungsgemäß nach ausgiebigen Tests hier, um eine Auswertung durchzuführen und Daten entsprechend an die Applikation(en) zu senden. Ich hatte bisher keine Timing-Probs damit. Mit ca. 20 und auch mehr verschiedenen FBs.

'CheckRCData' (hier nicht gelistet) validiert die Daten vorgegebener FB's/Programme und gibt sie bei Gültigkeit je nach Einstellung über eine flexibel einzubindende Callback-Rotuine an ein korrespondierendes Programm / System weiter:
- als ein KeyBoard-Event oder Maus-Simulation (an gewählte App / Fenster)
- via API des Programms direkt dahin
- oder .... was sonst noch so geht. Ausbaufähig ?!

Funktioniert wirklich recht genial.
Auf meinem WinDoof-HTPC (AMD X2 5400) kann ich damit die DVB-App 'DVBDream', VLC und auch die Online-TV-Zeitung TV-Browser recht gut vom Sofa mit einer IMON-FB (die hat ein Mausrad) recht prima bedienen. Noch gibt es hier & da ein paar Macken - aber da arbeite ich ja (wieder) dran .....


Problem ist bei der Lösung, dass ich wie o.g. absolut kein Held in Sachen Threads bin.
Und da dieses Proggie sowohl als Stand-Alone-App läuft (zum Anlernen, einstellen, zuweisen), sowie auch als DLL in einer Anwendung, ist eine generelle Kernzuweisung für die gesamte App sicher nicht der schlaueste Weg ...
Im SA-Betrieb stört die generelle Zuweisung auf einen Kern absolut nicht, aber z.B. als DLL mit DVBDream und meinem kleinen (naja, auch wachsenden) DVB-Proggie geht das von der reduzierten Performance her gar nicht.
Mit VLC + TV-Browser hab ich das mangels Zeit noch nicht getestet.

Wie löst man das mit dem Zugriff betreff QPC auf nur einen Kern am schlauesten ?

-

Geändert von TERWI (18. Jul 2011 um 20:11 Uhr)
  Mit Zitat antworten Zitat