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

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

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

306 10 VOLTAGE AND TEMPERATURE MEASUREMENT

Output

Voltage

High

Low

Low

Hi

Temperature

Figure 10-3 Typical curve – voltage divider output vs temperature (NTC thermistor).

The output voltage of this circuit will drop as the temperature increases for negative coefficient thermistors as shown in Figure 10-3. The shape of this curve can be brought closest to a straight line when the value of the bias resistor in the voltage divider circuit is chosen to equal the resistance of the thermistor approximately midway through its temperature range. The circuit output may need to be buffered if a significant level of current must be supplied to the electronics that senses this output voltage (in our case the VCO). Note that time is required for the body of the thermistor to reach the temperature of the object or medium it is placed into contact with. Also, the temperature of its body can be adversely affected if excessive current is passed through the device.

10.4 The Object Class VCO

The output voltage from the thermistor voltage divider circuit is connected to the VCO input pin. This voltage determines the output frequency (and hence the period) of the VCO’s pulse-train. The higher the voltage that is applied to the input of the VCO, the higher the frequency of the pulse-train. If we develop a method to measure the period of the pulse-train (i.e. the time to complete one cycle) we can evaluate the frequency. As expected we will be using the parallel port of the PC to interface the VCO output to the computer.

We can develop a new object to provide for future use of the VCO in other program applications. This object will need to process the pulse-train signal received at the parallel port and measure its period by detecting its signal level. By signal level, we mean the logic-high or logic-low status of the signal at any given instant. To develop this new object we can use the class ParallelPort as the base class. The class definition for the new VCO class is given in Listing 10-1.

10 VOLTAGE AND TEMPERATURE MEASUREMENT 307

Listing 10-1 The class definition for the VCO class, file – vco.h.

#ifndef VcoH #define VcoH

#include "pport.h"

enum BITNUMBER{Bit7=0x80,Bit6=0x40,Bit5=0x20, Bit4=0x10,Bit3=0x08};

class VCO : public ParallelPort

{

private:

long int Period; BITNUMBER Bit;

public:

VCO(int baseaddress = 0x378, BITNUMBER bit=Bit3); long int MeasurePeriod();

long int GetPeriod(); int SignalLevel(); virtual ~VCO(){}

};

#endif

The VCO class has two data members and five member functions. The data member Period will store the measured period. We will use the port at address BASE+1 to read the VCO output into the computer via the parallel port. Note that the VCO output can be connected to any bit number between bit 3 and bit 7 inclusive of the port at address BASE+1. To provide the user with the flexibility to connect the VCO output to any of these bits, we pass a parameter to the constructor that specifies the bit number. The enumerated data type BITNUMBER is created for this purpose, and the enumerated identifiers are assigned integer values. The use of these values will be understood once we define all the member functions. We have used Bit3 as the default bit that will interface to the VCO output.

A strategy must be developed to measure the period of the pulse-train before defining the member functions of the VCO class. We can monitor the logic level of the VCO output by reading the port at address BASE+1 and then filtering out all unwanted bits. We can recognise the start of a pulse by detecting a transition in signal level - from low to high, or from high to low. After the transition we can begin measuring the pulse period. The signal must undergo two more transitions to complete one cycle as shown in Figure 10-4.

308 10 VOLTAGE AND TEMPERATURE MEASUREMENT

Start of Cycle

 

End of Cycle

Bit 3 of BASE+1

 

 

 

 

 

 

 

t

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1st transition

 

3rd transition

2nd transition

Figure 10-4 Detection of one complete cycle.

Since the signal level plays an important role, let us first establish the member function SignalLevel().

Listing 10-2 The member function SignalLevel().

int VCO::SignalLevel()

{

if(ReadPort1() & Bit) return 1;

else

return 0;

}

The signal level will either be logic-low or logic-high at any given time. To signify these two states we can use 0 and 1 respectively. The function SignalLevel() shown in Listing 10-2, returns 0 or 1 depending on the logic level of the VCO output.

First the inherited member function ReadPort1() is used to read the port at address BASE+1. An AND operation with Bit is used to filter out all bits except the bit we have allocated for use with the VCO output. If the signal is logic-high, the AND result will be equal to Bit which is non-zero, the if condition will evaluate to a non-zero value and 1 will be returned. However, if the logic level of the signal is low, the result of the AND operation will be zero and a 0 is returned.

A signal transition can be detected if we read the port continuously, checking for a change in the logic level of the bit used. Measurement of the period begins as the first transition is detected and will be complete once the next two signal level transitions have been detected. The ideal means to measure the period of the pulsetrain is to use real-time techniques, however, this topic is yet to be covered (in Chapter 13). Instead, we will use software loops to measure the pulse-train period by counting the number of times the port is read within a pulse-train cycle. It may be possible to evaluate the period as an actual time value if the time for one port

10 VOLTAGE AND TEMPERATURE MEASUREMENT 309

read and filter operation was known. However, program execution times will vary for different computers, and are also affected by intervening system events.

The steps required to measure the period are as follows (assume that the signal is connected to bit 3):

1.Initialise a counter variable to zero.

2.Repeatedly read the signal level of bit 3 until a change is detected.

3.Repeatedly read the signal level of bit 3 until the second change is detected while incrementing the counter after each read.

4.Repeatedly read the signal level of bit 3 until the third change is detected while incrementing the counter after each read.

5.Return the counter value as the period.

These steps (the pseudo code) can now be converted to form the function MeasurePeriod(). Examining the pseudo-code; steps 2, 3 and 4 are very similar. We will start by developing code for step 2. It is then possible for us to use the same code for step 3 and 4, and add the incrementing of the counter after each read.

Step 2 of the pseudo-code can be implemented as shown in Listing 10-3. SignalLevel() uses the inherited function ReadPort1() of the class

ParallelPort.

Listing 10-3 Detecting a signal transition in the VCO output.

VCO Vco;

unsigned char Signal;

Signal = Vco.SignalLevel(); while(Signal == Vco.SignalLevel());

First, the variable Signal is used to store the current signal level by calling the member function SignalLevel(). Then the program enters a while loop, where the signal level is repeatedly read by calling the SignalLevel() function and its result compared with the previously stored value of the signal level. While they are equal, the while loop will continue to execute. Note that the body of the while loop is empty - containing only a semi-colon. When a change in signal level is detected, the while condition will evaluate to false and the while loop will terminate.

Definitions for all the member functions of the VCO class are given in Listing 10-4. The function MeasurePeriod() determines the period of the signal being measured and returns this value. The GetPeriod() function merely accesses and returns the value of the private data member Period. The development of the VCO class is now complete. In the next section we will learn how to use VCO objects in a program that will measure voltages.

310 10 VOLTAGE AND TEMPERATURE MEASUREMENT

Listing 10-4 Member function definitions of the VCO class – vco.cpp.

#include "vco.h"

VCO::VCO(int baseaddress, BITNUMBER bit) :ParallelPort(baseaddress)

{

Bit = bit; Period = 0;

}

long int VCO::MeasurePeriod()

{

unsigned char Signal; Period = 0;

Signal = SignalLevel(); while(Signal== SignalLevel())

; // empty body

Signal = SignalLevel(); while(Signal== SignalLevel())

Period++;

Signal = SignalLevel(); while(Signal== SignalLevel())

Period++;

return Period;

}

long int VCO::GetPeriod()

{

return Period;

}

int VCO::SignalLevel()

{

if(ReadPort1() & Bit) return 1;

else

return 0;

}

10 VOLTAGE AND TEMPERATURE MEASUREMENT 311

10.5 Measuring Voltages Using the VCO

As described in section 10.2, the voltage-controlled oscillator (VCO) is an electronic circuit that generates a square wave at a frequency proportional to the analog voltage applied to its input. Various voltage levels need to be applied to its input to verify its proper operation. The easiest way to do this is to connect the output of the interface board’s potentiometer to the VCO input. We can measure the potentiometer output voltage with a voltmeter to establish a quantifiable relationship between the VCO output and the voltage applied to the VCO input. A different approach which eliminates the need for a voltmeter, is to use the output of the DAC circuit to generate a known analog voltage. We can easily control the DAC output by writing a number to it as explained in Chapter 6.

The following keyboard controls will be implemented within the main() function for easy use of the program:

ξ

Pressing the up arrow will increase the output voltage of the DAC.

ξ

Pressing the down arrow will decrease the output voltage of the DAC.

ξ

Pressing Alt-X will exit the program.

A fragment of skeleton code is given in Listing 10-5 to implement the above steps.

Listing 10-5 Implementing keyboard control.

int Quit = 0, key;

while(!Quit)

{

// Insert lines to measure and display the pulse period

if(bioskey(1)!=0) // check if a key is pressed

{

key = bioskey(0); // read key code

 

switch(key)

/* Alt-X */

{

case 0x2d00 : Quit = 1;

 

break;

/* Up Arrow */ case 0x4800 : //Increase DAC output break;

/* Down Arrow */ case 0x5000 : //Decrease DAC output ; // Empty statement

}

}

}

312 10 VOLTAGE AND TEMPERATURE MEASUREMENT

The while loop will eventually contain code to measure and display the pulse period. This code is not shown yet - instead, a comment is included to that effect. The next statement in the while loop is an if statement that contains a switch statement. Program control will be transferred to the true clause of the if statement when the if condition evaluates to true - when a key is pressed. The bioskey() function (when passed an actual argument of 1) will return true if a key has been pressed. If no key is pressed it will return false. The bioskey() function will not wait for a key press. This means that if a key has not been pressed, the body of the if statement will be skipped, and the other statements of the while loop (such as those used to measure the pulse period and display it) will continue to execute.

If a key has been pressed, program control will be transferred to the body of the if statement where its true clause will be executed. Within the true clause, the first action to perform is to determine the actual key pressed. The bioskey() function can be used for this purpose, although to do this it must be passed an actual argument of 0. The bioskey() function will now retrieve the key code of the key pressed. The three key codes corresponding to the keys; up arrow, down arrow, and Alt-X are listed as cases of the switch statement. Within the switch statement; if the key code of the pressed key matches one of the cases, program control will be transferred to that case. If no matching cases are found, no action will be taken. In the case of Alt-X, the case statement sets the variable Quit to 1. This will cause the while loop to terminate the next time the while condition is evaluated. Programming statements for the two cases corresponding to the up arrow and the down arrow are not included yet. Instead, comments are used in their place.

A main() function that sends the DAC output to the VCO and measures the pulse period of the VCO output is given in Listing 10-6. The comment lines in Listing 10-5 have now been replaced by actual programming statements. Note that the program is written to jointly operate the VCO and the DAC. Therefore, a DAC object named Dac and a VCO object named Vco have been instantiated at the start of the main() function.

Listing 10-6 Measuring VCO pulse period (DAC driving VCO input) – period.cpp.

#include <bios.h> #include <conio.h>

#include "dac.h" #include "vco.h"

void main()

{

DAC Dac;

10 VOLTAGE AND TEMPERATURE MEASUREMENT 313

VCO Vco;

int Quit = 0, key; unsigned char DACbyte;

clrscr();

Dac.SendData(0); // initialise to zero

while(!Quit)

{

gotoxy(10,10);

cprintf("The pulse period is %10lu\a", Vco.MeasurePeriod()/1000);

if(bioskey(1)!=0)

{

DACbyte = Dac.GetLastOutput(); key = bioskey(0);

switch(key)

{

case 0x2d00 : Quit = 1;

/*Alt-X*/

/*Up Arrow*/

break;

case 0x4800:

 

if(DACbyte>247) // limit max value

 

DACbyte = 247;

 

Dac.SendData(DACbyte+8);

 

break;

/*Down Arrow*/ case 0x5000 :

if(DACbyte<8) // limit min value DACbyte = 8;

Dac.SendData(DACbyte-8);

}

}

}

}

The gotoxy()function locates the cursor at screen coordinates (10,10). The first number within the above pair of parentheses is referred to as the ‘X coordinate’ and is measured from the left edge of the screen. The second number is referred to as the ‘Y coordinate’ and is measured from the top edge of the screen. Screen coordinates are shown in Figure 10-5. Therefore, the measured pulse period will be displayed starting at screen coordinates (10,10). The function cprintf()is similar to the printf() function we saw previously, with the exception that it does not convert the new line character combination (\n) to a new line and carriage return combination (\n\r). It is especially designed to send output to the

314 10 VOLTAGE AND TEMPERATURE MEASUREMENT

screen. In general, gotoxy() is used to set the position of the cursor, and therefore there is no need for a line feed or carriage return.

The measured value of the pulse period is a ‘representation’ of the square wave period. This value is obtained by using the member function MeasurePeriod() of the Vco object. We use the cprintf() function to call the MeasurePeriod() function. The cprintf() function prints the measured value divided by 1000 on the screen.

The member function GetLastOutput() of the DAC class is called to obtain the previous value output to the DAC. This value is then used within the switch statement block to ensure that the byte being sent to the DAC is kept within its operating range of 0 to 255 for each press of the up or down arrow key. The two cases corresponding to the up arrow and the down arrow have been implemented using the SendData() function of the DAC class. Depending whether the up or down arrow key has been pressed, the value sent to the DAC is either incremented or decremented by 8. During execution of the SendData() function, the data member LastOutput of the DAC class is updated to store the value just output.

Origin (1,1)

Positive y

Positive x

Figure 10-5 Screen coordinates in text mode.

Three code modules are required to generate an executable program for the code segment shown in Listing 10-6. These are the ParallelPort, VCO, and DAC modules. A project file (or make file) must be formed to compile all modules and link them together to form the executable file. The VCO module consists of the header file given in Listing 10-1 and the function file given in Listing 10-4. The ParallelPort class header file and its function file were formed in Section 9.4 and are repeated in Listing 10-7 and Listing 10-8, respectively.

10 VOLTAGE AND TEMPERATURE MEASUREMENT 315

Listing 10-7 Header file for the ParallelPort class - pport.h.

#ifndef PportH #define PportH

class ParallelPort

{

private:

unsigned int BaseAddress; unsigned char InDataPort1;

public:

ParallelPort(); ParallelPort(int baseaddress);

void WritePort0(unsigned char data); void WritePort2(unsigned char data); unsigned char ReadPort1();

virtual ~ParallelPort(){}

};

#endif

Listing 10-8 Function file for the ParallelPort class - pport.cpp.

#include <dos.h> #include "pport.h"

ParallelPort::ParallelPort()

{

BaseAddress = 0x378; InDataPort1 = 0;

}

ParallelPort::ParallelPort(int baseaddress)

{

BaseAddress = baseaddress; InDataPort1 = 0;

}

void ParallelPort::WritePort0(unsigned char data)

{

outportb(BaseAddress,data);

}

void ParallelPort::WritePort2(unsigned char data)