Zeitmessung
von B. Kainka, Lernpaket MSR mit dem PC

Hardwarenahe Aufgaben benötigen meist eine möglichst genaue Zeitbasis. Windows stellt bereits die Funktion Sleep für Wartezeiten in Millisekunden zur Verfügung. Allerdings ist damit keine gute Genauigkeit zu erreichen. Besser ist der Aufbau eigener Zeitfunktionen, die hier mit in die RSCOM-Unit eingebaut werden.
In jedem PC befindet sich ein Hardware-Timer in Form des Timerbausteins 8253 bzw. eines vergleichbaren Bausteins. Er wird mit 1,193180 MHz getaktet und liefert damit eine zeitliche Auflösung von unter einer Mikrosekunde. Windows stellt Zeitmessfunktionen zur Verfügung, die von diesem Hardwaretakt abgeleitet werden. Die Funktion QueryPerformanceFrequency liest den Zeittakt aus und liefert den Wert 1193180, also praktisch die Taktfrequenz des Timers in Hz.
Ganzen Artikel lesen...
Da der Hardware-Timer selbst nur eine Auflösung von 16 Bit besitzt, bildet Windows durch entsprechende Überträge einen Timer mit einer Auflösung von 64 Bit. Der Zählerstand seit dem Einschalten des PCs wird mit QueryPerformanceCounter abgefragt. Von diesen wird eine Delphi-Funktion TimeRead abgeleitet, die allerdings nach außen eine Realzahl für die Zeit in Millisekunden mit Nachkommastellen übergibt. Praktisch lassen sich zeitliche Auflösungen von wenigen Mikrosekunden erreichen.
Die Prozedur TimeInit setzt die Zeitvariable StartTime auf den Wert der aktuellen Zeit, sodass TimeRead nun die Zeit seit dem letzten Aufruf von TimeInit liefert. Außerdem wird hier mit QueryPerformanceFrequency der genaue Wert des Zeittakts abgefragt und daraus die Länge des Zeittakts in TimeUnit berechnet. TimeUnit wurde allerdings schon bei der Deklaration mit einem Startwert gefüllt, damit die Zeitfunktionen auch ohne den Aufruf von TimeInit funktionieren.
Zusätzlich wurde eine Prozedur Delay gebildet, die ebenfalls die Zeit in Millisekunden als Realzahl erhält. Delay verwendet TimeRead und wartet in einer Schleife, bis die übergebene Verzögerungszeit verstrichen ist.
procedure TIMEINIT();
var f: Int64;
begin
QueryPerformanceFrequency(f);
TimeUnit := 1000 / f;
QueryPerformanceCounter(StartTime)
end;
function TIMEREAD(): Real;
var t: Int64;
begin
QueryPerformanceCounter(t);
TIMEREAD := TimeUnit*(t - StartTime) ;
end;
procedure DELAY(DelayTime: Real);
var TimeStart: real;
begin
TimeStart := TIMEREAD;
while TIMEREAD < (TimeStart + DelayTime) do;
end;
Listing 4: Die Zeitfunktionen
Das in Windows verwendete Multitasking erschwert die Lösung von Echtzeitaufgaben. Ein zeitkritisches Programm kann jederzeit für einen anderen Prozess unterbrochen werden. Üblich sind kurze Unterbrechungen in der Größenordnung von Millisekunden. Allerdings kann es Situationen geben, in denen noch längere Verzögerungen auftreten, z. B., wenn der RAM-Speicher des Systems überladen ist und eine Auslagerung auf die Festplatte nötig wird.
Einem laufenden Prozess kann Windows unterschiedliche Prioritäten zuweisen. Je nach Level wird das Programm mehr oder weniger von anderen Prozessen unterbrochen. Mit SetPriorityClass kann einem Prozess eine neue Prioritätsstufe zugewiesen werden. Der höchste Level ist REALTIME_PRIORITY_CLASS. SetPriorityClass benötigt zum Aufruf ein Handle auf den laufenden Prozess, das mit GetCurrentProcess() abgefragt werden kann.
Hier wurden zwei Delphi-Prozeduren zur Umschaltung der Prozesspriorität gebildet. RealTime schaltet die hohe Priorität ein, NormalTime schaltet in den Grundzustand zurück. Die Wahl einer hohen Priorität ist nur für kurze Perioden in einem Programm sinnvoll, weil damit andere Windows-Ereignisse blockiert werden. Deshalb ist eine Bedienung mit der Maus vorübergehend unmöglich.
procedure REALTIME();
begin
SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
end;
procedure NORMALTIME();
begin
SetPriorityClass (GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
end;
Listing 5: Die Prozesspriorität

Lernen Sie die ganze Vielfalt vom Franzis Buch- und Softwareverlag kennen