Stoppuhr realisieren durch Timer: Riesenproblem
-
Tag! Ich benutze Visual C++ 6.0 und brauche für ein zukünftiges Projekt eine Stoppuhr, die möglichst genau läuft (im hundertstel-bereich). Also hab ich mich hingesetzt und eine MFC dialogfeldbasierende einfache Testanwendung, mit einem Timer geschrieben: eine ganz simple Stoppuhr, mit "Start", "Stop" und "Zurücksetzen"-Button. Siehe Bild:
die Stoppuhr zeigt "minute:sekunde.hundertstel"
Nun zum Problem: auf den meisten, relativ aktuellen Rechnern, läuft die Stoppuhr zu langsam (sprich: ca. 38 Sekunden für die echte Minute) - so auch auf meinem P4 2,6GHz. Ich hab das bisher immer auf die Ausführung von anderem Code (if-abfragen etc.) neben dem Timer, geschoben. Allerdings habe ich gerade auf einem alten Rechner (P3 400 MHz) probiert, und die Uhr scheint dort genau zu laufen. Woran kann das liegen?
Hier noch der Code:
BOOL CStoppuhrDlg::OnInitDialog() { CDialog::OnInitDialog(); . . . //Member-Variablen init, Startformatierung des Textes, Dialogbildschirm updaten min=0; sec=0; hun=0; m_anzeige.Format("%02d:%02d.%02d",min,sec,hun); UpdateData(FALSE); boolean flag=false; . . . } void CStoppuhrDlg::OnTimer(UINT nIDEvent) { // IDC_ANZEIGE , m_anzeige if(hun>99) { hun=0; sec++; } if(sec>59) { sec=0; min++; } if(min>59) { KillTimer(ID_MYTIMER); hun=0; sec=0; min=0; flag=true; } m_anzeige.Format("%02d:%02d.%02d",min,sec,hun); if(flag==false) hun++; //wenn stunde nicht ereicht, hundertstel hochzählen UpdateData(FALSE); CDialog::OnTimer(nIDEvent); } //start-button void CStoppuhrDlg::OnStart() { flag=false; UpdateData(TRUE); m_anzeige.Format("%02d:%02d.%02d",min,sec,hun); UpdateData(FALSE); SetTimer(ID_MYTIMER, 10, NULL); } //stop-button void CStoppuhrDlg::OnStop() { KillTimer(ID_MYTIMER); } //falls stoppuhr läuft, timer stoppen, anzeige zurücksetzen --> Zurücksetzen-Button void CStoppuhrDlg::OnDel() { // TODO: Code für die Behandlungsroutine der Steuerelement-Benachrichtigung hier einfügen //UpdateData(TRUE); KillTimer(ID_MYTIMER); min=0; sec=0; hun=0; m_anzeige.Format("%02d:%02d.%02d",min,sec,hun); UpdateData(FALSE); }
Enthält der Code noch Fehler? Kann ich es schaffen für alle Rechner eine möglichst genaue Stoppuhr zu programmieren?
Danke für alle die sich die Mühe machen im Voraus!
Falls sich jmd. die Stoppuhr angucken möchte: Download
-
Pfeif doch auf den Timer.
Beim Start merkst Du Dir einfach die Zeit in einem CTime oder COledateTime Objekt.
Dann benutzt Du einen Timer nur um die Daten anzuzeigen.
Beim Stop, holst Du Dir die neue Zeit und bildest die Differenz.Den Timer benutzt Du nur um die Anzeige upzudaten, aber nicht um die Zeit zu errechnen.
-
danke erstmal für die antwort
aber das würde ja bedeuten, ich erfahre die zeit erst beim drücken des stop-buttons, oder?
das ist leider äußerst unpraktisch, ich muss leider sehen können wie die zeit verstreicht
-
War da nicht noch was mit 20 Millisekunden als kleinstes Intervall ?
Und hast du da ein 10 Millisekunden Intervall angegeben, in dem er jeweils die 100stel hoch zählt ?
-
soweit ich weiß ist 20 millisekunden nicht das kleinste intervall
außerdem: wenn ich den timer auf 100 setze, sollte jede zehntel einer sekunde entsprechen, durch ausprobieren hat sich aber herausgestellt, das auch dies zu langsam ist
und ja: ich zähle hunderstel hoch
sind es 100 hunderstel, setze ich hundertstel auf 0 und zähle die sekunde einmal hoch, gebe die sache aus usw.
-
@Martin Richter
Er braucht aber eine Uhr mit Hunderstel und soweit ich das weiss, unterstützt CTime und COleDateTime keine Hunderstel.@scuffle
Trotzdem ist der Ansatz von Martin Richter korrekt. Nur müsstest du z.b. auf timeGetTime zurückgreifen, wo du die tausendstel Sekunden bekommst, seit Windows läuft.Den Timer benutzt du nur noch um, sagen wir mal alle halbe Sekunden die Ansicht zu aktualisieren. Also am Anfang holst du dir beim Start mit timeGetTime den Startwert und speicherst den ab. Zudem startest du den Timer für alle halben Sekunden (500ms). Beim OnTimer Event, holst du dir die aktuelle Zeit seit Windows läuft wieder mit timeGetTime und rechnest die verstrichene Zeit aus und zeigst diese an. Wenn der Benutzer dann auf Stop drückt, zerstörst du den Timer und holst, womöglich vielleicht zuerst, mit timeGetTime wieder die Zahl. Rechnest wieder den unterschied aus und hast dann die genau gestoppte Zeit.
Referenz
timeGetTimeGrüssli
-
ok ich werds mal versuchen, danke
-
So, habs jetzt mal mit timeGetTime() probiert, stehe aber nun vor einem neuen Problem (ich beschränke mich hier auf den timer und den start-button):
void CStoppuhrDlg::OnTimer(UINT nIDEvent) { CDialog::OnTimer(nIDEvent); zeit_messpunkt=timeGetTime(); zeit_vergangen=zeit_messpunkt-zeit_startpunkt; //-------------------------------------- // ? //-------------------------------------- m_anzeige.Format("%02d:%02d.%02d",min,sec,hun); UpdateData(FALSE); } void CStoppuhrDlg::OnStart() { zeit_startpunkt=timeGetTime(); SetTimer(ID_MYTIMER, 10, NULL); }
ok in der "zeit_vergangen" variablen hab ich nun die genaue zeit in millisekunden stehen, die seit klick auf "start" vergangen ist, doch wie bekomme ich die jetzt in die form MM:SS.ZH ??? insbesondere muss ja nach 99 hundertstel sekunden die skala wieder auf 00 wechsel und die sekundenzahl erst nach 59 auf 00 springen usw.
die millisekunden in die variablen "min", "sec", und "hun" umzurechnen bereitet mir grad echtes kopfzerbrechen
kann mir wer auf die sprünge helfen?
-
hmm ich such schon einen tag vergeblich nach 'ner lösung
-
Versuch's mal mit Division/Modulo-Rechnung:
hun = (zeit_vergangen/10) % 100; sek = (zeit_vergangen/1000) % 60; min = (zeit_vergangen/60000);
-
aaah danke die modulo haben's gebracht
darauf bin ich einfach nicht gekommen. in den variablen steht auch jetzt das richtige, nur hab ich bei der ausgabe
m_anzeige.Format("%02d:%02d.%02d", min, sec, hun);
noch probleme. und zwar zeigt er bei programmablauf die minuten an der richtigen stelle, wo eigentlich die sekunden raufgezählt werden, steht immer nur 00, da wo die hundertstel raufgezählt werden sollten, stehen nun die sekunden und die hunderstel werden garnicht angezeigt. - wenn ich die variablen einzeln ausgebe, sehe ich aber das in allen die korrekten werte stehen.
es scheint also ab der zweiten variablen, scheinen die nächsten um eins verschoben und somit werden die hundertstel ganz rausgeschoben ???
kann ich mir nicht erklären...
*edit*
ich habs jetzt selbst rausgefunden: die funktion Format(); hat scheinbar ein problem mehr als eine Variable vom typ LONGLONG auszugeben (die von timeGetTime() zurückgegebenen werte)
das problem hab ich gelöst, indem ich einfach die LONGLONG's auf int's gecastet habe (nach der umrechnung sind die INT's eh nie größer als 99), dann klappts problemlos
-
Ja, das ist ein Problem mit variablen Parameterlisten - die Parameter werden einfach hintereinander auf den Stack geschoben und von der Funktion dann portionsweise wieder ausgelesen (und Format() erwartet für %d einen int, der etwas kleiner ist als dein LONGLONG, dadurch hat er den Sekundenwert offenbar in die oberen Hälfte von min reininterpretiert).
PS: Die Endwerte dürften nicht so groß werden (außer du misst Zeiten von mehreren Tagen mit deinem Programm), also könntest du deine Ausgabevariablen durchaus als int definieren. Ansonsten verwendest du halt als Kennung "%02I64d" (das I64 kennzeichnet den Wert als 64-Bit-Integer (also long long)).
-
ok danke, du scheinst richtig ahnung zu haben
stoppuhr läuft, ist ins projekt eingebaut und nun kanns weiter gehen