Файл: Interfacing with C plus plus-programing communication with microcontrolers (K. Bentley, 2006).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 14.06.2025

Просмотров: 3606

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

6 DIGITAL-TO-ANALOG CONVERSION 131

function named GetLastOutput() is also added to the derived class. It returns the value stored in LastOutput. Therefore, the only statement within the body of the GetLastOutput() function is:

return LastOutput;

Any function in the program that requires the value of the last data number output to the DAC (from the port at address BASE) must call the DAC class’s public function GetLastOutput(). This is the only way to access the value stored in the private data member LastOutput and ensures that functions outside the DAC class have no direct access to it. Once again, this demonstrates the controlled access to private data of a class by an object-oriented program.

Both constructors have been modified to initialise LastOutput to 0 by including the statement:

LastOutput = 0;

Therefore, at the time of instantiating a DAC object, the constructor will initialise the new data member LastOutput to 0.

We have modified the WritePort0() function in order to store the latest value output to the DAC by adding the line:

LastOutput = data;

However, the definition of the WritePort0() function given in Listing 6-7 will fail to compile. This is because the WritePort0() function of the derived class DAC is trying to use the inherited data member BaseAddress which is private to the base class. Although the private data of the base class is inherited, the derived classes cannot access this private data as shown in Figure 6-15.

One means to allow access to BaseAddress is by relaxing its access attributes. This can be done by declaring BaseAddress in the base class with protected access. Then BaseAddress can be accessed by all functions of all derived classes provided the classes are derived using a public or protected base class access specifier. Access specifiers are described in more detail ahead in section 6.5.1. The modified class definition of the base class is given in Listing 6-8.

NOTE

Declare variables as private unless you plan to derive other classes using the

current class as a base class. When you want to use the current class as a base class to derive new classes, carefully determine the variables of the current class you would want the derived class to have access to. Declare only these variables

as protected in the current class.

132 6 DIGITAL-TO-ANALOG CONVERSION

Derived Class

Derived Class

Inherited Base Class Members

Inherited Base Class’s Members

Parallel Port

Parallel Port

private

protected

BaseAddress

BaseAddress

InDataPort1

private

public

InDataPort1

ParallelPort ( )

public

.

ParallelPort ( )

.

.

.

.

Derived Class Members

Derived Class Members

DAC

DAC

private

private

LastOutput

LastOutput

public

public

DAC ( )

DAC ( )

DAC (baseaddress)

DAC (baseaddress)

WritePort0 ( )

WritePort0 ( )

GetLastOutput ( )

GetLastOutput ( )

Case (a)

Case (b)

Figure 6-15 Private and protected access specifiers.

Listing 6-8 Base class ParallelPort - BaseAddress as protected data member.

class ParallelPort

{

protected:

unsigned int BaseAddress;

private:

unsigned char InDataPort1;

public:

ParallelPort(); ParallelPort(int baseaddress);

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

};


6 DIGITAL-TO-ANALOG CONVERSION 133

class DAC : public ParallelPort

{

private:

unsigned char LastOutput;

public:

DAC();

DAC(int baseaddress);

void WritePort0(unsigned char data); unsigned char GetLastOutput();

};

A program that uses the class definition shown above is given in Listing 6-9.

6.5.1 Access specifiers

Consider the line:

class DAC : public ParallelPort

The keyword public in this line is an access specifier. Access specifiers change the access attributes as follows. The protected variables of the ParallelPort class can be accessed by the member functions of the DAC class, if and only if, the

DAC class is derived from ParallelPort using a public or protected base class access specifier. Access specifiers can also be private. Figure 6-16 shows how the access specifiers determine access attributes of inherited members.

Access Specifier public

When deriving a class using a public base class access specifier, all inherited public members of the base class will become public members of the derived class, all inherited protected members of the base class will become protected members of the derived class. All inherited private members will remain private to the base class, and so the derived class cannot access them.

Access Specifier protected

When deriving a class using a protected base class access specifier, all inherited public and protected members of the base class will become protected members of the derived class. All inherited private members will remain private to the base class, and so the derived class cannot access them.

Access Specifier private

When deriving a class using a private base class access specifier, all inherited public and protected members of the base class will become private members of the derived class. All inherited private members will remain private to the base class, and so the derived class cannot access them.


134 6 DIGITAL-TO-ANALOG CONVERSION

BASE CLASS

Private

Protected

Public

Private

Protected

Public

Private

Protected

Public

DERIVED CLASS

Privateaccess

specifier

No access

Private

Private

Protected

specifieraccess

No access

Protected

accessPublic

Protected

specifier

No access

Protected

Public

Figure 6-16 Access specifiers determine access attributes of derived class members.

6.5.2 Polymorph Functions

The attempt to redefine the member function WritePort0() in Listing 6-9 is quite legitimate. However, WritePort0() is a function the DAC class inherited from the base class ParallelPort. To allow derived classes to redefine

inherited functions, the inherited functions must derived class definition as done in Listing 6-9 (and there are two WritePort0() functions: one

be explicitly included in the Listing 6-7). In this example, of them belonging to the

ParallelPort class; and the other belonging to the DAC class. The existence of functions of the same name throughout a class hierarchy is termed polymorphism. These functions not only have the same name, but also the same number of parameters, same types of parameters, and the same sequence of parameters.

The declaration of the DAC class is given in Listing 6-8. Despite the DAC class inheriting the function WritePort0() from the base class ParallelPort, it is explicitly coded again in the DAC class. This allows us to redefine the body of the WritePort0() function to suit the needs of the DAC class.


6 DIGITAL-TO-ANALOG CONVERSION 135

NOTE

There is a clear difference between the term polymorphism and overloading.

Overloaded functions also have the same function name. They differ in the number or type of parameters passed to the functions. In addition, overloaded functions do not need to be member functions.

The complete program, including the class hierarchy that can be compiled without errors is given in Listing 6-9.

Listing 6-9 Digital-to-analog conversion with the expanded DAC object.

/***************************************************** In this program, the compilation error has been eliminated by changing the access attribute of BaseAddress in the base class (ParallelPort) from private to protected. Now the functions of the publicly derived class can access the inherited BaseAddress.

This accessibility is only available to the derived classes of the base class and to the base class

itself. The function WritePort0(), which is re-declared in the derived class, can now be modified without any compilation errors.

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

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

class ParallelPort

{

protected:

unsigned int BaseAddress;

private:

unsigned char InDataPort1;

public:

ParallelPort(); ParallelPort(int baseaddress);

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

136 6 DIGITAL-TO-ANALOG CONVERSION

unsigned char ReadPort1();

};

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)

{

outportb(BaseAddress+2,data ^ 0x0B);

}

unsigned char ParallelPort::ReadPort1()

{

InDataPort1 = inportb(BaseAddress+1);

//Invert most significant bit to compensate

//for internal inversion by printer port hardware. InDataPort1 ^= 0x80;

//Filter to clear unused data bits D0, D1 and D2 to zero.

InDataPort1 &= 0xF8; return InDataPort1;

}

class DAC : public ParallelPort

{

private:

unsigned char LastOutput;

public:

DAC();

DAC(int baseaddress);

void WritePort0(unsigned char data);

6 DIGITAL-TO-ANALOG CONVERSION 137

unsigned char GetLastOutput();

};

DAC::DAC()

{

LastOutput = 0;

}

DAC::DAC(int baseaddress) : ParallelPort(baseaddress)

{

LastOutput = 0;

}

void DAC::WritePort0(unsigned char data)

{

outportb(BaseAddress,data); LastOutput = data;

}

unsigned char DAC::GetLastOutput()

{

return LastOutput;

}

void main()

{

DAC D_to_A;

D_to_A.WritePort0(0);

",

D_to_A.LastOutput); // Does

// printf("\nDAC byte:%3d

printf("\nDAC byte:%3d

",

// not work, why?

D_to_A.GetLastOutput());

cout << "

Measure voltage

and press a key" << endl;

getch();

D_to_A.WritePort0(32);

",

D_to_A.GetLastOutput());

printf("\nDAC byte:%3d

cout << "

Measure voltage

& then press a key" << endl;

getch();

D_to_A.WritePort0(64);

",

D_to_A.GetLastOutput());

printf("\nDAC byte:%3d

cout << "

Measure voltage

and press a key" << endl;

getch();


138 6 DIGITAL-TO-ANALOG CONVERSION

D_to_A.WritePort0(128);

printf("\nDAC byte:%3d ", D_to_A.GetLastOutput()); cout << " Measure voltage and press a key" << endl; getch();

D_to_A.WritePort0(255);

printf("\nDAC byte:%3d ", D_to_A.GetLastOutput()); cout << " Measure voltage and press a key" << endl; getch();

}

In the above program, the constructor DAC() is called at the time of instantiating the DAC class object D_to_A. Referring to the function definition of the default DAC() constructor; it makes a call to the default constructor of the class ParallelPort before entering the body of the DAC() constructor. The default constructor of the ParallelPort class will initialise BaseAddress to 0x378 (and set InDataPort1 to 0). Then execution of the body of the constructor DAC() begins. It will initialise the value of the DAC class’s private data member

LastOutput to 0.

Note that the member function GetLastOutput() is called when the value of LastOutput needs to be printed onscreen. This needs to be done because the printf() function does not have direct access to the private data member

LastOutput.

The definition of the WritePort0() function can be modified slightly to revert the access attribute of BaseAddress back to private for the following reasons. Consider the function WritePort0() from Listing 6-9 reproduced in Listing 6-10.

Listing 6-10 WritePort0() function of the DAC class.

void DAC::WritePort0(unsigned char data)

{

outportb(BaseAddress,data); LastOutput = data;

}

The only time BaseAddress is accessed is when the data is sent out the port. The polymorphic function WritePort0() of the ParallelPort class can do this. It has no problem in accessing BaseAddress since the function and the data are in the same class. It is possible to call the polymorphic function WritePort0() of the ParallelPort class from inside the polymorphic function WritePort0() of the DAC class by using the scope resolution operator;

6 DIGITAL-TO-ANALOG CONVERSION 139

the double colon (::). This process is shown in Figure 6-17 and is implemented by modifying the fragment of code from Listing 6-10 to become that given in Listing 6-11.

Derived Class

Derived Class

Inherited Base Class Members

Inherited Base Class’s Members

Parallel Port

Parallel Port

private

private

BaseAddress

BaseAddress

InDataPort1

InDataPort1

public

public

ParallelPort ( )

ParallelPort ( )

WritePort0 (data)

WritePort0 ( )

.

.

.

.

.

.

Derived Class Members

Derived Class Members

DAC

DAC

private

private

LastOutput

LastOutput

public

public

DAC ( )

DAC ( )

DAC (baseaddress)

DAC (baseaddress)

WritePort0 ( )

WritePort0 ( )

GetLastOutput ()

GetLastOutput ( )

Case (a)

Case (b)

Figure 6-17 Use of inherited polymorphic functions (BaseAddress private again).

Listing 6-11 Calling a polymorphic function of a base class.

void DAC::WritePort0(unsigned char data)

{

ParallelPort::WritePort0(data);

LastOutput = data;

}

In the ParallelPort class definition, the access attribute of the data member BaseAddress can now be set back to private as shown in Figure 6-17. The new and preferred program is given in Listing 6-12.