Problem mit SetWaitableTimer und WaitForSingleObject



  • Hallo,

    mit dem Borland C++ Builder 5 versuche ich die Geschwindigkeit einen Schrittmotors zu regulieren, welcher über die serielle Schnittstelle angesprochen wird (unter Windows 2000).

    Ich versuche also die Zeit zu ermitteln, die das Programm für einen Schritt benötigt. Dies tue ich mit clock_t aus der time.h. Falls die Zeit des Motorschrittes geringer ist, wie es die Motorgeschwindigkeit erfordert, verzögere ich mit einem Waitable Timer Objects aus der windows.h (nach einem Beispiel aus der msdn http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/using_waitable_timer_objects.asp).
    Angeblich soll die Genauigkeit bei 100ns liegen. Ich habe aber das Problem das die Verzögerung bei mir mindestens bei 15ms liegt.

    Hier der Code, jedenfalls der wichtige Teil:

    //Variablen zum messen der Prozessorzeit.
    clock_t zeitstart, zeitende;
    float laufzeit, sollzeit, wartezeit;
    
    //Zeit pro Schritt berechnen. sollzeit ist immer zwischen 0,003 - 1,5 Sekunden.
    sollzeit = 60.0/(motorgeschwindigkeit * 40.0);
    
    //Timer erzeugen.
    HANDLE hTimer = NULL;
    LARGE_INTEGER liDueTime;
    // Create a waitable timer.
    hTimer = CreateWaitableTimer(NULL, TRUE, "WaitableTimer");
    
    ...
    
    //Den Motor ansteuern bis er auf die gewünschte Position gefahren ist.
    while((motorposition < anfahrposition) && (!motorstopp)){
      //Zeit beim Start des Motorschrittes messen.
      zeitstart = clock();
    
      ... //Ausführen des Motorschrittes.
    
      //Zeit am Ende des Motorschrittes messen.
      zeitende = clock();
      //Laufzeitberechnen. Wenn durch CLK_TCK geteil wird, erhält man die Zeit
      //  in Sekunden.
      laufzeit = (zeitende - zeitstart)/CLK_TCK;
    
      if(sollzeit > laufzeit){
        //Da beim Timer 1ms = -10000 entsprechen, muss die Zeit umgerechnet werden.
        //D.h. aus 0.001 muss -10000 werden. Also mal Faktor -10000000 rechnen.
        wartezeit = (sollzeit - laufzeit) * (-10000000);
    
        //Wartezeit angeben. -100000000 entspricht 10Sekunden.
        liDueTime.QuadPart = wartezeit;
        //Setzen des Timers.
        SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0);
    
        //Hier messe ich nochmal die Zeit, die die WaitForSingleObject wartet. 
        zeitstart = clock();
    
        //Warten bis die Zeit abgelaufen ist, maximal aber 1500ms.
        WaitForSingleObject(hTimer, INFINITE);
    
        zeitende = clock();
        //Die Zeit während des wartens ist immer mindesten 15ms. 
        //Der Motor fährt auch deutlich zu langsam!
        laufzeit = (zeitende - zeitstart)/CLK_TCK;
      }
    }
    

    Was mache ich falsch? Auch wenn ich als wartezeit den Wert -30000 (für 3ms) fest eingebe im Code, benötigt er länger (die Besagten 15ms). Ich würde mich freuen, wenn mir jemand helfen kann. Ich habe bereis in diversen Foren gesucht aber keine Lösung für mich gefunden.

    P.S. Kann mir jemand erläutern was der Unterschied zwischen absuluter und relativer Zeit bei SetWaitableTimer ist?



  • Wenn du sehr hohe Genauigkeit benötigst wirst du wohl mit Windows sehr bald an die Grenzen stoßen. 🙄



  • Wie gesagt, die Genauigkeit soll bei etwa 100 ns liegen. Bei mir weicht die Zeit aber um 12ms ab. Entweder die haben sich einen Scherz erlaubt (was ich nicht hoffe) oder ich habe einen (Denk)Fehler drin.

    Oder gibt es eine Alternative? Mit Sleep habe ich es schon probiert, das kann man vergessen.

    Gruss splitta



  • wie flenders gesagt hat ist die genauigkeit nicht so berauschend.
    ich würde es gar nicht mit einem timer machen sondern mit einer schleife die den aktuellen tick abfrägt. in der schleife würde ich dann solange warten bis die bedingung erfüllt ist. denn tick würde ich entweder über RDTSC der cpu holen (musst a bissl assembler lernen) oder über QueryPerformanceCounter.
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/Timers/TimerReference/TimerFunctions/QueryPerformanceCounter.asp



  • Ich habe auf miller_m's anraten und einem Beispiel aus diesem Forum mal was entworfen. Dieser Code funktioniert für meine Bedürfnisse sehr gut. Ich kann die Wartezeiten ausreichend genau bestimmen und in der Schleife verzögern.

    double  laufzeit, sollzeit, wartezeit;
    LONGLONG Frequency, CurrentTime, LastTime;
    double TimeScale;
    
    //Zeit pro Schritt berechnen. Liegt immer zwischen 3ms und 1500ms.
    sollzeit = 60.0/(motorgeschwindigkeit * 40.0);
    
    //Bestimmen der CPU-Frequenz.
    QueryPerformanceFrequency( (LARGE_INTEGER*) &Frequency);
    //t = 1/f, bestimmen der Zeit die ein CPU-Takt benötigt.
    TimeScale = 1.0/Frequency;
    
    //Den Motor ansteuern bis er auf die gewünschte Position gefahren ist.
    while((motorposition < anfahrposition) && (!motorstopp)){
    
      //Den Fortschritt des CPU-Taktes bestimmen.
      QueryPerformanceCounter( (LARGE_INTEGER*) &LastTime);
    
      ... //Irgendwas machen. 
    
      //Den Fortschritt des CPU-Taktes bestimmen.
      QueryPerformanceCounter( (LARGE_INTEGER*) &CurrentTime);
    
      //Die Differenz der Takte mal die Zeit pro Takt ergibt die vergangene Zeit
      //  in Sekunden.
      laufzeit = ((CurrentTime - LastTime) * TimeScale);
    
      //Wenn die Laufzeit geringer wie die Sollzeit ist...
      if(sollzeit > laufzeit){
        //...dann die Wartezeit berechnen.
        wartezeit = (sollzeit - laufzeit);
    
        //Den Takt-Wert der letzten ermittelten Zeit merken.
        LastTime = CurrentTime;
    
        //Solange die Wartezeit grösser der Laufzeit der Schleife ist fortfahren.
        do{
          //Den Fortschritt des CPU-Taktes bestimmen.
          QueryPerformanceCounter( (LARGE_INTEGER*) &CurrentTime);
          //Die Differenz der Takte mal die Zeit pro Takt ergibt die vergangene 
          //  Zeit in Sekunden.
          laufzeit = ((CurrentTime - LastTime) * TimeScale);
        }while(laufzeit < wartezeit);
      } 
    }
    

    Nochmal besten dank miller_m

    Gruss splitta


Anmelden zum Antworten