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 ?
-