Frage deutsch
~~~~~~~~~~~~~~
Wie bilde ich Wartezeiten ab 0,056 s
Wie umgehe ich dabei das Problem des Timer-Rücksprungs um Mitternacht von 86400.000 auf den Wert "0" ?
 

Question English
~~~~~~~~~~~~~~
How can I generate delays which are multiples of 0,056 sec ?
 
Antwort 1
~~~~~~~~~
[ von Thomas Antoni, 10.5.02 - 12.3.05 ]
 
Die zwei verbreitetsten Methoden hierfür basieren auf den Befehlen SLEEP und TIMER.
 
Im Folgenden der entsprechende Ausschnitt aus meinem QBasic-Kochbuch, das Du auf
www.qbasic.de in der QBasic-Tutorial-Rubrik herunterladen kannst.
 
- SLEEP [<n%>] - Wartezeit n sec einlegen. z.B. SLEEP 2 '2sec warten; nur ganze
sec. Bei PowerBasic nicht vorhanden (durch DELAY ersetzen). Bei SLEEP mit
Parameter '0' oder ohne Parameter wird bis zur nächsten Tastenbetätigung
gewartet. SLEEP ist äußerst unprofessionell, weil der Tastaturpuffer
nicht geleert wird und nach 15-maliger Tastenbetätigung überläuft, was
bei den nächsten Tastenbetätigungen ein ohrenbetäubendes Piepsen zur
Folge hat.
 
- TIMER - Systemuhr, zeigt die nach Mitternacht vergangenen Sekunden mit
einer Auflösung von 18,2 Inkrementen pro sec., d.h. von ca 56ms=0,056 s.
Wartezeiten unter 0,056 sec sind daher mit Timer nicht realisierbar.
Der Timer liefert Gleitpunktwerte vom Typ SINGLE zwischen 0.000 und 86400.000
sec (entspricht den 24 Stunden von 00:00:00h ... 23:59:59h). Bei der
Realisierung von Stoppuhren und Countdown-Timern ist der Rücksprung vom
Maximalwert 86400 auf 0 um Mitternacht zu berücksichtigen.
 
Beispiel zur Erzeugung einer feinaufgelösten Wartezeit von 0,5s:
 
starttime! = TIMER ' seit Mitternacht abgelaufene
' Zeit in s
DO: LOOP UNTIL TIMER > starttime! + .5 ' 0.5 sec warten
 
Mit TIMER können nur Wartezeiten von Vielfachen von 0.056 sec erzeugt werden. Die Bildung noch kürzerer Wartezeiten ist nur mit gewissen Tricks möglich und wird in dem FAQ-Eintrag "Wie bilde ich noch kürzere Wartezeiten prozessorunabhängig" erklärt. Nur -> PowerBASIC bietet hier für einen "Microtimer" (MTIMER), der solche kurzen Wartezeiten mit "Bordmitteln" ermöglicht.
 
QBasic-Anfänger verwenden für die Bildung von Wartezeiten gerne den SLEEP- Befehl. Mit "SLEEP n" kann man Programmablauf für eine ganze Anzahl von n Sekunden anhalten. SLEEP 3 fügt z.B. eine Pause von 3 sec ein. Als kleinste Pausenzeit kannst Du 1 sec erreichen, indem Du "SLEEP 1" verwendest.
 
Wie kann man nun aber genauere und kleinere Wartezeiten als mit SLEEP erzeugen?
 
Dazu verwendet man die TIMER-Funktion. TIMER liefert den aktuellen Stand der Systemuhr zurück und zeigt die Anzahl der seit Mitternacht vergangenen Sekunden an. TIMER hat eine Auflösung von 18,2 "Takten" pro sec. D.h. TIMER wird alle 0,055 sec (= 55 ms) um den Wert 0,055 erhöht. Wartezeiten unter 0,055 sec sind daher mit TIMER nicht realisierbar.
 
Der Timer liefert Gleitpunktwerte vom Typ SINGLE zwischen 0.000 und 86400.000 sec (24 x 3600 = 86400; entspricht den in 24 Stunden von 00:00:00h ... 23:59:59h verstrichenen Sekunden). Bei der Realisierung von Wartezeiten, Stoppuhren und Countdown-Timern ist der Rücksprung des TIMERs vom Maximalwert 86400.000 auf 0.000 um Mitternacht zu berücksichtigen (genauer gesagt, von 86399.444 auf Null, denn statt der 86400.000 wird um Mitternacht ja die Null zurückgeliefert).
 
Das folgende Progrämmchen zeigt, wie Du Wartezeiten mit TIMER realisierenkannst.
 
'***************************************************************
' TIMER.BAS = Erzeugung von Wartezeiten
' =========
' Der Anwender gibt eine Wartezeit in sec ein.
' Anschliessend wird die TIMER-Funktion
' verwendet, um den Programmablauf fuer diese
' Zeit anzuhalten. Wenn die Zeit abgelaufen
' ist, ertoent ein Piepston.
'
' (c) Thomas Antoni, 12.11.2003
'****************************************************************
CLS
PRINT "Gib eine Wartezeit in [sec] ein; ";
PRINT "Nachkommastellen erlaubt....t = ";
INPUT t!
starttime! = TIMER 'Startzeit merken
'
DO
PRINT TIMER - starttime! 'abgelaufene Zeit anzeigen
LOOP UNTIL TIMER > starttime! + t! 'Warteschleife
'
PRINT "Wartezeit ist abgelaufen"
BEEP
END
 
Das obige Programm steht im Verzeichnis Progs\ zur Verfügung sowie online unter www.antonis.de/faq/progs/timer.bas .
 
*** Die Problematik des Timer-Überlaufs um Mitternacht
Um Mitternacht springt der System-TIMER von 86399.944 auf 0 zurück. Dadurch kommt es bei normalen Timer-Routinen, wie sie gemeinhin verwendet werden zu Problemen (z.B. auch beim obenstehenden Programm TIMER.BAS).
 
Der TIMER hat z.B. 5 sec. vor Mitternacht einen Wert von 86395. Will man jetzt eine Wartezeit von 10 sec einfügen, wartet das Programm solange, bis der um 10 erhöhte Wert von 86405 erreicht wird. Dieser Wert wird aber nie erreicht, wegen des Rücksprungs von 86399.944 auf 0 . Daher wartet das Programm ewig und drei Tage und das Programm hängt sich genau um Mitternacht in einer Dauerschleife auf.
 
Mein folgendes Programm TIMER24.BAS umschifft dieses Problem und erzeugt "saubere" Wartezeiten von bis zu 24h:
 
'***************************************************************************
' TIMER24.BAS = erzeugt Wartezeiten, die auch um Mitternacht funktionieren
' ===========
' Dieses Q(uick)Basic-Programm erzeugt Wartezeiten mit einer
' zeitlichen Aufloesung von 0,056 sec, die auch um Mitternacht herum
' funktionieren. Das Programm verwendet den System-TIMER, der die ab
' Mitternacht verstrichenen Sekunden zaehlt und alle 0.055556 sec erhoeht
' wird. Der Anwender gibt eine Wartezeit in sec ein. Das Programm
' berechnet hieraus und aus dem momentanen Stand des System-TIMERs den
' in der Zukunft liegenden TIMER-Stand nach Ablauf der Wartezeit. Dabei
' wird auch der Ruecksprung ("Rollover") des TIMERs um Mitternacht von
' 86399.944 auf 0 beruecksichtigt. Wenn die Wartezeit abgelaufen ist,
' erfolgt eine entspechende Anzeige und es ertoent ein Piepston.
'
' (c) Thomas Antoni, 12.3.2005
'**************************************************************************
DECLARE SUB Pause24 (Wartezeit!)
DO
CLS
PRINT "Gib eine Wartezeit in [sec] ein ";
PRINT "(Dezimalpunkt erlaubt)"
INPUT "t = ", t!
IF t! = 0 THEN END 'Programm beenden wenn "0" eingegeben
CALL Pause24(t!)
PRINT "Wartezeit ist abgelaufen"
BEEP
PRINT
PRINT "Wiederholen...[beliebige Taste] Beenden...[Esc]"
DO: Taste$ = INKEY$: LOOP WHILE Taste$ = ""'Warten auf Tastenbetaetigung
IF Taste$ = CHR$(27) THEN END
LOOP
'
SUB Pause24 (Wartezeit!)
'******************************************************************
' Pause24 = Q(uick)Basic-Subroutine zur Erzeugung von Pausen von
' ======= max. 24h , die auch um Mitternacht herum funktionieren
'******************************************************************
'
'---- Start und Endezeit ermitteln
Startzeit! = TIMER
Endezeit! = Startzeit! + Wartezeit!
'
'---- Warteschleife, wenn kein Ueberlauf erfolgt
IF Endezeit! <= 86399.94 THEN
DO: LOOP WHILE TIMER < Endezeit!
'
'---- Warteschleife wenn Ueberlauf des Timers
'---- um Mitternacht von 86399.94 auf 0 erfolgt
ELSE
DO
LOOP WHILE (TIMER >= Startzeit!) OR (TIMER < (Endezeit! - 86400))
END IF
'
END SUB
 
Das obige Programm steht im Verzeichnis Progs\ zur Verfügung sowie online unter www.antonis.de/faq/progs/timer24.bas .
 
 
*** Realisierung schnellstmöglicher Animationen mit TIMER
Wenn Du Animationen schnellstmöglich aublaufen lassen willst, kannst Du Wartezeiten von 0,056 sec realisieren, indem Du einfach immer vor jeder Bildschirmausgabe solange wartest, bis sich der TIMER-Wert geändert hat.
 
So lässt Du z.B. den Buchstaben "X" animiert quer über den Bildschirm laufen:
 
SCREEN 8
CLS
SLEEP 1 '1 sec Wartezeit
FOR i% = 1 TO 79
LOCATE 3, i%
PRINT "X"
t = TIMER
DO: LOOP UNTIL TIMER <> t '0,056 sec Wartezeit
NEXT i%
 
 

Answer 2
~~~~~~~~~
How do I do a better delay than FOR .. NEXT?
Try using the timer... while it may not get as small of increments as you want, it's usually, good, or you can look into some of the other delayers that can do really small units of time. Basically, get a long variable to hold the current timer. (CurrentTime! = TIMER), then just do .. loop until the current timer equals the old timer, plus the number of seconds you want to delay.
 
 

Answer 3
~~~~~~~~~~
[ by Bill White ]
 

HOW CAN I PRODUCE A DELAY IN MY PROGRAM INDEPENDENT OF THE CPU SPEED?
 
*** Question:
I am writing a program and I am wanting the opening screen to pause for maybe 3 seconds (I can work on revising the amount of time if I can ever get the screen to pause) before continuing on to the rest of the program. I have tried TIMER (3) variations, but haven't been able to get anything to work. It keeps going straight into the program. Does anybody have a routine to pause a program for a certain amount of time before continuing? Thankyou.
 
*** Answer:
The old BASIC statement we used for years:
 
FOR i=1 to 1000: NEXT i
 
is not independent of the CPU speed.
 
You could use SLEEP 2. This, however, has problems: integer numbers only, user can hit _ANY_ key to jump out of it, and that key will be held in the buffer waiting to bite the next INPUT command unless dumped with DO: LOOP UNTIL INKEY$ = ""
 
TIMER can be used - it ticks off 1/18.2 of a second:
 
delay = 2
finish = TIMER + delay
DO
LOOP UNTIL TIMER => finish
 
This works, but has a fatal midnight flaw: the timer is reset to 0 at midnight and it is therefore possible that "finish" will never be reached. It's easy to test for midnight (there are 86,400 seconds in a day). I leave that as an exercise for the reader!
 
However, it is easy not to depend on TIMER to do the counting - count it yourself. Something like:
 
delay = whatever
t! = INT(TIMER)
DO
IF t! <> INT(TIMER) THEN
t! = INT(TIMER)
count = count + 1
PRINT count
END IF
LOOP UNTIL count = delay
 
This routine doesn't care what the reading of TIMER is, onlythat it has changed. A roll-over at midnight is just as valid a change as an increase of one second. If you want 1/10's of a second, you'll need to alter accordingly. Since TIMER ticks at 18.2 times a second, the smallest interval will be 0.0549450549 sec.
 
If you're looking for something else, Chad Beck suggests the following:
 
Here's a delay routine[...]: it's small, it doesn't use floating point values, it has an 18th of a second accuracy, and it accounts perfectly for midnight. Num18ths is the number of 18ths of a second that you want to delay for. [In other words, Num18ths is the number of seconds you want in the delay, times 18 -- or, more accurately, 18.2.]
 
DEFINT A-Z
DEF SEG = 0
'
FOR Delay = 1 to Num18ths
Timr = PEEK(&H46C) 'Read BIOS timer tick count
DO
LOOP WHILE Timr = PEEK(&H46C)
NEXT
 

[ The QBasic-MonsterFAQ --- Start Page: www.antonis.de/faq ]