home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
World of A1200
/
World_Of_A1200.iso
/
datafiles
/
text
/
c_manual
/
devices
/
timerdevice
/
timerdevice.doc
< prev
next >
Wrap
Text File
|
1995-02-27
|
22KB
|
683 lines
2 TIMER DEVICE
2.1 INTRODUCTION
The timer device may not sound very exciting but it can be
extremely useful. Many so called "high level" devices are using
the timer device, and very often you need to use it yourself.
It is not very accurate since other tasks running at the same
time may delay the time reports, but is usually good enough,
and in the long run it is very accurate.
The timer device is a very simple device. It's main task is to
send messages to all its users after specified time periods.
The timer device can also compare different time periods, and
add as well as subtract one time period with another. No
thrills, but useful stuff.
2.2 TIMER
The timer device's main task is to send time reports to all its
users. It is controlled like all other devices by sending
request blocks, and is therefore easy to handle. See previous
chapter "Devices" for more information about devices and
request blocks.
2.2.1 TIME REQUEST
The timer device's request block is an "extended" request
block and is defined in header file "devices/timer.h":
struct timerequest
{
struct IORequest tr_node;
struct timeval tr_time;
};
tr_node: As with all request blocks, the top part consists of
an IORequest structure which is used by Exec to handle
the requests. While using the timer device you do not
need to use this structure (well, at least not very
often). Simply let the system take care of it.
tr_time: The second part of the request block consists of a
timeval structure: (Also defined in the header file
"devices/timer.h".)
struct timeval
{
ULONG tv_secs;
ULONG tv_micro;
};
tv_secs: The number of seconds you want to wait before
the time request should be completed.
tv_micro: The number of microseconds. Note that the
timer device is not very accurate. Your
requests can be delayed by other programs
running at the same time, but in the long
run the timer device is accurate enough.
As with all devices you have to connect a message port to
the request block with which the timer device can talk to you.
Create a message (reply) port by calling the CreatePort()
function. Normally you should not use any name (the message
port should not be made public) and priority set to 0 (normal
priority).
Synopsis: msg_port = CreatePort( name, pri );
msg_port: (struct MsgPort *) Pointer to the new MsgPort
structure, or NULL if something went wrong.
name: (char *) Pointer to a string containing the name
of the message port, or NULL. When working with
devices you normally do not need to use any name.
pri: (BYTE) This message port's priority. Usually set
to 0 (normal priority).
Once you have successfully opened a message port you may
allocate and initialize the request block. Since the request
block is "extended" (not the same size as the "standard" -
IOStdReq structures) you need to use the CreateExtIO()
function to allocate and initialize the request block.
(Do not use the CreateStdIO() function!)
Synopsis: ext_req = CreateExtIO( msg_port, size );
ext_req: (struct IORequest *) Pointer to the new extended
request block, or NULL if the request block could
not be created.
msg_port: (struct MsgPort *) Pointer to the message port
the CreatePort() function returned. The timer
device will use it to send messages to you.
size: (long) The number of bytes that should be allocated
for the extended request block. Use the function
sizeof() to find the exact number of bytes needed.
Example: sizeof( struct timerequest ).
Here is an example:
/* The reply port: */
struct MsgPort *replymp;
/* The timer request block: */
struct timerequest *timer_req;
...
/* Get a reply port: (No name, normal priority) */
replymp = (struct MsgPort *)
CreatePort( NULL, 0 );
if( !replymp )
clean_up( "Could not create the reply port!" );
/* Allocate and preinitialize a request block: */
timer_req = (struct timerequest *)
CreateExtIO( replymp, sizeof(struct timerequest) );
if( !timer_req )
clean_up( "Could not create the request block!" );
2.2.2 OPEN THE TIMER DEVICE
Once you have successfully created a request block you can
"open" the timer device. You should, however, first decide how
accurate you want the timer device to be. The timer device can
work in two different modes:
1. The timer device can use the video beam to control its own
timer. This will make the timer very stable over long time
periods, but is not very accurate for short intervals
(less than one second).
This mode is easy for the timer device to handle and thus
little computer time is used. This mode is called
"vertical blank timer" and the flag is "UNIT_VBLANK".
2. If you want to use very short time periods (less than one
second) it can sometimes be necessary to use the special
CIA chips for the timer. The CIA chips has a much higher
"resolution" than the video beam, thus it will result in
very accurate time periods for short intervals. This mode
is more complicated than the vertical blank timer, and
more computer time is therefore used. This mode is called
"micro hertz timer" and the flag is "UNIT_MICROHZ"
The general rule is that time periods longer than one second
should use the vertical blank timer. Else, if really high
"resolution" is needed, the micro hertz timer should be used.
Once you have decided which mode should be used you can open
the timer device. No frills here, simply use the OpenDevice()
function.
Synopsis: error = OpenDevice( name, unit, req, flags );
error: (long) If OpenDevice() managed to open the timer
device it returns 0, else an error number is
returned.
name: (char *) Name of the device you want to open.
The name for the timer device has been defined
as "TIMERNAME" (header file "devices/timer.h").
unit: (long) Which timer mode you want the device to
use. Set this field to either "UNIT_VBLANK" or
"MICROHZ".
req: (struct IORequest *) Pointer to a previously
initialized request block.
flags: (long) Special flags which are ignored by the timer
device.
Here is an example:
/* Open the Timer Device: ("vertical blank timer") */
error = OpenDevice( TIMERNAME, UNIT_VBLANK, timer_req ,0 );
if( error )
clean_up( "Could not open the Timer Device!" );
2.2.3 SET TIME REQUEST
When the timer device has been opened you can start to send your
time requests. You specify the number of seconds and microseconds
that should pass before a message will be sent by initializing
the timeval structure. Note that the time you set is the minimum
time. When the exact time has elapsed it may happen that some
other program is for the moment occupying the processor, and
thus the time message will be delayed for some microseconds.
You must also tell the device that you are sending a time
request. You do it by setting the flag "TR_ADDREQUEST" in the
"io_Command" field.
The request can now be sent to the device. Either use the DoIO()
function if you want to wait for the request to be completed,
or use the SendIO() function if you want your program to
continue to run while the request is executed. See the previous
chapter for a complete explanation on when and how the DoIO()
and SendIO() functions should be used.
Here is an example:
/* Set time: (5.25 seconds)*/
timer_req->tr_time.tv_secs = 5;
timer_req->tr_time.tv_micro = 250000;
/* We want to add a time request: */
timer_req->tr_node.io_Command = TR_ADDREQUEST;
/* Do our request and return when done: */
/* (Our task is put to sleep for 5.25 sec.) */
DoIO( timer_req );
If you want to send multiple requests you have to use several
request blocks which each has been properly initialized. You
must then also use the function SendIO() and not DoIO() since
your program will else be put to sleep directly after the first
request has been sent. See example 2.
Each time you send a request to the timer device all values in
the timeval structure are destroyed. You must therefore always
reinitialize these fields before you may repost the request to
device.
2.2.4 CLEAN UP
As always you must remember to clean up after you. All
allocated request blocks must be removed, all opened message
ports closed as well as all devices.
Fist you should close the timer device. Note that all requests
you have posted to that device must either have been completed
or aborted before you may close the device! All devices are
closed by the special CloseDevice() function.
Synopsis: CloseDevice( req );
reg: (struct IORequest *) Pointer to the timer device's
own request block. (The same request block which
was used when you opened the timer device.)
The request blocks are deleted by calling the DeleteExtIO()
function. Note that the request must have been completed or
aborted before you may delete it! Note also that you can not
use the DeleteStdIO() function since the timer device is using
extended request blocks.
Synopsis: DeleteExtIO( std_req, size );
std_req: (struct IOStdReq *) Pointer to the request block
you want to delete.
size: (long) The size of the request block, in bytes.
Use the function sizeof() to get the correct number
of bytes. Example: sizeof( struct timerequest ).
Finally you should close all message ports by calling the
DeletePort() function. All messages must have been removed
before you may close the message port.
Synopsis: DeletePort( msg_port );
msg_port: (struct MsgPort *) Pointer to the MsgPort structure
that should be deleted.
Here is a short example:
/* Close the Timer Device: */
CloseDevice( timer_req );
/* Delete the request block: */
DeleteExtIO( timer_req, sizeof( struct timerequest) );
/* Remove the message port: */
DeletePort( replymp );
2.2.5 EXAMPLE
Here is a complete example on how to use the timer device. It
will open a message port, create a request block, open the timer
device, and finally put itself to sleep for 10 seconds. When the
time has elapsed everything is returned and the program
terminates. (Hmmm, very similar to example 1.)
#include <exec/types.h> /* STRPTR */
#include <exec/ports.h> /* struct Message */
#include <exec/memory.h> /* MEMF_PUBLIC */
#include <devices/timer.h> /* TIMERNAME */
/* The reply port: */
struct MsgPort *replymp;
/* The timer request block: */
struct timerequest *timer_req;
/* When the Timer Device is open this variable is TRUE: */
BOOL not_opened = TRUE;
/* Declare our functions: */
void clean_up();
void main();
void main()
{
/* 1. Get a reply port: */
replymp = (struct MsgPort *)
CreatePort( NULL, 0 );
if( !replymp )
clean_up( "Could not create the reply port!" );
/* 2. Get an extended request block: */
timer_req = (struct timerequest *)
CreateExtIO( replymp, sizeof( struct timerequest) );
if( !timer_req )
clean_up( "Could not create the IO!" );
/* 3. Open the Timer Device: */
not_opened = OpenDevice( TIMERNAME, UNIT_VBLANK, timer_req ,0 );
if( not_opened )
clean_up( "Could not open the Timer Device!" );
/* 4. Set our request: */
timer_req->tr_node.io_Command = TR_ADDREQUEST; /* Add a request. */
timer_req->tr_time.tv_secs = 10; /* 10 seconds. */
timer_req->tr_time.tv_micro = 0; /* 0 micro seconds. */
/* 5. Do our request: (Wait 10 seconds) */
printf( "Good night...\n" );
DoIO( timer_req );
printf( "Good morning!\n" );
/* Clean up and quit: */
clean_up( "The End!" );
}
void clean_up( text )
STRPTR text;
{
/* Close the Timer Device: */
if( !not_opened )
CloseDevice( timer_req );
/* Delete the extended request block: */
if( timer_req )
DeleteExtIO( timer_req, sizeof( struct timerequest) );
/* Remove the message port: */
if( replymp )
DeletePort( replymp);
/* Print the last message: */
printf( "%s\n", text );
/* Quit: */
exit( 0 );
}
2.3 SYSTEM TIME
The timer device can also be used to get the current system
time. It should be noted that this system time has nothing to
do with the Amiga's internal clock. The system time is simply
a value which is initialized to the date when the boot disk was
last modified. The value is relative to 1 January 1978,
time 00:00:00.
Although it may not be a "correct" time value it can be useful.
For example, if you want to adjust several time periods with
each other it can be handy to have an external timer like the
system time.
With help of the timer device you can both get the current
system time as well as set it to a new value.
2.3.1 GET SYSTEM TIME
To get the current system time you set the flag "TR_GETSYSTIME"
in the io_Command field of the request block and send the
request block to the timer device. When the timer device has
completed the request it is returned and you may now examine the
timeval structure where the current system time has been stored.
Each time you get the system time you will receive a value which
is different from previous calls (it is an unique value).
Here is a short example:
/* Get the current system time: */
timer_req->tr_node.io_Command = TR_GETSYSTIME;
/* Do the request: */
DoIO( timer_req );
/* Print the current system time: */
printf( "The current system time is:\n" );
printf( "Seconds: %d\n", timer_req->tr_time.tv_secs );
printf( "Microseconds: %d\n", timer_req->tr_time.tv_micro );
2.3.2 SET SYSTEM TIME
To set the system time to a new value simply set the
"TR_SETSYSTIME" flag in the io_Command field of the request
block, set the new time in the timeval structure, and finally
send the request block to the timer device.
You may only set values that are greater than the current
time. The timer device or other programs may be confused if
the time suddenly went backwards.
Here is a short example:
/* Add two hours and 0.4 seconds: */
tr->tr_time.tv_secs += 60 * 60 * 2;
tr->tr_time.tv_micro += 400000;
/* Set the new system time: */
tr->tr_node.io_Command = TR_SETSYSTIME;
/* Do the request: */
DoIO( tr );
2.4 SPECIAL TIME FUNCTIONS
The timer device can also be used to compare different time
reports and to add or subtract time values. To do these things
the timer device contain three functions - CmpTime(), AddTime()
and Subime(). Since these features are accessed as functions
rather than using request blocks you have to prepare the timer
device a little before you may use these functions.
The timer device is using a global pointer which is always
called "TimerBase". This global pointer is used to find the
starting address of the three special timer functions. Before
you may use these functions you must therefore declare and
initialize the TimerBase pointer. To initialize it simply use
any request block which is connected to the timer device. In
the "io_Device" field of the request block you will find the
current address value of the timer device.
Here is a short example:
/* Declare the global timer pointer: */
struct Device *TimerBase;
/* ... create request blocks, open timer device etc ... */
/* Get the global pointer to the timer device: */
TimerBase = timer_req->tr_node.io_Device;
Once you have found the TimerBase pointer you may start to use
the timer functions. Their primary usage is to coordinate
different time requests with each other.
2.4.1 COMPARE TIMES
The CmpTime() function is used to compare two timeval
structures and returns 0 if they were identical, -1 if the
first timeval structure was greater than the second, and
1 if the opposite (the second timeval structure was greater
than the first).
Synopsis: dif = CmpTime( time1, time2 );
dif: (long) If the two time values were identical 0 is
returned. If the first time value was greater than
the second time value -1 is returned, and if the
opposite (the second time value is greater than the
first time value) 1 is returned.
time1: (struct timeval *) Pointer to the first timeval
structure.
time1: (struct timeval *) Pointer to the second timeval
structure.
2.4.2 ADD TIME
The AddTime() function is used to add the time in one timeval
structure with the time in another timeval structure.
Synopsis: AddTime( tiem1, time2 );
time1: (struct timeval *) Pointer to the first timeval
structure. The value of the second timeval
structure will be added with this timeval
structure, and the result stored here.
time1: (struct timeval *) Pointer to the second timeval
structure.
2.4.3 SUBTRACT TIME
The SubTime() function is used to subtract the time in one timeval
structure with the time in another timeval structure.
Synopsos: SubTime( tiem1, time2 );
time1: (struct timeval *) Pointer to the first timeval
structure. The value of the second timeval
structure will be subtracted with this timeval
structure, and the result stored here.
time1: (struct timeval *) Pointer to the second timeval
structure.
2.4.5 EXAMPLE
This short example demonstrates how to use the timer device's
own functions. ("tr1", "tr2" and "tr3" are three already
initialized requst blocks.)
/* Set the times: */
/* Request 1: */
tr1->tr_time.tv_secs = 10;
tr1->tr_time.tv_micro = 0;
/* Request 2: */
tr2->tr_time.tv_secs = 7;
tr2->tr_time.tv_micro = 0;
/* Request 3: */
tr3->tr_time.tv_secs = 5;
tr3->tr_time.tv_micro = 0;
/* Get a pointer to the timer device: */
TimerBase = tr1->tr_node.io_Device;
/* Compare the first two time val structures: */
printf( "Compare time1 - time2: %d\n",
CmpTime( &(tr1->tr_time), &(tr2->tr_time) ) );
/* Add the time in the third timeval structure to */
/* the second timeval structure. The second timeval */
/* structure should now contain the time 12 sec. */
AddTime( &(tr2->tr_time), &(tr3->tr_time) );
/* Subtract the time in the third timeval structure with */
/* the second timeval structure. The second timeval */
/* structure should now again contain the time 7 seconds. */
SubTime( &(tr2->tr_time), &(tr3->tr_time) );
Note that we have to use pointers to the timeval structures
as arguments [&(tr1->tr_time]. The parentheses around the
expression are actually unnecessary since the "->" operator
already has higher priority than the "&" operator. However,
this makes it easier to read.
2.5 FUNCTIONS
The functions DoIO(), SendIO(), WaitIO(), AbortIO() etc...
have already been defined in the previous chapter, and I
will therefore not repeat all this information here.
There exist three functions which are supported by the timer
device. Before you may use them you have to initialize a
global pointer named "TimerBase". (See above for more
information.)
CmpTime()
The CmpTime() function is used to compare two timeval
structures and returns 0 if they were identical, -1 if the
first timeval structure was greater than the second, and
1 if the opposite (the second timeval structure was greater
than the first).
Synopsis: dif = CmpTime( time1, time2 );
dif: (long) If the two time values were identical 0 is
returned. If the first time value was greater than
the second time value -1 is returned, and if the
opposite (the second time value is greater than the
first time value) 1 is returned.
time1: (struct timeval *) Pointer to the first timeval
structure.
time1: (struct timeval *) Pointer to the second timeval
structure.
AddTime()
The AddTime() function is used to add the time in one timeval
structure with the time in another timeval structure.
Synopsis: AddTime( tiem1, time2 );
time1: (struct timeval *) Pointer to the first timeval
structure. The value of the second timeval
structure will be added with this timeval
structure, and the result stored here.
time1: (struct timeval *) Pointer to the second timeval
structure.
SubTime()
The SubTime() function is used to subtract the time in one
timeval structure with the time in another timeval structure.
Synopsos: SubTime( tiem1, time2 );
time1: (struct timeval *) Pointer to the first timeval
structure. The value of the second timeval
structure will be subtracted with this timeval
structure, and the result stored here.
time1: (struct timeval *) Pointer to the second timeval
structure.
2.6 EXAMPLES
Example 1
This example demonstrates how you can use the Timer Device.
The program will be put to sleep for 10 seconds.
Example 2
This example demonstrates how you can send several requests
to the Timer Device.
Example 3
This example demonstrates how you can use the Timer Device
to get the current system time. We will then add two hours
and set the new system time.
Example 4
This example demonstrates how you can compare, add and
subtract time values with help of the timer device's own
functions.