3.5 Zeitfunktionen


 

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. 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 Hardware-Takt abgeleitet werden. Die Funktion QueryPerformanceFrequency liest den Zeittakt aus und liefert den Wert 1193180, also praktisch die Taktfrequenz des Timers in Hz.

 

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, der ebenfalls die Zeit in Millisekunden als Realzahl erhält. Delay verwendet TimeRead und wartet in einer Schleife, bis die übergebene Verzögerungszeit verstrichen ist.

 

implementation

 

var  StartTime: Int64;

     TimeUnit: Real = 0.000838;

 

...

 

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;

 

 

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 denn 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;

 

 

Das folgende Programmbeispiel demonstriert die tatsächlich erreichbare Zeitauflösung bei der Erzeugung von Rechteckimpulsen an der Ausgangsleitung DTR. Die Puls- und Pausenlänge für ein Rechtecksignal kann im Bereich 10 µs bis 1 ms frei eingestellt werden. Damit lassen sich Rechtecksignale mit Frequenzen zwischen 500 Hz und 50 kHz erzeugen. Die zeitliche Genauigkeit lässt sich leicht mit einem Oszilloskop beurteilen. Gleichzeitig wird aber die Gesamtzeit für 500 Impulse gemessen und angezeigt, um einen Eindruck von der erreichbaren Genauigkeit zu erhalten.

 

 

Abb. 3.4 Messung der tatsächlichen Impulszeiten ((Delay.GIF))

 

Das Programm DelayTest.dpr verwendet zwei unterschiedliche Methoden zur Zeitmessung. Einmal wird Delay benutzt, um die Zeitverzögerung für jede Phase des Signals zu erzeugen. Bei dieser Methode addieren sich aber die übrigen Rechenzeiten zur Delay-Zeit hinzu. Abb. 3.4 zeigt einen zusätzlichen Zeitbedarf von 17% bei einer eingestellten Phasendauer von 100 µs. Dieser Wert ist vom verwendeten PC abhängig.

 

Die bessere Methode ist hier die Verwendung von While-Schleifen mit der Funktion TimeRead. Die Zeit wird so lange gemessen, bis ein vorberechneter Zeitpunkt erreicht ist. Rechenzeiten gehen damit in die Verzögerungszeit ein, sodass insgesamt eine wesentlich höhere Genauigkeit erreicht wird.

 

unit DelayTestf;

 

interface

 

uses

  RSCOM, Controls, Classes, Windows, Messages, SysUtils, Graphics, Forms, Dialogs,

  StdCtrls;

 

type

  TForm1 = class(TForm)

    ScrollBar1: TScrollBar;

    LabelTime: TLabel;

    ButtonTest1: TButton;

    ButtonTest2: TButton;

    LabelTime1: TLabel;

    LabelTime2: TLabel;

    procedure ButtonTest1Click(Sender: TObject);

    procedure FormCreate(Sender: TObject);

    procedure ScrollBar1Change(Sender: TObject);

    procedure ButtonTest2Click(Sender: TObject);

  private

    { Private-Deklarationen}

  public

    { Public-Deklarationen}

  end;

 

var

  Form1: TForm1;

 

implementation

 

{$R *.DFM}

 

 

procedure TForm1.FormCreate(Sender: TObject);

begin

  OpenCOM ('COM2');

end;

 

procedure TForm1.ScrollBar1Change(Sender: TObject);

begin

  LabelTime.Caption := IntToStr(ScrollBar1.Position) + ' us';

end;

 

procedure TForm1.ButtonTest1Click(Sender: TObject);

var N: Integer;

    T: Real;

begin

  RealTime;

  TimeInit;

  T := ScrollBar1.Position / 1000;

  For N := 1 to 500 do begin

    DTR (1);

    Delay (T);

    DTR (0);

    Delay (T);

  end;

  T := TimeRead;

  NormalTime;

  LabelTime1.Caption := FloatToStr(T) + ' ms'

end;

 

procedure TForm1.ButtonTest2Click(Sender: TObject);

var N: Integer;

    T: Real;

begin

  RealTime;

  TimeInit;

  T := ScrollBar1.Position / 1000;

  For N := 1 to 500 do begin

    DTR (1);

    While TimeRead < (2*N-1)*T Do;

    DTR (0);

    While TimeRead < (2*N)*T Do;

   end;

  T := TimeRead;

  NormalTime;

  LabelTime2.Caption := FloatToStr(T) + ' ms'

end;

 

end.

 

Listing 3.9 Impulserzeugung im Mikrosekundenbereich (DelayTest.dpr)


Download: Delphi-Beispiele

 

weiter
zurück