Entwickler-Ecke
Multimedia / Grafik - Brauche schnellen Timer
NemesisoD - So 16.03.08 20:58
Titel: Brauche schnellen Timer
Hallo zusammen,
ich schreibe zurzeit die Software für einen Flugsimulator Marke Eigenbau. Wir nutzen den MS Flightsimulator 2004 und lesen über eine Zusatz DLL die Variablen des FlightSims aus. Diese werden dann über einen virtuellen COM-Port an die selbstgebaute Elektronik gesendet.
Es funktioniert auch alles wunderbar, bis auf das ich bereits beim Auslesen einer Variable aus dem Flightsim eine merkliche Verzögerung habe. Ich nutze den Standard Timer mit einem Intervall von 1ms. Hier die Funktion:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28:
| procedure TMain.Timer1Timer(Sender: TObject); var frequenz : DWORD; dwResult : DWORD; KHZ : Byte; MHZ : Byte; begin if FSUIPC_Read($034E, 3, @frequenz, dwResult) then begin if FSUIPC_Process(dwResult) then begin frequenz := ((frequenz and not(($300000))) OR $10000); Label2.Caption := Format('%x',[frequenz]); KHZ := BCDVal(frequenz and $000000FF); MHZ := BCDVal(frequenz and $FFFFFF00); Label3.Caption := inttostr(MHZ) + '.' + inttostr(KHZ); if not((KHZ = VKHZ) or (MHZ = VMHZ)) then begin VMHZ := MHZ; VKHZ := KHZ; ApdComPort1.Output := chr(MHZ); ApdComPort1.Output := chr(KHZ); end; end else begin Label2.Caption := 'Processing: ' + ResultText[dwResult]; end; end; end; |
Ich muss die Variablen noch von BCD als Integer Kovertieren, und da es sich einmal um die Frequenz in MHZ und KHZ handelt muss ich das auchnoch trennen.
Meine Frage ist nun, ob es eine schnellere Möglichkeit gibt.
Das Konvrtieren könnte ich zurnot auch noch auf die Mikrocontroller packen.
Besten Dank schon mal
MfG
Michael
dummzeuch - Mo 17.03.08 22:16
Titel: Re: Brauche schnellen Timer
NemesisoD hat folgendes geschrieben: |
Ich nutze den Standard Timer mit einem Intervall von 1ms.
|
Ein Timer hat ein minimales Intervall von ca. 50 ms. Das ist nicht zu beeinflussen, da eine Einschraenkung von Windows.
Kann das evtl. der Grund sein? Ohne jetzt Deinen Code genauer angeschaut zu haben: Evtl. rechnest Du irgendwo mit 1 MHz obwohl es nur 200 Hz sind?
twm
NemesisoD - Mo 17.03.08 23:51
Hallo,
danke erstmal für deine Antwort. Die MHZ bezieht sich nicht auf eine Geschwindigkeit, das ist eine Frequenz welche ich aus dem Flugsimulator beziehe.
Aber ich habe mitlerweile eine neue Idee, könnte man das pollen nicht einfach in ein eigenes Thread auslagern, und immer wenn sich eine Variable ändert wird dies dem Haupt-Thread mittgeteilt und dieser sendet dann die Daten an die Elektronik.
Aber wie kann ich dem Haupt-Thread sagen das er was machen soll, ohne das das Neben-Thread aufhört zu arbeiten, weil die Sycronize Funktion bewirkt ja, das das Neben-Thread wartet bis das Haupt-Thread fertig ist. Kann man das vielleicht über ein Event machen? Aber wie bau ich mir ein solches???
Danke schonmal für eure Antworten.
Micha
dummzeuch - Di 18.03.08 14:43
NemesisoD hat folgendes geschrieben: |
Aber ich habe mitlerweile eine neue Idee, könnte man das pollen nicht einfach in ein eigenes Thread auslagern, und immer wenn sich eine Variable ändert wird dies dem Haupt-Thread mittgeteilt und dieser sendet dann die Daten an die Elektronik.
Aber wie kann ich dem Haupt-Thread sagen das er was machen soll, ohne das das Neben-Thread aufhört zu arbeiten, weil die Sycronize Funktion bewirkt ja, das das Neben-Thread wartet bis das Haupt-Thread fertig ist. Kann man das vielleicht über ein Event machen? Aber wie bau ich mir ein solches???
|
Wieso soll denn der Haupt-Thread an die Elektronik schicken? Das kann doch auch der Hintergrund-Thread machen. Oder Du startest extra dafuer einen neuen Thread und synchronisierst mittels eines TEvent-Objects.
Aber Vorsicht: Wenn Du noch nie ein multithreaded Programm geschrieben hast, kann das schnell in die Hose gehen. Z.B. Zugriffe auf die VCL aus Hintergrund-Threads heraus, sind verboten.
twm
Hidden - Di 18.03.08 16:36
dummzeuch hat folgendes geschrieben: |
Zugriffe auf die VCL aus Hintergrund-Threads heraus, sind verboten.
|
Aber Massages kann man doch schicken. Das ist ein sehr einfacher Umweg, ansonsten TNotifyEvent.
Hörte sich jetzt so sehr nach man kann nichts beeinflussen an :wink: .
mfG,
FinnO - Di 18.03.08 18:25
Mal ne ganz dumme Frage: Muss man überhaupt nen Timer nehmen? Ich meine ne Endlosschleife ist doch schneller oder?
NemesisoD - Di 18.03.08 20:43
@FinnO
Ich bin jetzt auch weg vom Timer, ich will das ganze ja nun mit Threads und einer Endlosschleife lösen, mein Problem bei der sache ist nur, das ich nicht weiß wie ich aus einem Thread einem anderen Thread sage das er was machen soll, und das möglichst ohne eines der Threads anhalten zu müssen.
Würde mich über einen kuren Beispiel Code sehr freuen.
Leider müssten beide Threads auf die gleiche Daten zugreifen.
Gruß
Michael
dummzeuch - Di 18.03.08 20:56
Hidden hat folgendes geschrieben: |
dummzeuch hat folgendes geschrieben: |
Zugriffe auf die VCL aus Hintergrund-Threads heraus, sind verboten.
|
Aber Messages kann man doch schicken. Das ist ein sehr einfacher Umweg, ansonsten TNotifyEvent.
|
TNotifyEvent ist auch nur ein Methodenaufruf, d.h. der Code darin wird dann auch im Hintergrundthread ausgefuehrt.
Senden von Messages an ein Window-Handle geht, kann allerdings schiefgehen, wenn das Handle noch nicht existiert, dann wird es naemlich vom Hintergrund-Thread erzeugt und man hat sehr komische Effekte.
Abgesehen davon, ist die Performance dabei eher schlecht. Damit musste ich mich in der letzten Zeit mehrfach rumschlagen. Jetzt laeuft das mit zwei zusaetzlichen Threads und das Programm hat nur noch knapp 45% CPU-Last statt vorher 95% und nebenbei reagiert es wieder besser auf Benutzereingaben. Aber das ist ein spezieller Fall mit ziemlich aufwendigen Berechnungen. In der Regel sollte es mit Messages funktionieren.
twm
dummzeuch - Di 18.03.08 21:20
NemesisoD hat folgendes geschrieben: |
ich will das ganze ja nun mit Threads und einer Endlosschleife lösen, mein Problem bei der sache ist nur, das ich nicht weiß wie ich aus einem Thread einem anderen Thread sage das er was machen soll, und das möglichst ohne eines der Threads anhalten zu müssen.
|
Ungetestet:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63:
| uses Windows;
type TData = class end;
var gblData: TData = nil;
type TConsumerThread = class(TThread) protected procedure Execute; override; end;
type TProducerThread = class(TThread) protected procedure Execute; override; end;
procedure TProducerThread.Execute; var Data: TData; OldData: TData; begin while not Terminated do begin Data := TData.Create; GetData(Data); OldData := TData(InterlockedExchange(integer(gblData), integer(Data))); if Assigned(OldData) then OldData.Free; end; end;
procedure TConsumerThread.Execute; var Data: TData; begin while not Terminated do begin Data := TData(InterlockedExchange(integer(gblDdata), nil); if Assigned(Data) then begin HandleData(Data); Data.Free; end; end; end;
var Producer: TProducerThread; Consumer: TConsumerThread;
begin Consumer := TConsumerThread.Create(false); Producer := TProducerThread.Create(false); |
Der ProducerThread holt mittels des GetData-Aufrufs neue Daten. GetData kehrt erst zurueck, wenn neue Daten gelesen wurden und in den Data Parameter uebertragen wurden. Dann benutzt er InterlockedExchange, um diese neuen Daten in gblData zu speichern. Falls gblData ein noch nicht verarbeitetes Objekt enthielt, wird dieses freigegeben.
Der ConsumerThread holt sich mittels InterlockedExchange den Inhalt von gblData und setzt gleichzeitig diese Variable auf nil. Wenn sie Daten enthielt, werden sie durch Aufruf von HandleData verarbeitet und anschliessend freigegeben.
Das sollte so funktionieren, jedoch kann es Dir passieren, dass der ConsumerThread jede Menge Rechenzeit verbraet, da er gblData pollt. Dagegen hilft dann ein Sleep.
Diese Art der Datenuebergabe kommt ohne Locks aus, hat aber den Nachteil, dass, falls die Daten schneller einlaufen als sie verarbeitet werden, Daten verlorengehen (ProducerThread gibt alten Inhalt von gblData frei, wenn nicht nil).
Alternativ kannst Du auch eine TQueue benutzen, die Du aber mittels einer TCriticalSection vor gleichzeitigen Zugriff schuetzen musst.
(Ich bin mir sicher, dass diverse Leute bei diesem Code Haare in der Suppe finden werden. Ich will auch nicht behaupten, dass das die beste aller Loesungen ist, aber funktionieren sollte es.)
twm
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!