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

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

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

11 ANALOG-TO-DIGITAL CONVERSION 357

ADC Adc;

int Quit = 0, key; unsigned char DACbyte;

clrscr();

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

while(!Quit)

{

gotoxy(10,10);

cprintf("The ADC output is: %3u",

(int) Adc.ADConvert());

if(bioskey(1)!=0)

{

DACbyte = Dac.GetLastOutput();

key = bioskey(0);

switch(key)

{

case 0x2d00:

/*Alt-X*/

 

Quit = 1;

 

Dac.SendData(0); // reset to 0

 

break;

/*Up Arrow*/

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);

}

}

}

}

We have made changes to just a few statements in the main function. The keyboard controls operate almost identically to the program in Listing 10-6 that used the VCO object. The difference being the addition of statements to limit the maximum and minimum value of the number written to the DAC (0 to 255).

358 11 ANALOG-TO-DIGITAL CONVERSION

Executable File Generation

 

Required Files

 

Listing No.

 

Project File Contents

 

 

pport.cpp

 

Listing 10-8

 

pport.cpp

 

 

 

 

 

 

pport.h

 

Listing 10-7

 

 

 

 

dac.cpp

Listing 10-10

dac.cpp

 

dac.h

Listing 10-9

 

 

 

adc.cpp

Listing 11-4

adc.cpp

 

 

adc.h

Listing 11-1

 

 

 

 

voltage.cpp

Listing 11-5

voltage.cpp

 

The entire definition of the DAC class must be provided before the compiler can compile the main() function given in Listing 11-5. The VCO object is not being used. Therefore, its class definition and function definition can be eliminated, however, its inclusion will not affect the operation of our program. When compiled, linked and run, this program will display an integer value on the screen that corresponds to the voltage applied at the VIN input of the ADC.

The program can be modified slightly to display the analog voltage instead of an integer number. The ADC produces an output that is 8 bits wide. These 8 bits can represent any value in the range 0–255 both inclusive (which forms 256 numbers). The operation of the ADC requires the full-scale range to be quantised (segmented) into 256 equal quantum levels. Each quantum then represents the full-scale voltage (5V) divided by 256. The ADC’s integer output of 0 corresponds to 0 volts at VIN, and its integer output value of i corresponds to an applied voltage of:

volts

Note that from a C++ program’s point of view, the division operation 5/256 is considered as an integer division and the result will be 0. Therefore, when including the above expression in the program, it must be typed in as:

5.0/256.0*i

Now the compiler will treat the division and multiplication operations as floating point operations, and a non-zero result will be evaluated for the expression 5.0/256.0. The statement containing the cprintf() function in Listing 11-5 must now be modified to include the above factor as shown below:

cprintf("The ADC output is: %5.2f (V)", 5.0/256.0*Adc.ADConvert());

The program will now display the actual voltage applied at the analog input pin of the ADC (VIN). The connections that need to be made for this program to operate are given in Figure 11-19, Table 11-4 and Table 11-5.

11 ANALOG-TO-DIGITAL CONVERSION 359

NOTE

Ensure that the DAC is placed into unipolar mode (0 to +5V output) by fitting the

jumper across the header in the position marked as LINK1. Then connect the 9V battery before connecting the output of the DAC circuit to the ADC input.

Table 11-5 Partial connections for the ADC.

 

ADC0804 (U8)

VDAC (pin 7, U10B)

VIN

GND

/READ

GND

/CS

 

 

11.7 Measuring Temperature Using the

ADC

A program was developed in Chapter 10 to measure temperature using the interface board’s thermistor and VCO. That program (Listing 10-12) only requires minor changes to measure temperature using the ADC. This new modified program is shown in Listing 11-6. Note that in this case we can accurately characterise the thermistor circuit response over the full 0 to +5V range since the ADC has very good linearity over its entire input range. In comparison, the VCO has similar linearity between +2.2V and +2.8V.

Listing 11-6 Program measures temperature using ADC and thermistor – temp.cpp.

/***************************************************** This program uses the thermistor circuit on the interface board to generate the analog input voltage to the ADC. The

byte produced by the ADC will be proportional to the applied voltage (temperature of the thermistor).

The program also calibrates the thermistor circuit output using upper and lower temperature points. The calibration equation will then interpolate a straight line through these two points. Once calibrated, the program will be able to display actual temperatures.

*****************************************************/

#include <bios.h>

360 11 ANALOG-TO-DIGITAL CONVERSION

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

#include "adc.h"

void main()

{

ADC Adc;

int Quit=0, HiFlag = 0, LoFlag = 0; int key = 0;

float HiTemp, LoTemp, Temp; long int HiCount, LoCount;

clrscr();

while(!Quit)

{

Adc.ADConvert();

gotoxy(10,10);

if((HiFlag == 1) && (LoFlag == 1))

{

Temp = LoTemp+(HiTemp-LoTemp)* (Adc.GetADCValue()-LoCount)/(HiCount-LoCount);

cprintf("The temperature is: %6.1f (deg)", Temp);

}

else

cprintf("The ADC Value is: %3u",

(int)Adc.GetADCValue());

if(bioskey(1)!=0)

{

key = bioskey(0);

switch(key)

{

case 0x2d00 : /* Alt-X */ Quit = 1;

break;

case 0x4800 : /* Up Arrow */ gotoxy(10,5);

cout << "Enter Upper Calibration Temp."; cin >> HiTemp;

11 ANALOG-TO-DIGITAL CONVERSION 361

HiCount = Adc.GetADCValue();

HiFlag = 1; break;

case 0x5000 : /* Down Arrow */ gotoxy(10,6);

cout << "Enter Lower Calibration Temp."; cin >> LoTemp;

LoCount = Adc.GetADCValue();

LoFlag = 1;

}

}

}

}

Executable File Generation

 

Required Files

Listing No.

Project File Contents

 

 

pport.cpp

 

 

 

 

Listing 10-8

pport.cpp

 

 

pport.h

Listing 10-7

adc.cpp

 

 

adc.cpp

Listing 11-4

 

adc.h

Listing 11-1

 

 

temp.cpp

Listing 11-6

temp.cpp

 

The wiring connections need to be changed slightly for this program to operate. We do not use the DAC to provide the analog voltage. Instead we use the thermistor circuit to generate a voltage that represents the temperature of the thermistor. The output of the thermistor circuit is connected to the ADC analog input (VIN) as given in Table 11-6. The remaining connections for the ADC and the MUX are shown in Figure 11-19.

Table 11-6 Thermistor circuit connections to the ADC.

 

ADC 0804

Thermistor Circuit

(U8)

VTH

VIN

 

/READ (to GND)

 

/CS (to GND)

It has been rather easy for us to change our program that used the VCO to now operate in conjunction with the ADC. We have been careful to be consistent in developing our classes so that minimum changes will be needed if they are later

362 11 ANALOG-TO-DIGITAL CONVERSION

modified for new or existing programs. These examples are typical of the ease with which object-oriented programs can be maintained and upgraded.

11.8 Summary

This chapter described the principles of operation and use of an analog-to-digital converter. The more popular types of analog-to-digital converters and their various methods of conversion have been explained. This was followed by a discussion of the importance of a sample and hold circuit and the effects of aliasing that occurs when signals are sampled too slowly.

We used our now familiar object-oriented approach to develop software for interfacing the parallel port of the PC with the ADC. We developed a new object class named ADC using an approach consistent with that of Chapter 10 when the VCO class was developed. This object-oriented approach has allowed us to develop the voltage and temperature measuring programs that used the ADC by making minor changes to the programs written for the VCO.

11.9 Bibliography

Fluke, The ABC’s of Oscilloscopes, Fluke Corporation, 1997.

Horowitz, P. and Hill, W., The Art of Electronics, Cambridge University Press, Cambridge, 1989.

Loveday, G., Microprocessor Sourcebook, Pitman Publishing Limited, London, 1986.

NS DATA CONVERSION/ACQUISITION Databook, National Semiconductor Corporation, 1984.

Stiffler, K., Design with Microprocessors for Mechanical Engineers, McGrawHill, 1992.

Webb, R.E., Electronics for Scientists, Ellis Horwood, New York, 1990. Wobschall, D., Circuit Design for Electronic Instrumentation, McGraw-Hill, 1987. Van Gilluwe, F., The Undocumented PC, Addison Wesley, 1994.

Winston, P.H., On to C++, Addison Wesley, 1994.

12

Data Acquisition with Operator Overloading

Inside this Chapter

ξ

ξ

ξ

ξ

ξ

ξ

ξ

ξ

Pass parameters by value and by reference.

Returning values by reference.

Operator overloading.

The Copy constructor and the assignment operator.

File input/output.

Friend functions.

Pass-through objects.

Data acquisition using the ADC.

12.1 Introduction

Some of the programs written in this book can be improved to require less memory when executing, and also operate faster by using pass by reference and return by reference mechanisms. They can also be changed to take advantage of simpler statements by using operator overloading and gain access to private member data through friend functions.

In this chapter, we will develop a data acquisition program to demonstrate how operator overloading can be used to write elegant programs that have the advantages outlined above. During data acquisition, signals are converted to data using a device such as an analog-to-digital converter. The data is then directly processed or written to a data file on a mass storage device such as a hard disk, or in some cases, sent to a standard output device such as a screen or printer.

12.2 Operator Overloading

When an operator is overloaded, the action carried out by the operator depends on the arguments the operator is associated with. For example, the results will be different if the division operator ( / ) is used in the following two contexts. One operation produces integer division and the other floating-point division:

5/2; // the result is 2 5.0/2.0; // the result is 2.5

Similarly, the double left arrow operator behaves in two different ways in the following two cases:

int y =

200;

output.

cout

<<

y; // 200 is sent to the standard

y <<

1;

// Shifts bits of y to left by

1 bit-position.

The action of an operator depends on the type of object it is used with. In the expression cout << y, cout is a class object of type ostream and y is an object of type int. The << operator takes appropriate action to print the value of y on the screen. However, both operands are of type integer in y << 1, and the action taken by the operator is to shift bits of y by 1 bit-position to the left.

The operators shown in Table 12-1 cannot be overloaded.

Table 12-1 Operators that cannot be overloaded.

. ?: :: .* sizeof

12 DATA ACQUISITION WITH OPERATOR OVERLOADING 365

We will demonstrate operator overloading by developing program segments that overload the double right arrow (>>) and the double left arrow (<<) operators to perform the following tasks:

1.Carry out an analog-to-digital conversion using an Adc object of type ADC, and store the result in the variable named value of type unsigned char. We want to be able to use a statement of the following form to accomplish this.

Adc >> value;

2.Carry out an analog-to-digital conversion and send the data directly to the standard output device (computer screen) using a statement of the following form. The object cout is of type ostream and Adc is again an object of type ADC.

cout << Adc;

Using a statement like this would simplify programming of a data acquisition system with an analog-to-digital converter where the results are to be viewed onscreen or stored in a file.

Operators can be overloaded in two different ways by writing a function using syntax that is specific to operator overloading:

1.As a member function of a class.

2.As a non-member function.

These two ways of overloading an operator will be discussed in the sections ahead. Operators can also operate as unary operators (such as ++ in the case of ++i) or as binary operators (such as + in the expression x+y). The unary operators shown in Table 12-2 operate on an object of type ObjectX. The binary operators operate on two objects; one of type ObjectX, and the other of type ObjectY. In this example the operator being overloaded is the @ symbol.

Table 12-2 Function headings for operator overloading.

Unary operator as a member function

ObjectX::operator@()

Unary operator as a non-member function

operator@(ObjectX x)

Binary operator as a member function

ObjectX::operator@(ObjectY y)

Binary operator as a non-member function

operator@(ObjectX x, ObjectY y)

The operators overloaded as shown above are used as follows. In the case of a unary operator, the operand must be to the right of the operator. For example, if x is an object of type ObjectX, the usage is:

@x;

366 12 DATA ACQUISITION WITH OPERATOR OVERLOADING

In the case of a binary operator, the first operand must be to the left of the operator and the second operand must be to the right of the operator. If x is an object of type ObjectX and y is an object of type ObjectY, then the usage is:

x @ y;

The syntax used with the operator is the same if the operator is overloaded as a member function or a non-member function. C++ concepts such as pass by value, pass by reference and copying objects with the copy constructor need to be understood before being able to understand how operators can be overloaded. These concepts are explained in the sections ahead.

12.2.1 Passing Parameters to a Function by Value

Our previous programs have often employed functions that used parameters. At the time of calling the function, these parameters are replaced by copies of the actual arguments (the real values used in the calling function). These copies of the arguments passed to the function are created as temporary values, used by the function, and destroyed when the function exits. As a result, the actual argument used when calling the function (in the calling environment) will not be affected by any changes the function makes to its copy.

The passing of parameters to functions can be better understood by considering the following example that attempts to add up n integers that start from the number 0:

#include <iostream.h>

// NOTE: the result from this function cannot be used! void FindSum(int sum, int n)

{

for(int j = 0; j < n; j++) sum = sum + j;

}

void main()

{

int Sum = 0; int n = 10;

FindSum(Sum,n); // FindSum() is called here

cout << "The sum of " << n << " integers is " << Sum << endl;

}

This program will print the following text on the screen:

The sum of 10 integers is 0