When a software timer is started, the data members state , type , and length are initialized and the timer is inserted into a linked list of active timers called the timerList . The timers in the timer list are ordered so that the first timer to expire is at the top of the list. In addition, each timer has a count associated with it. This value represents the number of ticks that will be remaining in the software timer once all previous timers in the list have expired. Taken together, these design choices favor quick updates to the timer list at the price of slower insertions and deletions. Speed is important during updates because the timer list will be updated every time the hardware generates a clock tick interrupt — that's every one millisecond.
Figure 7-1 shows the timer list in action. Remember that each software timer has its own unique length and starting time, but once it has been inserted into the list, only the count field matters for ordering. In the example shown, the first and second timers were both started (the second might actually have been restarted, because it is periodic) at the same time. Since the second is 5 ms longer, it will expire 5 clock ticks after the first. The second and third timers in the list both happen to expire at the same time, though the third timer will have been running for 10 times longer.
Figure 7-1. The timer list in action
The code for the interrupt service routine is shown below. This routine is declared to be of type void interrupt . The keyword interrupt is an extension of the C/C++ language that is understood only by compilers for 80x86 processors. By declaring the routine in this way, we ask the compiler to save and restore all of the processor's registers at the entry and exit, rather than only those that are saved during an ordinary function call.
/**********************************************************************
*
* Method: Interrupt()
*
* Description: An interrupt handler for the timer hardware.
*
* Notes: This method is declared static, so that we cannot
* inadvertently modify any of the software timers.
*
* Returns: None defined.
*
**********************************************************************/
void interrupt Timer::Interrupt() {
//
// Decrement the active timer's count.
//
timerList.tick();
//
// Acknowledge the timer interrupt.
//
gProcessor.pPCB->intControl.eoi = EOI_NONSPECIFIC;
//
// Clear the Maximum Count bit (to start the next cycle).
//
gProcessor.pPCB->timer[2].control &= ~TIMER_MAXCOUNT;
} /* Interrupt() */
Of course, the tick method of the TimerList class does most of the work here. This method is mostly concerned with linked list manipulation and is not very exciting to look at. Briefly stated, the tick method starts by decrementing the tick count of the timer at the top of the list. If that timer's count has reached zero, it changes the state of the software timer to Done and removes it from the timer list. It also does the same for any timers that are set to expire on the very same tick. These are the ones at the new head of the list that also have a count of zero.
After creating and starting a software timer, the application programmer can do some other processing and then check to see if the timer has expired. The waitfor method is provided for that purpose. This routine will block until the software timer's state is changed to Done by timerList.tick . The implementation of this method is as follows:
/**********************************************************************
*
* Method: waitfor()
*
* Description: Wait for the software timer to finish.
*
* Notes:
*
* Returns: 0 on success, -1 if the timer is not running.
*
**********************************************************************/
int Timer::waitfor() {
if (state != Active) {
return (-1);
}
//
// Wait for the timer to expire.
//
while (state != Done);
//
// Restart or idle the timer, depending on its type.
//
if (type == Periodic) {
state = Active;
timerList.insert(this);
} else {
state = Idle;
}
return (0);
} /* waitfor() */
One important thing to notice about this code is that the test while (state != Done) is not an infinite loop. That's because, as we just learned a few paragraphs back, the timer's state is modified by timerList.tick , which is called from the interrupt service routine. In fact, if we were being careful embedded programmers, we would have declared state as volatile . Doing so would prevent the compiler from incorrectly assuming that the timer's state is either done or not done and optimizing away the while loop. [20] A word of caution about waitfor: this implementation spins its wheels waiting for the software timer to change to the done state. This technique is called busy-waiting, and it is neither elegant nor an efficient use of the processor. In Chapter 8, we'll see how the introduction of an operating system allows us to improve upon this implementation.
The final method of the Timer class is used to cancel a running timer. This is easy to implement because we need only remove the timer from the timer list and change its state to Idle . The code that actually does this is shown here:
/**********************************************************************
*
* Method: cancel()
*
* Description: Stop a running timer.
*
* Notes:
*
* Returns: None defined.
*
**********************************************************************/
void Timer::cancel(void) {
//
// Remove the timer from the timer list.
//
if (state == Active) {
timerList.remove(this);
}
//
// Reset the timer's state.
//
state = Idle;
} /* cancel() */
Of course, there is also a destructor for the Timer class, though I won't show the code here. Suffice it to say that it just checks to see if the software timer is active and, if so, removes it from the timer list. This prevents a periodic timer that has gone out of scope from remaining in the timer list indefinitely and any pointers to the "dead" timer from remaining in the system.
For completeness, it might be nice to add a public method, perhaps called poll , that allows users of the Timer class to test the state of a software timer without blocking. In the interest of space, I have left this out of my implementation, but it would be easy to add such a routine. It need only return the current value of the comparison state == Done . However, in order to do this, some technique would need to be devised to restart periodic timers for which waitfor is never called.
Читать дальше