Вы находитесь на странице: 1из 5

Timers and timing

Fernando Rodriguez - March 2007

The measurement of time is exteremely important for the control of systems, because
the state of a system typically evolves as time advances. We therefore may need to
determine the duration of a given event, or respond to changes of the system within
an allocate time period. The consequences of missing a deadline or errors in keeping
track of the time (for instance in order to measure the duration of an event, or in order
to produce a specified delay) may result in complete failure of the system.

1 Time delays and time accurate loops


Time delays can be introduced in a program by the use of a delay loop such as these
ones:
Assembly:

delay: lda #$FF


loop: dbnza loop
rts

C code:

void delay(void)
{ int i,j;
for (i=0; i<200; i++)
for (j=0; j<30; j++)
{}
}

Unfortunately there are a number of inconveniences in using these methods. To begin


with it is difficult to determine the exact duration of the delay; particularly in the case
of the C routine, as we need to know exactly how the compiler translates the code into
machine code. Furthermore, this method wastes valuable computing resources (and
energy!).
Consider the following control program written by a clever frog:

1
//USE OF DELAY SUBROUTINE
void main(void)
{
int time=0; //Says the frog
for (;;) //the main (infinite) loop
{
data=scan_pond(); //Call some functions
targets=process(data); //process data to find fly
move_towards(targets); //and somehow move towards it
time++; //Keep track of time
if (time%100==0) //every second
eat_fly(); //eat a Re: fly
delay(10); //Delay 10 ms
}
}

The goal of the delay statement is to ensure the main loop executes once ever 10 ms.
(assuming that the subroutines take less than this time to execute!). The frog assumes
that the variable time will be incremented every 10 ms, so it can eat a fly every second
and avoid starving to death.
Unfortunately for the frog this will not be the case (even if the delay subroutine ex-
ecutes for exactly 10 ms.), as the other statements and subroutines in the code will take
some time to execute. The frog dies.
The frog should have used a hardware timer. All that is required is to make a note
of the current timer value at the beginning (or end) of the loop, and then wait until the
timer has advanced its count by a given amount. With the excitement of such a clever
idea, the frog resurrects and writes:

//USE OF HARDWARE TIMER


//POLLING TIMER VALUE METHOD
void main(void)
{
int time=0; //Says the frog
unsigned int timestamp=10; //End of current time slot
//i.e. 10 ms.
reset_timer(); //Set F=1000 Hz, and reset T=0
for (;;) //the main (infinite) loop
{
data=scan_pond(); //Call some functions
targets=process(data); //process data to find fly
move_towards(targets); //and somehow move towards it
time++; //Keep track of time
if (time%100==0) //every second
eat_fly(); //eat a fly

2
//Now wait till our 10 millisecond period ends (POLL):
while(timer()!=timestamp);

timestamp=timestamp+10; //calculate new timestamp


}
}

So all seems fine for our frog! It lives happily in the pond and eats a rather large number
of flies. However it suddenly drops dead! What could be wrong?
Well, the frog was quite busy waiting for the timer to become 13005 when (exactly)
at T=13003 an interrupt arrived. The interrupt routine took 5 milliseconds and finished
at T=13008! Oh, well, perhaps I should have used a “<=” comparison rather than “!=”.
However, the frog reconsiders, since timer() and timestamp are both “circular”; i.e. they
roll-over from 65535 to 0 it will be difficult to tell which is the largest, time 10 or time
65525!!
Way too complicated for a frog.

1.1 A better way

The frog remembers from its University courses that the hardware timer has got some
other logic associated with it. In particular it has got a modulus comparator, which
resets the timer when a particular count (or equivalently, when a particular length of
time) has ellapsed. Furthermore this action also sets a flag. Could the frog then, not just
set things up and look at the flag? It rapidly looks at the timer implementation of the
HCS08 on pages 153-166 of the MC9s08GB manual and writes:

//HARDWARE TIMER
//FLAG POLLING METHOD
#define TOF 0x80 //Location of flag in TPM2SC
void main(void)
{

int time=0; //Says the frog


TPM2SC=0x0A; //Use Fbus divided by 4 (Fbus=4MHz)
TPM2MOD=10000; //10 ms= 10000*1us
for (;;) //the main (infinite) loop
{
data=scan_pond(); //Call some functions
targets=process(data); //process data to find fly
move_towards(targets); //and somehow move towards it
time++; //Keep track of time
if (time%100==0) //every second
eat_fly(); //eat a fly
//Now wait till our 10 millisecond period ends (POLL):

3
while(TPM2SC & TOF == 0); //by looking at the flag
TPM2SC=TPM2SC & ~TOF; //and reset the flag to 0
}
}

With this code the frog lives a happy but busy life in the pond. If I could only stop
looking at that flag and enjoy Coronation street more often! Grief!

1.2 An Even Greater Solution: Interrupts

So as things stands we have a poor, bored beyond comprehension frog watching (polling)
a flag. It is so busy that it cannot find happines. It spends its winters frozen solid then
thawed during spring, constantly looking at the flag. One day a gardner decapitates
the frozen frog. If it only would have had some free time, it might have found a warm
place to spend winter!
The gardner, who was also an expert programmer, tells the frog that it should have
used interrupts (instead of polling) like this:

//USING INTERRUPTS
#define TOF 0x80 //Location of flag in TPM2SC
volatile int time=0;
void main(void)
{
//startup
time=0; //Says the frog
TPM2SC=0x4A; //Use Fbus divided by 4 (Fbus=4MHz)
//and enable overflow interrupts
TPM2MOD=10000; //10 ms= 10000*1us
//So interrupt will be called every 10ms
for (;;) //the main (infinite) loop
{
watch_coronation_street();
go_to_the_pub();
sleep();
}
}
interrupt 14 void Timer2overflowISR(void)
{
//This interrupt routine is called every time there
//is a timer overflow (i.e. every 10 ms).
// 14 is the number of the Timer 2 overflow vector
TPM2SC=TPM2SC&~TOF; //CLEAR INTERRUPT!
data=scan_pond(); //Call some functions
targets=process(data); //process data to find fly

4
move_towards(targets); //and somehow move towards it
time++; //Keep track of time
if (time%100==0) //every second
eat_fly(); //eat a fly
}
//NOTE that we no longer wait inside this routine at all!
//we just rely on the fact that another interrupt will
//occur at the specified time interval.
//
//WARNING: The importance of clearing the interrupt cannot be
//over-emphazised. This process (which is entirely dependent
//on the specific peripheral/micro used), acknowledges and
//clears the interrupt. If this is not performed, one would
//find itself back into the interrupt handler routine
//immediatly after exiting the routine (forever!)

This way when the timer overflows, it generates an interrupt; which causes our handler
to be executed. In this case that is ever 10ms. Once our handler (i.e. our interrupt
service routine, ISR) finishes executing, the program returns to the normal flow, which
is as it may be, to watch tv and go to the pub.
The frog wonders, am I dead, or will I be so when I am thawed in spring?

Вам также может понравиться