Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Interfacing with C plus plus-programing communication with microcontrolers (K. Bentley, 2006)

.pdf
Скачиваний:
192
Добавлен:
12.08.2013
Размер:
3.18 Mб
Скачать

408 13 THE PC TIMER

7

6

5

4

3

2

1

0

 

 

 

 

 

 

 

 

0

1

0

0

0

0

0

1

0

1

0

0

1

1

1

0

0

1

0

1

0 0

01

10

11

0

1

0

1

Meaning

Binary counting BCD Counting Mode 0 selected Mode 1 selected Mode 2 selected Mode 3 selected Mode 4 selected Mode 5 selected undefined

Read/Load status – MSB only Read/Load status – LSB only Read/Load Status – LSB first, then MSB New initial count loading finished

New initial count loading in progress Timer output signal status – LOW Timer output signal status – HIGH

Figure 13-2 Description of the returned counter status byte.

13.3 Programming the Timer

Programs will be developed in the coming sections that take advantage of the PC timer’s accurate timing. We will start by developing an object class for the PC timer. Note that although the timer hardware is independent of the parallel port hardware, the object classes we created in the preceding chapters can be used with the new timer object class developed in this chapter.

Programs have been developed to accurately measure time periods, measure a persons reflexes, and to generate a time-base for an earlier program that displayed the VCO output on-screen (see Chapter 10). The final program digitises an electrical waveform generated on the interface board, and saves the digitised values for later analysis.

As explained previously, the PC’s three timers are connected to a clock signal having a frequency of 1.1932 MHz. Their counters can be loaded with a maximum value of 65,535 to produce a maximum time of 54.9 ms per countdown cycle. If we are to measure times greater than 54.9 ms, we must monitor and account for the number of down-counts. Each countdown of Timer 0 is referred to as a ‘tick’. A special region of memory known as BIOS is used to store the number of ticks since mid-night. BIOS is the abbreviation for the PC’s Basic Input Output System. It is a segment of software that predominantly interacts with hardware devices to carry

13 THE PC TIMER 409

out low-level tasks. Like many operating systems, the PC’s operating system suffers from poor determinism (determinism is the term used to describe the ability of software to carry out tasks on time), and other latencies (time delays when actioning requests – some generated by hardware). As a result of these latencies, the tick count does not always get updated immediately, and so the BIOS tick count cannot be used reliably. We need to devise a reliable method to monitor and account for the number of tick counts so we can use this value in conjunction with the timer count value to measure time periods greater than one full timer countdown cycle. We will use Timer 0 for our time reference since it operates in a suitable mode.

13.3.1 Reading Timer 0 Count Value and Ticks

To read the number of counts corresponding to a given instant in time, we must read the timer ticks and also the contents of the Timer 0 counter. Section 13.2.4 describes how to latch the count value of Timer 0 (a snap shot taken) by issuing a read-back command. The latched data can be read later by reading the output register of Timer 0.

Before starting to program the timers, it is important to be aware that we will not adhere to the strict practice of structured procedure abstraction (use of functions). This will allow us to produce programs that execute fast and provide satisfactory accuracy when reading the timer. Additional time would be consumed if we called a function within one of the member functions of our PCTimer class.

13.4 The Object Class PCTimer

We will develop a class to provide a means of measuring time and generating time delays. To do this the class needs functions that perform the following tasks:

ξ

Set a ‘zero’ time reference (this does not mean clearing the hardware timer

 

in the PC).

ξ

Generate a time delay of a specified value.

ξ

Read the current time.

The definition of the PCTimer class that has the above capabilities is shown in Listing 13-1.

Listing 13-1 The PCTimer class - pctimer.h.

#ifndef PctimerH #define PctimerH

class PCTimer

{

410 13 THE PC TIMER

private:

unsigned int InitCount; unsigned long TickCount; unsigned int LastCount;

public:

PCTimer();

void ResetTimer();

void Delay(const double& milliseconds); double ReadTimer();

void UpdateTicks();

};

#endif

Setting ‘Zero’ Time

The basic requirement when making use of time is to have a reference of ‘zero’ time. The data member InitCount is used to store the count value (remaining before the next tick) that corresponds to this ‘zero time’ as shown in Figure 13-4. This value of InitCount is established at the time of instantiating the PCTimer object, and can also be re-established by calling the member function

ResetTimer().

The ResetTimer() function can be used at any time to create a ‘zero time’ reference. It latches and reads Timer 0, then stores the count value into the data members InitCount and LastCount. LastCount is to temporarily store the timer’s last count value so the other member functions can detect the next tick as described in the following text. The number of ticks that have occurred, stored in data member TickCount, is then reset to zero.

Accounting for Timer Ticks

As explained previously, to determine the exact time that corresponds to any given instant, we need to know the number of timer ticks elapsed since the ‘zero’ time reference was created using Timer 0. We must monitor each countdown of Timer 0 to prevent any ticks being missed. The three member functions; Delay(), ReadTimer(), and UpdateTicks(), all need to track and record the number of ticks that take place during their operation. Figure 13-3 shows three different scenarios when monitoring timer ticks, explained as follows.

The counter is first read and its count value stored in the member variable LastCount. A short time later during execution of the program, the counter is read again to test if a tick has passed, and this count value is stored in the variable Count. Case (a) shows the two reads of the counter occurring within a countdown cycle – no tick has occurred. In this case the variable Count is not greater than the variable LastCount. Case (b) shows a new countdown cycle underway when the counter is read for the second time. In this case a tick has occurred; Count is greater than LastCount. Case (c) shares the same result as case (a) in that

13 THE PC TIMER 411

Count is not greater than the variable LastCount. When using the same criteria to test for a tick as used for case (a), we will make an incorrect evaluation that a tick has not occurred because the two reads of the counter in case (c) are made more than a full countdown period apart (and Count < LastCount ).

65,535

65,535

Count

 

 

LastCount

Count

 

 

LastCount

 

 

 

 

 

 

 

 

0

 

 

 

 

 

 

 

 

 

 

 

0

 

 

 

 

 

0

 

Time

0

 

 

Time

(a) Tick has not occurred

(b) Tick has occurred

 

Count is not ! LastCount

 

 

Count ! LastCount

65,535 LastCount

 

Count

0

 

0

Time

(c)

Tick is missed

Read period ! 1 countdown (54.9 ms)

Figure 13-3 Accounting for timer ticks.

Evaluating Elapsed Time

At the start of the timing process, the counter is latched, read, and its value is stored in the data member InitCount. This value represents ‘zero’ time, and is the number of counts remaining before the next tick as shown in Figure 13-4.

Tick occurred

Tick occurred

Tick occurred

Tick occurred

(0

65,535)

(0

65,535)

(0

65,535)

(0

65,535)

65,535

 

 

 

65,535

Count

 

 

 

 

 

 

 

 

 

InitCount, LastCount

0

‘zero time’

0

Current Time

0

0

 

 

 

Time

Elapsed Time

Figure 13-4 Evaluation of the elapsed time.

At the end of the ‘Elapsed Time’ the timer is read again and its value stored in the variable Count. The number of timer counts since the last tick can be evaluated by subtracting Count from 65,535 (i.e. 65,535 – Count). The number of tick counts within the elapsed time is stored in TickCount. Therefore, the total counts corresponding to the elapsed time is:

TotalCounts = InitCount + (TickCount-1)*65535 + 65535–Count;

Which can be written as:

412 13 THE PC TIMER

TotalCounts = InitCount + TickCount*65535 – Count;

This is then easily converted into a value of time by knowing the frequency of the PC’s clock that drives the counter.

The purpose and operation of the member functions of this class are explained as follows.

The PCTimer() function shown in Listing 13-2 is the constructor for the PCTimer object class. The constructor allocates space for all data members of the class and then takes an initial reading of the timer for the ‘zero time’ reference. Obtaining the ‘zero time’ reference is performed within the body of the constructor by using the function ResetTimer().

Listing 13-2 The constructor of the PCTimer class.

PCTimer::PCTimer()

{

ResetTimer();

}

The ReadTimer() function determines the total number of counts since zero time until the instant ReadTimer() is called. The total number of counts is calculated as described above. Finally, the total count value is converted into a value of time in milliseconds and a value of type double is returned. The ReadTimer() function does not call another member function to avoid added delays. Instead, all the code it needs is placed within its body.

The Delay() function is used to generate a specified delay that is passed to it as a parameter in milliseconds. When called, it reads the timer once to determine the start of the delay period, being the current number of counts since zero time. Then it adds the number of counts since zero time to the number of counts corresponding to the specified delay. The timer is then read repeatedly until the number of counts since zero time is equal to the total number of counts evaluated above. At this point in time the delay will expire.

The UpdateTicks() function simply performs the task of quickly monitoring the tick status during down-counting (faster than the ReadTimer() function). It does this using the same method as employed in the other member functions of the PCTimer class. For the proper functioning of the PCTimer class, the Update() function must be called at least once within a countdown, preferably every 40 ms. This completes the development of our PCTimer object class. The function file pctimer.cpp that contains these functions is given in Listing 13-3.

Listing 13-3 Member functions of the PCTimer class - pctimer.cpp.

#include <dos.h>

13 THE PC TIMER 413

#include "pctimer.h"

PCTimer::PCTimer()

{

ResetTimer();

}

void PCTimer::ResetTimer()

{

//Latch Timer 0 outportb(0x43, 0xD2);

//Read latched Count

LastCount = InitCount = inportb(0x40)+inportb(0x40)*256; TickCount = 0; // Initialise to zero.

}

double PCTimer::ReadTimer()

{

double Time;

unsigned long TotalCounts; unsigned int Count;

//Latch Timer 0. outportb(0x43, 0xD2);

//Read latched Count

Count = inportb(0x40) + inportb(0x40)*256;

if(Count > LastCount) TickCount++;

LastCount = Count;

TotalCounts = ((long)InitCount + TickCount*65535L -(long)Count);

Time = (TotalCounts/1.1932)/1000.0; return Time; // In milliseconds.

// return TotalCounts;

}

void PCTimer::Delay(const double& milliseconds)

{

unsigned int Count;

long StartCount, DelayCount, EndCount, TotalCount;

41413 THE PC TIMER

//Latch Timer 0. outportb(0x43, 0xD2);

//Read latched CountOne.

Count = inportb(0x40) + inportb(0x40)*256;

if(Count > LastCount) TickCount++;

LastCount = Count;

StartCount = ((long)InitCount + TickCount*65535L

-(long) Count);

DelayCount = (long) (milliseconds*1.1932*1000);

EndCount = StartCount + DelayCount;

//Repeat a loop for the duration of the period. do

{

//Latch Timer 0. outportb(0x43, 0xD2);

//Read latched CountOne.

Count = inportb(0x40) + inportb(0x40)*256;

if(Count > LastCount) TickCount++;

LastCount = Count;

TotalCount = ((long)InitCount + TickCount*65535L -(long)Count);

}

while (TotalCount < EndCount);

}

void PCTimer::UpdateTicks()

{

unsigned int Count;

//Latch Timer 0. outportb(0x43, 0xD2);

//Read latched Count.

Count = inportb(0x40) + inportb(0x40)*256;

13 THE PC TIMER 415

if(Count > LastCount) TickCount++;

LastCount = Count;

}

As mentioned previously, it is essential that when using any of the member functions of the class that they are called within a countdown cycle. The class will function best if the PC’s interrupts are disabled, however, we have chosen to leave all interrupts active to avoid unnecessary complexity in our programs. The effect of these interrupts may cause small and unforeseen time delays.

For example; An attempt is made to read the timer at a particular instant using the ReadTimer() function. If the timer interrupt also occurs at this time, it will force the function ReadTimer() to wait until the timer interrupt has been serviced. This delay will cause ReadTimer() to return a count value that is greater than the instantaneous value when the call to the ReadTimer() function was initiated. The effect of interrupts is demonstrated in one of the programs that uses a member function of the PCTimer object.

NOTE

Although updating the tick count is handled automatically by the Delay() function while it is active, the ReadTimer() and UpdateTicks() functions will only perform this task at the instant they are called. Therefore, if time

periods need to be measured, ensure that functions ReadTimer() and

UpdateTicks() are called repetitively within a full timer countdown period

(54.9 ms) to correctly monitor and update the tick count value.

13.5 Measurement of Time

The PCTimer object class will be used to demonstrate the measurement of realtime. As explained earlier, measurement of time periods can be affected by the execution of interrupt routines. The following program will allow us to observe the delays that can be generated by the various interrupt service routines executing in the PC. The disable() function can be called to stop all interrupts (disabling most of the PC’s peripherals, including the keyboard). Therefore, make sure that the interrupts are disabled for a minimum length of time! Calling the enable() function re-enables the interrupts and allows the PC’s peripherals to resume operation. The program that measures time is shown in Listing 13-4.

416 13 THE PC TIMER

Listing 13-4 Measurement of time – time.cpp.

#include <iomanip.h> #include <math.h> #include <iostream.h> #include <conio.h> #include <dos.h>

#include "pctimer.h"

main()

{

PCTimer T;

double TimeValue[1000]; int i;

//disable();

for(i = 0; i < 1000; i++)

{

for(int j = 0; j < 50; j++) sin(j);

TimeValue[i] = T.ReadTimer();

}

enable();

for(i = 1; i < 1000; i++)

{

cout << i << '\t';

cout << setprecision(3) << TimeValue[i] << '\t';

cout << setprecision(3) << (TimeValue[i] - TimeValue[i-1])<< '\t';

cout << endl;

if( i % 20 == 0)

{

cout << "Press a key for more ... "; getch();

cout << endl;

}

}

return 0;

}

13 THE PC TIMER 417

Executable File Generation

 

Required Files

 

Listing No.

 

Project File Contents

 

 

pctimer.cpp

 

Listing 13-3

 

pctimer.cpp

 

 

 

 

 

 

pctimer.h

 

Listing 13-1

 

time.cpp

 

 

time.cpp

 

Listing 13-4

 

 

 

The program measures the time consumed by each iteration of a for loop that executes 1000 times. Within this for loop is another for loop that repeatedly executes the sin() function 50 times for no real purpose except to waste time. You can alter the number of times the sin() function executes to change the time spent by each iteration of the external for loop. The ReadTimer() function is called within each iteration to read the time and store the time values in an array. Note that the UpdateTicks() function does not need to be called since the ReadTimer() function monitors and accounts for timer ticks, and importantly, the for loop will execute in less than a full countdown period.

The time value and also the time difference between two consecutive readings of the timer are displayed on-screen 20 lines at a time. Interrupts will have the effect of adding time delays of varying value to the time taken to perform the calculations. Therefore, if interrupts are active (not disabled), calculations will take different periods of time, and it is these differences that the program is displaying. If the interrupts are disabled, calculation times will be uniform. Therefore, any irregularities in the times displayed in the third column will be caused by interrupts. You can run the program twice, once with the interrupts disabled and then with the interrupts enabled to observe these effects. These results should provide some insight into the effect of interrupts when measuring time.

13.6 Reflex Measurement

In this section we will use the interface board to measure a person’s hand reflexes. A program has been provided that will light up a set of LEDs on the interface board after a random time delay. The person under test will react and press the button switch on the board in response to the LEDs lighting up. The delay in time from the LEDs lighting up and the press of the button switch is a measure of a person’s reflexes. Listing 13-5 shows the program that performs the reflex measurement.

Listing 13-5 Reflex measurement – reflex.cpp.

#include <iomanip.h> #include <conio.h> #include <iostream.h> #include <stdlib.h>