This code provides a timer system that allows many timers to be defined, all slaved from a single periodic interrupt. The Forth words in the user accessible group documented below are compatible with the token definitions for the PRACTICAL virtual machine, with the code supplied with MPE's embedded targets, and with VFX Forth. This code assumes the presence of a global value TICKS which holds a time value incremented in milliseconds. The timebase is approximate, and granularity and jitter are affected by the timer ISR and the time taken by your own code to execute. By default, the timer is set to run every 100ms. The source code is in the the file TIMEBASE.FTH.
The timer chain is built using a buffer area, and two chain pointers. Each timer is linked into either the free timer chain, or into the active timer chain.
All time periods are in milliseconds. Note that on a 32 bit system such as ProForth VFX, these time periods must be less than 2^31-1 milliseconds, say 596 hours or 24 days, whereas if the code is on a 16 bit system, time periods must be less than 2^15-1 milliseconds, say 32 seconds.
These basic words are defined for applications to use the timer system. Other words are detailed elswhere in this chapter.
|
After the timers have been started, actions can be added. The example below starts a timer which puts a character on the debug console every two seconds. Note that when using generic I/O, the output and input devices MUST be specified.
|
The item on stack is a timer handle, use TSTOP to halt this timer.
AFTER is very useful for creating timeouts, such as required to determine if something has happened in time. AFTER returns a timerid. If the action you are protecting happens in time, just use TSTOP when the action happens, and the timer will never trigger. If the action does not happen, the timer event will be triggered.
All timers are executed within a single interrupt, and so all timer action words share a common user area. This has some impact on timer action words. Since you do not know in which order timer action words are executed, you must set up any USER variables such as BASE that you may use, either directly or indirectly.
The interrupt that handles all the timers does not set IPVEC and OPVEC to a default value. If you are going to use Forth I/O words such as EMIT and TYPE within a timer action, you MUST set IPVEC and OPVEC before using the I/O. For the sake of other timer action routines that may still be using default I/O, it is polite to save and restore IPVEC and OPVEC in your timer action words.
Do not worry about calling TSTOP with a timerid that has already been executed and removed from the active timer chain; if TSTOP cannot find the timer, it will ignore the request.
Under some conditions, the execution time of all the timer routines may be longer than the requested period of the timer. In addition, the timer interrupt may be subject to jitter.
The following discussion is relevant if you want to modify this code. Functionally equivalent code is provided with MPE's VFX Forth systems. In the Windows environment, timer interrupts are implemented by callbacks and critical sections.
By default, the word DO-TIMERS is run from within the periodic timer interrupt. If interrupts are not re-enabled after resetting the timer interrupt, you may have latency issues if a number of timers is used, or if the timer routines take a considerable time. In this case, it would be better to set up the timer routine to RESTART a task, which calls DO-TIMERS, e.g.
|
0 value ticks \ -- addr ; holds timer count
Get current clock value in milliseconds.
#8 constant #timers \ -- n ; maximum number of timers
A constant used at compile time to set the maximum number of timers
required. Each timer requires RAM as defined by the ITIMER
structure.
: after \ xt period -- timerid/0 ; xt is executed once,
Starts a timer that executes once after the given period. A timer
ID is returned if the timer could be started, otherwise 0
is returned.
: every \ xt period -- timerid/0 ; xt is executed periodically
Starts a timer that executes every given period. A timer
ID is returned if the timer could be started, otherwise 0
is returned. The returned timerID can be used
by TSTOP to stop the timer.
: tstop \ timerid --
Removes the given timer from the active list.
: pause \ -- ; multitasker hook
Allows the sytem multitasker to get a look in. Under Windows
this also allows the message queue to be handled.
: later \ n -- n'
Generates the timebase value for termination in n millseconds time.
: expired \ n -- flag ; true if timed out
Flag is returned true if the timebase value n has timed out.
N.B. Calls PAUSE.
: timedout? \ n -- flag ; true if timed out
Flag is returned true if the timebase value n has timed out. Does not
call PAUSE, so can be used in interrupts, winprocs and callbacks. In particular,
TIMEDOUT? should be used rather than EXPIRED inside timer action words
to reduce timer jitter.
: ms \ n --
Waits for n milliseconds. Uses PAUSE through EXPIRED.